89 lines
7.2 KiB
Markdown
89 lines
7.2 KiB
Markdown
# 棒 0b|手机经网关操作 WorkBuddy 会话 —— 取证与打通
|
||||
|
|
|
|||
|
|
> 2026-09-29 · 工作区 `E:/ProgramData/AIProject/ai1net-dsh-server` · 对应工单:`ai1net-dsh-anywhere/docs/交接单/对接单_WorkBuddy手机客户端-Android_20260928.md`
|
|||
|
|
> 本件是**纯取证 + 打通 + 更正**,⛔ 未改动平台与 DSH 任何代码。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、一句话结论
|
|||
|
|
|
|||
|
|
**"手机 App 操作电脑上 WorkBuddy 会话"这条链路,除"真从手机发出一条消息"外,全部环节已在本机对真网关打通并取证;且原先三处前提被推翻 —— 现在不需要任何人工作任何配置。**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、🔴 三条推翻旧结论的更正(都有源码/实测双依据)
|
|||
|
|
|
|||
|
|
| # | 旧结论(09-28) | 09-29 实测与源码 | 影响 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| 1 | 拿令牌必须人工做一次:敲 `/gateway token`,或从「设置→远程控制」复制带 `password=` 的地址 | 网关口令的解析顺序(gateway 源码)是 **`process.env.CODEBBUDDY_GATEWAY_PASSWORD` → `settings.gateway.password` → 都没有才随机生成并写回**。**桥只要跑在 WorkBuddy 进程树内就能自动取到** | 🔴 **人工步骤从"必须"降为"零"** |
|
|||
|
|
| 2 | 带令牌用 `Authorization: Bearer <令牌>`(或 `?password=`) | 实测四种写法:`?password=` **在受保护路径上永远失效**(`requireAuth` 构造请求对象时写死 `query:{}`,上游行为);**`x-access-token: <口令>` 实测 200** | 桥原来用的写法虽可用但非首选;`token.txt` 路线作废 |
|
|||
|
|
| 3 | 上一轮判定"env 令牌 → 401,D-1 的『实测 200』需更正" | 🔴 **那个判定本身是错的** —— 上一轮用的是 `?password=`/Bearer 写法。用 `x-access-token` 打同一串值 ⇒ **200**。env 里的值**就是口令本身**(43 字符 base64url),既不陈旧也不是哈希 | 令牌来源问题**彻底消失** |
|
|||
|
|
|
|||
|
|
> 📌 教训沉淀:**"401" 不等于"凭据不对",也可能只是"带法不对"**。遇到 401 应先读鉴权代码确认它到底从哪取、怎么比,再动手。本轮曾据此误判并差点把 env 路线整条废弃。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、已验证清单(对真网关、真数据)
|
|||
|
|
|
|||
|
|
| # | 验证项 | 结果 | 方式 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| 1 | 网关口令自动获取 | ✅ 来源 `env:CODEBUDDY_GATEWAY_PASSWORD` | 起真桥读自检端点 |
|
|||
|
|
| 2 | 鉴权头 `x-access-token` | ✅ 200 | 四种写法对照试打 |
|
|||
|
|
| 3 | 经桥 `GET /api/v1/sessions` | ✅ 200,跨项目 24 条 | 起真桥 → 客户端请求 |
|
|||
|
|
| 4 | 经桥 `GET /api/v1/sessions/live` | ✅ `{sessionId, writerOccupied}` | 同上 |
|
|||
|
|
| 5 | 经桥 `GET /api/v1/sessions/{id}/history` | ✅ `{data:{name, requests, sessionId}}` | 同上 |
|
|||
|
|
| 6 | `GET /api/v1/sessions/{id}/replay` | ✅ 953 事件 / 2.6 MB | 直连 |
|
|||
|
|
| 7 | 写端点路由与参数名 | ✅ 缺 `text` ⇒ `400 BAD_REQUEST "text is required"` | 直连(零副作用) |
|
|||
|
|
| 8 | 写端点活会话闸门 | ✅ 非活会话 ⇒ **409 `SESSION_FOLLOW_NOT_LIVE`** | 直连(零副作用) |
|
|||
|
|
| 9 | **手机页面解析逻辑 × 真实响应** | ✅ 全绿(会话列表 / 活会话 / 历史三处) | `_verify-page.mjs` 断言 |
|
|||
|
|
| 10 | 端口确定性判据(父链) | ✅ 选中 `57452`,其 `/live` 与"最近会话"都指向本会话 | `bridge.py --discover` |
|
|||
|
|
| 11 | ACP 免凭据(回环) | ✅ `connect` 200 + `initialize` 200 | 直连 |
|
|||
|
|
| 12 | ACP 断开清理 | ✅ `DELETE /api/v1/acp` 200 | 直连 |
|
|||
|
|
| 13 | 真从手机发一条消息 | ⏳ **未做**(见 §六) | —— |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、改动的文件(全部在 `tmp/wb-phone/`,⛔ 不入库)
|
|||
|
|
|
|||
|
|
| 文件 | 改动 | 性质 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `bridge.py` | ① 新增口令来源 `CODEBUDDY_GATEWAY_PASSWORD`;② 鉴权头改 `x-access-token`;③ 新增**父链判据**(`Toolhelp32` 快照上溯)+同档位"活会话择一";④ 自检端点回传口令来源与鉴权头 | 修真实错处 |
|
|||
|
|
| `www/index.html` | 修正 **5 处字段路径**:列表取 `data.sessions`、活会话取 `data`、历史取 `data.requests`、`updatedAt` 按 epoch 毫秒格式化、副标题回退 `cwd→消息数`;鉴权头改 `x-access-token`;无活会话时给出明确提示;401 文案改为"桥须由 WorkBuddy 拉起" | 修真实错处 |
|
|||
|
|
| `README.md` | 按 §二 三条更正重写;补 §六 实测结论、§七 ACP、§九 自检脚本 | 文档更正 |
|
|||
|
|
| `_verify-bridge.sh` / `_verify-page.sh` / `_verify-page.mjs` / `verify-write-gate.py` / `probe-token-forms.py` / `probe-acp-init.py` / `verify-acp-write.py` / `asar-peek.mjs` / `reflow.mjs` / `_src/*` | 新建(可复跑) | 新增工具 |
|
|||
|
|
|
|||
|
|
**回滚点**:`tmp/wb-phone/` 全部为新建或已备份;`www/index.html` 上一版可由 §六 的字段表复原;⛔ 未动 `settings.json`、⛔ 未动任何平台/DSH 代码。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、对棒 1(垫片改造)与 D-1b 的输入
|
|||
|
|
|
|||
|
|
1. **令牌一环可以从设计里删掉**:垫片/桥**不再需要"令牌投递器"**,直接读自己进程的 `CODEBUDDY_GATEWAY_PASSWORD` 并加 `x-access-token` 即可 —— 前提是**它必须跑在 WorkBuddy 进程树内**(这正是"WorkBuddy 侧极薄组件"的正当理由,而不是为了传令牌)。
|
|||
|
|
2. **端口发现别只判"能连"**:本机实测并存 3 个 `WorkBuddy.exe`、**共用同一口令** ⇒ **鉴权判据不能区分实例**。必须用**父链**;同档位多候选时再用"活会话=最近活跃会话"择一。
|
|||
|
|
3. **D-1b(REST vs ACP)事实已齐**:
|
|||
|
|
- **A 案(REST `reply`)**:零新增依赖;**不占 ACP writer**(不干扰桌面);但**只能操作"桌面当前正开着的那一个会话"**(其余 409)。
|
|||
|
|
- **B 案(ACP)**:**回环免凭据**;`loadSession:true` 可 attach 已有会话;⚠️ 但**会占用 writer**,且实测 `session/new` **返回的就是"当前会话"**(不是新建)。
|
|||
|
|
- **两者不冲突的用法**:默认走 `reply`(桌面开着时);`reply` 得 409 时才用 ACP `session/load` 接管(此时桌面没开着它,不争用)。
|
|||
|
|
4. **内嵌文档有错**:ACP `session/new` 的参数名是 **`cwd`**(文档写 `workingDirectory`);`Accept` 必须带 `application/json, text/event-stream`(否则 406)。⛔ 别照文档抄。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、⏳ 唯一未做的一步,以及为什么不做
|
|||
|
|
|
|||
|
|
**真从手机发一条消息到桌面会话。**
|
|||
|
|
|
|||
|
|
不做,是因为它**在语义上必然落到"你桌面当前正开着的那个会话"**,也就是**你自己的使用行为** —— 我替你做,等于往你正在聊的会话里插一条消息、并触发一整轮 Agent 执行。考虑到本项目此前已发生过"会话卡住"类事故,**这类副作用不擅自做**。
|
|||
|
|
|
|||
|
|
现已把除这一行之外的全部代码路径验到:路由存在、参数名正确、活会话闸门(409)、投递实现(源码 `runDefault` 那一行)。
|
|||
|
|
|
|||
|
|
**你想立刻验证的话,二选一**:
|
|||
|
|
- 手机点进带绿标「活会话」的那条 → 输框发一句;或
|
|||
|
|
- 在电脑上对 `liveId` 发 `POST /api/v1/sessions/{liveId}/reply {"text":"…"}`(对非活会话必 409)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七、下一步(按优先级)
|
|||
|
|
|
|||
|
|
1. 你在手机上试发一条 → 需求收口。
|
|||
|
|
2. 棒 1 按 §五 改垫片(令牌一环删除、加父链判据、上游改指 WorkBuddy gateway 动态口)。
|
|||
|
|
3. G-A(`~/.dsh/profiles/` 缺 `desktop` ⇒ 垫片无处可挂)仍需先恢复。
|