---
name: chatgpt-bridge
description: "用用户本机已登录的 chatgpt.com 会话做两件事：(1) 新建对话，把你不确定/不清楚的技术问题发过去，等回答生成结束，把完整答案取回来；(2) 按关键字找回历史对话，在里面继续追问，拿到确切答案。复用 Chrome 登录凭证（不碰账号密码、不需要 API key），读取走 ChatGPT 官方 backend-api 因此 markdown 完整、不受页面虚拟化影响。触发场景 — 问一下 ChatGPT / 帮我问 GPT / 用我的 ChatGPT 问 / 复查这个结论 / 找回之前和 GPT 聊过的 X / 继续问那个对话 / ask ChatGPT in my browser / 我不确定这个 API 怎么用 去问一下。"
agent_created: true
---

# chatgpt-bridge

## Purpose

把 ChatGPT 当成一个**可脚本调用的外部专家**：发送你不确定的问题，取回准确答案；或按关键字
定位历史对话并在其中继续追问。三条设计约束：

- **发送**走页面 UI（官方发送接口需要页面计算的 proof-of-work / sentinel token，绕过会被拒）。
- **读取**走 `backend-api`（markdown 完整、含代码块，不受对话 DOM 虚拟化影响）。
- **凭证**复用用户本机 Chrome 的登录态副本，全程不接触密码，也不需要 OpenAI API key。

## When to Use

- 遇到不确定的技术细节（某个 API 的确切签名、某个标准里的编号、某段代码是否成立），需要
  **外部权威答案**而不是自己猜。
- 用户说"帮我问一下 GPT""复查这个结论""找回之前聊过的那个话题再问一句"。
- 需要在已登录站点上做操作，而 AppleScript 路径不可用。
- 触发词：问一下 ChatGPT、帮我问 GPT、用我的 ChatGPT 问、复查这个结论、找回之前和 GPT 聊过的 X、
  继续问那个对话、ask ChatGPT in my browser、"我不确定这个 API 怎么用 去问一下"。

## When NOT to Use

- 答案能靠本地代码 / 文档 / 搜索确定 -> 没必要消耗用户配额。
- 只想抓公开网页 -> 用 WebFetch。
- 本机 Chrome 中未登录 chatgpt.com -> 无法复用登录态，停下来让用户手动登录后重试。

## Workflow

**快速开始**：

```bash
SK="$HOME/.workbuddy/skills/chatgpt-bridge/scripts"
NODE=<node 绝对路径>          # 需要 Node 18+
# 关键：清掉沙箱注入的代理环境变量，否则连不上本地 CDP
AB() { env -u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY \
        NO_PROXY="127.0.0.1,localhost" no_proxy="127.0.0.1,localhost" "$@"; }

python3 "$SK/bridge.py" up --proxy http://127.0.0.1:7890   # 1. 起桥（无代理需求则 --proxy none）
AB $NODE "$SK/chatgpt.mjs" status                            # 2. 确认已登录、输入框就绪
...                                                          # 3. 干活（见下）
python3 "$SK/bridge.py" down                                 # 4. 必须收尾（会删掉含 cookie 的副本）
```

`bridge.py up` 返回 JSON：`{"status":"up","port":9222,"browser":"Chrome/152..."}`。

**工作流 1：新建对话提问，取回答案**

```bash
AB $NODE "$SK/chatgpt.mjs" ask "<你的问题>" --new --out /tmp/ans.json --timeout 240000
```

`--new` 先跳到 `chatgpt.com/` 开新对话再提问。回答较长时**务必用 `--out`**：stdout 只打印摘要，
全文写进 JSON 文件，再用读文件工具取，避免命令行输出被截断。

**工作流 2：按关键字找回历史对话 / 继续提问**

```bash
AB $NODE "$SK/chatgpt.mjs" search "网格生成算法 语言选择" --limit 5 --out /tmp/s.json
AB $NODE "$SK/chatgpt.mjs" read <conversationId> --out /tmp/c.json    # 另生成 /tmp/c.md
AB $NODE "$SK/chatgpt.mjs" ask "<追问>" --in <conversationId> --out /tmp/followup.json
```

## Decision Rules

| 命令 | 作用 | 关键参数 |
|---|---|---|
| `status` | 当前页 URL / 标题 / 会话 id / 是否登录 / 输入框是否就绪 | - |
| `new` | 跳到新对话 | - |
| `open <id\|url>` | 打开指定对话 | - |
| `ask "<文本>"` | 发送并等待回复，取回答案 | `--new` `--in <id>` `--out` `--timeout` |
| `read [<id>]` | 导出完整会话（API，markdown 保真） | `--out`（另生成同名 `.md`） |
| `list` | 最近会话列表 | `--limit` `--out` |
| `search "<词>"` | 关键字/语义检索历史会话 | `--limit` `--out` |
| `api <path>` | 直接打 authed GET，调试用 | - |

- **拿到答案的判定标准**：`ok: true` 且 `matchedLastUserMessage: true`。`matched` 是脚本做的强校验
  —— 把 API 返回的「最后一条 user 消息」与本次发送的问题归一化比对，不等则说明读到了别的轮次。
  `ok: false` 时检查 `domFallback` 字段。
- **超时**：`--timeout` 是等待上限（默认 300000ms）。超时**不代表失败**，脚本仍会读一次已持久化
  的内容并打印 `WARN`；此时仍以 `matchedLastUserMessage` 为准。
- **检索是语义的**：搜"读取节点坐标"可能返回标题为「DWG图纸比较方法」的对话。**不要只看标题就
  下结论**，用 `snippet` 判断是否真是目标话题；不匹配就换关键字或改用 `list` 人工挑。
- **读优先走 API**：仅在 API 读不到时才用 DOM 兜底。响应形状、选择器与完成判定的实现细节见
  `references/chatgpt-api.md`。
- **失败分流**：超时 / 连不上 / 崩溃循环 / 403 验证码 -> 读 `references/known-issues.md`；
  环境相关项（CDP、profile 副本、代理、AppleScript）-> 读 `references/transport.md`。

## Constraints

- **必须收尾**：结束前运行 `python3 "$SK/bridge.py" down`。临时 profile 副本里是**有效的 session
  cookie**，不收尾等于把登录凭证留在盘上；`down` 会杀掉实例并删除副本。若用户明确要保留调试现场，
  用 `status` 查看路径后自行处理，但绝不长期保留。
- **发送消息是外部动作**：内容必须是用户确实想问的，不要替用户自由发挥；第一次发送后先汇报，
  再继续。
- **隐私**：检索/读取用户的历史对话属于隐私数据，只取所需，不要把无关历史内容转述出去。
- **只对用户自己的账号、只做用户要求的动作**；遇到验证码 / 二次验证 / 未登录，停下来交回用户，
  不要尝试绕过。
- 代理、端口、Chrome 路径都是环境相关项，不要硬编码成常量后长期依赖。

## Stop Conditions

- `status` 的 `loggedIn: false` -> 停止，告知用户在 Chrome 里手动登录后重试；不要尝试代替登录。
- 出现验证码 / Turnstile / 二次验证 -> 停止并交回用户，不尝试绕过。
- `--proxy` 指定的代理 curl 实测不通 -> 停止，先让用户修代理，不要反复重试。

## Verification

交付前逐项确认：

- [ ] `bridge.py up` 返回 `status: up`；`status` 显示 `loggedIn: true`。
- [ ] 取回的答案满足 `ok: true` 且 `matchedLastUserMessage: true`（或已说明超时后读到的是 WARN 内容）。
- [ ] 长答案经 `--out` 落盘后读取，未依赖被截断的 stdout。
- [ ] 已运行 `bridge.py down`；未把含 cookie 的 profile 副本留在磁盘上。

## Output

- **Outcome**：ChatGPT 的完整 markdown 答案（或历史会话全文），落到 `--out` 指定的 JSON；
  `read` 额外生成同名 `.md`。
- **Done when**：`## Verification` 全部勾选，且桥已 `down`。
- **Evidence**：`ask` 返回的 `conversationId` / `url` / `answerChars` 与 `matchedLastUserMessage: true`；
  `read` 生成的 `.md` 文件路径。

## References

按需加载，不要预先全读：

- `references/chatgpt-api.md` — 需要接口路径、页面选择器、响应形状或完成判定的实现细节时读。
- `references/transport.md` — 需要排障 CDP 连接、profile 副本、Chrome 启动参数或代理时读。
- `references/known-issues.md` — 出现超时 / 连不上 / 崩溃循环 / 403 验证码等现象时读。

## Scripts

- `scripts/bridge.py` — 起桥/收桥：把最小 Chrome profile 副本交给一个可抛弃的 Chrome 实例并开
  CDP 端口。用法 `bridge.py up [--port 9222] [--proxy URL|none] [--window-size WxH]` /
  `status` / `down [--all]`；`up` 返回 JSON，`down` 删除副本。
- `scripts/chatgpt.mjs` — 全部 ChatGPT 操作（`status`/`new`/`open`/`ask`/`read`/`list`/`search`/`api`），
  见 `## Decision Rules` 的命令表。
- `scripts/cdp.mjs` — 底层 CDP 客户端，被 `chatgpt.mjs` 导入；也可 CLI 直用做临时调试：
  `node cdp.mjs targets | nav <url> [timeoutMs] | diag <url> [timeoutMs] | eval <js> | eval-file <path>`。
