@luchenglong/cli-bridge 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,79 @@ GitHub Release(`scripts/verify.mjs` 在 CI 与 prepack 双重锁一致)。
9
9
  > - CodeBuddy 线:[docs/history/codebuddy/CHANGELOG.md](docs/history/codebuddy/CHANGELOG.md)(终版 1.6.2)
10
10
  > - MiMo 线:[docs/history/mimo/CHANGELOG.md](docs/history/mimo/CHANGELOG.md)(终版 1.2.9)
11
11
 
12
+ ## [0.2.6] - 2026-10-18
13
+
14
+ ### Fixed
15
+ - **`codebuddy_models` / 设置面板的模型清单「不是实时、也是错的」**(用户报告)。根因:清单
16
+ 读的是随包冻结的 `product.ioa.json` 静态表,实测已落后于本机 CLI ——
17
+ **静态 16 项 vs CLI 19 项**,其中 `minimax-m3` 实为 `minimax-m3-pay`、`kimi-k3-1` 实为
18
+ `kimi-k3-2`(两个 id 已失效),另缺 `step-5-preview` / `space-bunny` / `glm-5.3-flashx` / `kimi-k3-2`。
19
+ 改为**以 CLI 为实时权威源**(对齐 `workbuddy-auth` 的「`agents[cli].models` 权威白名单」思路):
20
+ - **A. `account-live`**(首选):起 CLI 用无效模型触发服务端 400,「Currently supported models
21
+ for your account」即该账号的授权清单 —— 与 workbuddy-auth 从 `/v3/config` 取白名单同义。
22
+ - **B. `cli-help`**(兜底,如实标注):解析 `--help` 的 `Currently supported: (...)`,
23
+ 零网络、随 CLI 版本自动更新。回落时 `source` 明确写 `cli-help` 并附 note,
24
+ **绝不把静态表冒充成账号可用清单**。
25
+ - 静态表降级为**仅倍率元数据**,不再参与「是否可用」判定。
26
+ - **面板 `defaultModel` 无视 `preferredBackend`**:旧实现硬编码 `DEFAULT_BACKEND`(codebuddy 面),
27
+ 无论用户选哪个后端都只列 codebuddy 面的模型。现跟随设置里的默认后端,且账号探测走与
28
+ `buildArgv` 完全同一套端点/凭据注入(`faceEnv`)—— 否则会出现「标着 codebuddy-en、
29
+ 实际问的是 codebuddy 面」的错配。
30
+ - **`--help` 名册折行导致清单抖动**:yargs 在长名册上任意列折行,实测把 `minimax-m3-pay`
31
+ 从中间断成 `minimax-` + `m3-pay`,同一命令两次运行给出 18/19 两种结果。解析改为
32
+ 「去掉括号内所有空白再按逗号切」,三次连跑稳定 19 项。
33
+ - **探测失败文案误导**:该面未登录时曾报「codebuddy CLI not found」,实际是「CLI 跑了但没吐
34
+ 账号名册」。现记录每个候选的真实结局(spawn 失败 / exit code + 输出首段),错误可指导处置。
35
+
36
+ ### Changed
37
+ - **`scripts/build.mjs` 支持内联多个片文件**:`providers/<id>-*.mjs` 全部按字典序内联进
38
+ dynamic 沙箱的同一作用域(此前只认写死的 `<id>-catalog.mjs`)。每撞一次 600 行红线就改一次
39
+ 构建器的做法不可持续。
40
+ - **`codebuddy` provider 再拆一层**:`codebuddy.mjs` 864 → 593 行,拆出 `codebuddy-catalog.mjs`
41
+ (564)与 `codebuddy-models-tool.mjs`(101),三者均在红线内。
42
+ - **`.mjs` 入口支持**:`CODEBUDDY_BIN` 指向 `.js/.mjs/.cjs` 时经 node 包装(也让测试夹具能
43
+ 完全脱离真实 CLI、可在任意机器复现)。
44
+ - 修的 `DEP0190` 告警:Windows `.cmd` 经 shell 时把命令拼成单字符串,不再传 args 数组。
45
+
46
+ ## [0.2.5] - 2026-10-18
47
+
48
+ ### Added
49
+ - **`codebuddy_models` 工具**:列出某个产品面可用的模型,三层合并(内置静态表 ∪ 磁盘描述
50
+ 文件 ∪ 上次运行收割的账号清单),已授权 id 优先,每项带积分倍率(`x0.00` = 免费)。支持
51
+ `backend` / `freeOnly` / `authorizedOnly` 过滤。设计参考 `workbuddy-auth` 的模型目录机制
52
+ (source 标注、解析失败大声报错),但**刻意不照抄它的静态降级**:拿不到目录时返回
53
+ `ok:false` + 可操作错误,绝不静默返回空列表或伪造模型。
54
+
55
+ - **预设只注册启用面**(行为变更,用户定案):`declareBridgePresets` 不再「恒常注册」全部
56
+ preset,而是只注册 `_scheduling.enabled` 里的 provider。停用的 CLI 不再在预设列表占位
57
+ (选中它等于立刻撞上一个零注册的桥)。代价:停用后旧会话头若指向该 preset 会找不到;
58
+ 重新启用即恢复(注册表在宿主启动时重建)。调度不可得时仍全量注册,绝不因读不到配置清空预设。
59
+
60
+ ### Changed
61
+ - **mimo 去 desktop 化**(用户定案):MiMo 免费 API 服务已被官方终止(CLI 报
62
+ `MiMo free API service has ended`)+ 本机 0 凭据,故取消 `mimo-desktop` 后端,只走
63
+ `mimo run`(MiMoCode CLI)。单后端化:`backends()` / `normalizeBackend()` /
64
+ `resolveBackend()` 恒 null,`defaultBackend: null`。**登录沿用 `mimo auth login`,用户自理。**
65
+ - **mimo 模型由用户在设置里指定**:新增 `defaultModel` 设置键(默认空串 = 不传 `--model`,
66
+ 交给 CLI 自身默认)。设置面板给 `type:'select'` + `allowCustom:true` 下拉,选项实时来自
67
+ `mimo models`。`mimo_settings` 入参由 `backend`/`desktopModel` 改为 `model`。
68
+ - **`providers/codebuddy.mjs` 拆分**(规则 §26,600 行红线):主文件 864 → 598 行,模型目录/
69
+ 积分/凭据解析等纯逻辑移入新的 `providers/codebuddy-catalog.mjs`(402 行)。`scripts/build.mjs`
70
+ 新增 `/*__EXTRA__*/` 占位符支持,允许 dynamic 模板内联第二个文件 —— 沙箱里仍是单脚本,
71
+ 行为零变化。`stripModuleSyntax` 同时支持**多行 import** 与**缩进 export**(搬出的函数保留
72
+ 原缩进,只匹配行首会残留 `export` 导致生成物语法崩)。
73
+ - **三形态 `defaultModel` 一致性修复**:此前 `currentSettings()` 只认 `host.getSettings`
74
+ (需 attach),而 MCP 形态不 attach(自建 engine)→ 设置面板的默认模型在 MCP 面对 `run`
75
+ **完全失效**,只有显式传 `model` 才落地。现统一把 `deps.readSettings()` 纳入合并链
76
+ (mimo 与 opencode 同步修复),三形态行为一致。
77
+
78
+ ### Fixed
79
+ - **mimo/opencode 在 MCP 形态读不到设置分区**:新增 `readSettings` 注入(真 Node 进程,
80
+ 每次现读设置文件)。
81
+ - **build 注释陷阱**:源码注释里写出占位标记字面量会被全局替换劈开,注释后半段变裸代码 →
82
+ 生成物语法崩。`providers/mimo.mjs`、`providers/codebuddy.mjs`、`codebuddy-catalog.mjs`
83
+ 均已加 ⚠️ 提醒。
84
+
12
85
  ## [0.2.4] - 2026-10-16
13
86
 
14
87
  ### Fixed
package/docs/HANDOVER.md CHANGED
@@ -2,30 +2,35 @@
2
2
 
3
3
  > 面向接手这个仓库/这台机器的下一个人(或下一个 Agent)。
4
4
  > 目标:**不读完所有历史也能安全改动、发布、排障**。历史事故与红线集中在 §5 与 §10。
5
- > 最近一次更新:2026-10-15(0.2.2 首发 npm + 旧包 deprecated + 旧仓归档之后)。
5
+ > 最近一次更新:2026-10-16(0.2.4:**第四个 provider OpenCode** 接入 + 状态灯淡出修复 +
6
+ > 版本单一来源;运行时改为 **npm registry 标准安装**,不再依赖本地开发目录)。
6
7
 
7
8
  ---
8
9
 
9
10
  ## 1. 这是什么
10
11
 
11
- `@luchenglong/cli-bridge` 把**本机三个编码 CLI**(agy / CodeBuddy / MiMo)以同一套 provider
12
- 描述符接入 DSH,并有四种落地形态(preset / 动态宿主 / 家级宿主插件+指示灯 / MCP stdio server)。
12
+ `@luchenglong/cli-bridge` 把**本机四个编码 CLI**(agy / CodeBuddy / MiMo / **OpenCode**)以同一套
13
+ provider 描述符接入 DSH,并有四种落地形态(preset / 动态宿主 / 家级宿主插件+指示灯 / MCP stdio server)。
13
14
  它由三个旧包(`agy-first-bridge` / `codebuddy-first-bridge` / `mimo-first-bridge`)合并而来,
14
- 旧包的工具名、预设 id、策略文案**逐字保留**(兼容承诺,见 §5)。
15
+ 旧包的工具名、预设 id、策略文案**逐字保留**(兼容承诺,见 §5)。OpenCode 是统一包首个**新增**
16
+ provider(无旧包可迁,无兼容包袱),见 §13。
15
17
 
16
18
  ## 2. 当前状态快照
17
19
 
18
20
  | 项 | 值 |
19
21
  | --- | --- |
20
- | 版本 | **0.2.2**(三处版本锁:`package.json` / `mcp/cli-bridge-mcp.mjs` 的 `SERVER_VERSION` / `CHANGELOG.md` 顶部;`verify.mjs` 机器比对) |
21
- | npm | `@luchenglong/cli-bridge@0.2.2`(`latest`;2026-10-14 首次发布,shasum `362d506e0b85dd7014bb8b3fc1100bf3a6d0d1d5`,58 files / 320.1 kB) |
22
- | GitHub | `new-256/cli-bridge`,`main` @ `520194c`;tag `v0.1.0` / `v0.2.1` / `v0.2.2` |
22
+ | 版本 | **0.2.4**(三处版本锁:`package.json` / `mcp/cli-bridge-mcp.mjs` 的 `SERVER_VERSION` / `CHANGELOG.md` 顶部;`verify.mjs` 机器比对) |
23
+ | npm | `@luchenglong/cli-bridge@0.2.4`(`latest`;shasum `08ca3325376e96abc935435784106fd1ee158ba4`,68 files / 376.0 kB) |
24
+ | GitHub | `new-256/cli-bridge`,`main` @ `acf6f09`;tag `v0.2.1` / `v0.2.2` / `v0.2.3` / `v0.2.4` |
23
25
  | 旧包 | 三个都已在 npm 标 **deprecated**(提示迁移到统一包):`agy-first-bridge@1.7.1`、`codebuddy-first-bridge@1.6.2`、`mimo-first-bridge@1.2.9` |
24
26
  | 旧仓 | `new-256/agy-first-bridge`、`new-256/mimo-first-bridge`:README 顶部已加迁移横幅且 **archived(只读)**;`codebuddy-first-bridge` 在 GitHub 上不存在 |
25
- | 测试 | **271 pass / 0 fail**(28 个 `*.test.mjs`),四闸门 `npm run check` 全绿 |
26
- | 本机运行时 | DSH Desktop,宿主 **2026-10-14 22:35 重启后加载 0.2.2**;`dsh-home/profiles/web` 以 `file:` 依赖 + pnpm 快照部署 |
27
+ | 测试 | **327 pass / 0 fail**(33 个 `*.test.mjs`),四闸门 `npm run check` 全绿 |
28
+ | 本机运行时 | DSH Desktop;`dsh-home/profiles/web` 以 **npm registry 标准依赖** `@luchenglong/cli-bridge: "0.2.4"` + pnpm 快照部署(2026-10-15 起不再用 `file:`) |
27
29
 
28
- **交接时点唯一未完成项**:家级补丁里四条 MCP 行刚被恢复,**需重启 DSH 才生效**(§7.3、§11)。
30
+ **已知遗留 / 未处理项**(均有意为之,非故障):
31
+ - 家级补丁 `dsh-home\cordis.patch.yml` 里四条 MCP 行**仍是注释状态** —— 与旧版本交接文档
32
+ §7.3 的描述不符(实测确认)。**用户明确表示此项不由本包管理**,不要擅自恢复(§7.3)。
33
+ - `docs/` 之外还有 `.snapshot-backup-before-npm-install/`(切换安装方式前的快照备份,未入库)。
29
34
 
30
35
  ## 3. 五分钟上手
31
36
 
@@ -41,29 +46,38 @@ Invoke-WebRequest "$env:DSH_WEB_URL/cli-bridge/status" -UseBasicParsing | Sele
41
46
  Invoke-WebRequest "$env:DSH_WEB_URL/cli-bridge/settings" -UseBasicParsing | Select-Object -Expand Content
42
47
  ```
43
48
 
44
- 设置面板:DSH「设置 → CLI Bridge 桥接」→ 横向标签 `总设置 | AGY | CB | MIMO`。
49
+ 设置面板:DSH「设置 → CLI Bridge 桥接」→ 横向标签 `总设置 | AGY | CB | MIMO | OPENCODE`。
45
50
  「总设置」= 调度(勾选启用 + `↑↓` 优先级),其余 = 各 CLI 偏好与诊断。
46
51
 
52
+ **改代码后如何让本机生效(必读,最容易踩)**:
53
+ 本机 profile 装的是 **npm registry 上的包**,不是本地仓库!改了本地代码后必须:
54
+ 1. `npm run check` 全绿 → 升版本 → commit/tag/push → `npm publish`(见 §8);
55
+ 2. 改 `dsh-home/profiles/web/package.json` 的依赖版本 → **删快照** → `pnpm install`(§7.1);
56
+ 3. **重启 DSH**(宿主半启动时加载)。浏览器半 F5 即可。
57
+ 跳过第 1 步 = 本机永远跑旧包(0.2.3 时踩过:代码有 opencode,但 profile 装的是 0.2.2,
58
+ 设置面板看不到 opencode —— 且两边版本号**当时都是 0.2.2**,极具迷惑性)。
59
+
47
60
  ## 4. 代码地图(改哪里)
48
61
 
49
62
  | 路径 | 职责 | 谁维护 |
50
63
  | --- | --- | --- |
51
- | `providers/registry.mjs` | **唯一 id 轴** `PROVIDER_IDS`;新 provider 第一站 | keystone(Lead 单作者) |
64
+ | `providers/registry.mjs` | **唯一 id 轴** `PROVIDER_IDS = ['agy','codebuddy','mimo','opencode']`;新 provider 第一站 | keystone(Lead 单作者) |
52
65
  | `providers/<id>.mjs` | 描述符:工具/policy/settings meta/mcp/diagnose/回退 | 每 provider 作者 |
53
66
  | `core/bridge-kit.mjs` | 共享内核:状态引擎、runner、限流、回退弹窗、MCP payload(**零 import**) | keystone |
54
67
  | `core/scheduling.mjs` | 调度单源(无 import → 可整段内联进沙箱) | keystone |
55
- | `home-plugin/cli-bridge-indicator/lib/index.mjs` | 宿主半:状态机 + HTTP 路由 + 收集器 + 门禁 | keystone |
68
+ | `home-plugin/cli-bridge-indicator/lib/index.mjs` | 宿主半:状态机 + HTTP 路由 + 收集器 + 门禁 + **TTL(含 `PROJECT_DONE_TTL_MS`)** | keystone |
56
69
  | `…/lib/settings.mjs` | 设置读写/掩码/旧文件迁移/`_scheduling` 区 | keystone |
57
- | `…/lib/preset-declarations.mjs` | 声明制注册三个 preset(恒注册,顺序按优先级) | keystone |
70
+ | `…/lib/preset-declarations.mjs` | 声明制注册四个 preset(恒注册,顺序按优先级) | keystone |
58
71
  | `…/lib/client.js` | 浏览器半:pill / 弹窗 / 设置标签页(**红线:load id ≡ 包名**) | keystone |
59
72
  | `preset/<dir>/` | preset 形态(`plugin-list.mjs` 是唯一权威源 + 生成物 `*.yml`) | 生成物入库 |
60
73
  | `dynamic/<id>.host.{js,template.mjs}` | 动态沙箱宿主(模板 → 生成物) | 生成物入库 |
61
- | `mcp/cli-bridge-mcp.mjs` + `mcp/providers/<id>.mjs` | 统一 MCP server(一个 server 暴露三家) | keystone |
62
- | `mcp/{agy,codebuddy,mimo}-mcp-server.mjs` | 旧包兼容入口(等价 `--provider=<id>`) | keystone |
74
+ | `mcp/cli-bridge-mcp.mjs` + `mcp/providers/<id>.mjs` | 统一 MCP server(一个 server 暴露四家) | keystone |
75
+ | `mcp/version.mjs` | **版本号单一来源**(读 package.json;读不到抛错,绝不静默回落) | keystone |
76
+ | `mcp/{agy,codebuddy,mimo}-mcp-server.mjs` | 旧包兼容入口(等价 `--provider=<id>`)。**opencode 没有**(无旧包) | keystone |
63
77
  | `scripts/build.mjs` / `verify.mjs` / `deploy.mjs` | 生成 / 发布红线 / 本地暂存部署 | keystone |
64
78
  | `deploy/legacy-mcp/` | **家级 `bin\` 服务器材料的权威副本**(`bin\` 不入 git) | keystone |
65
- | `tests/keystone/` | 红线行为测试(client 面、状态机、路由、门禁、legacy MCP) | keystone |
66
- | `tests/{agy,codebuddy,mimo}/`、`tests/core/` | 各 provider 与内核回归 | provider 作者 |
79
+ | `tests/keystone/` | 红线行为测试(client 面、状态机、路由、门禁、legacy MCP、TTL) | keystone |
80
+ | `tests/{agy,codebuddy,mimo,opencode}/`、`tests/core/` | 各 provider 与内核回归 | provider 作者 |
67
81
 
68
82
  ## 5. 不可动的契约(红线)
69
83
 
@@ -76,10 +90,17 @@ Invoke-WebRequest "$env:DSH_WEB_URL/cli-bridge/settings" -UseBasicParsing | Sele
76
90
  3. **模板占位符各恰一次**:`/*__KIT__*/`、`/*__PROVIDER__*/`、`/*__SCHED__*/`;生成物
77
91
  (`dynamic/*.host.js`、`preset/*/agent.cordis.yml`、`preset.yml`)**必须入库**,`build --check` 漂移即红。
78
92
  4. **单一 id 轴**:任何地方需要 id 列表都从 `providers/registry.mjs` 派生(`build.mjs`、
79
- `preset-declarations`、client 的 `providerMetas()` 都已改为派生)。禁止第二处硬编码三家 id。
93
+ `preset-declarations`、client 的 `providerMetas()` 都已改为派生)。禁止第二处硬编码 id 列表 ——
94
+ 包括**测试**:0.2.3 时 4 个 keystone 测试硬编码了三家 id,新增 provider 即红;已全部改为派生
95
+ (`PROVIDER_IDS` / `providerMeta()`)。加第五家时这些文件应当**零改动**。
80
96
  5. **零运行时依赖**,且**禁止新增 `@deepseek-ai/dsh*` peer 依赖**(`verify.mjs` + 测试双锁)。
81
97
  6. **家级 `bin\` 三个服务器与 `deploy/legacy-mcp/` 副本逐字节一致**(`tests/keystone/legacy-mcp-gate.test.mjs`)。
82
98
  7. **失败要响亮(开发期)**:不写静默降级/兜底;门禁只做「注册/不注册」,不改工具语义。
99
+ 8. **版本号单一来源**:`mcp/version.mjs` 从 `package.json` 读一次,MCP 各面的 `meta.version`
100
+ 全部派生自它。**禁止在任何 provider 文件里硬编码版本字面量**(0.2.3 教训:8 处硬编码导致
101
+ 「server 报 0.2.3、provider 面报 0.2.2」)。测试只断言 semver 形态,不钉字面量。
102
+ 9. **preset id 命名**:`cordis-` 前缀(agy/mimo)是**旧包历史遗留**,不可套用到新 provider;
103
+ 新增 provider 用 `<id>-first`(与 codebuddy-first 同构)。opencode 因此是 `opencode-first`。
83
104
 
84
105
  ## 6. 调度(0.2.0+)实现要点
85
106
 
@@ -100,21 +121,48 @@ Invoke-WebRequest "$env:DSH_WEB_URL/cli-bridge/settings" -UseBasicParsing | Sele
100
121
  (面板改完立即生效,无需重启子进程);mimo 的 dispatch 层单独加了拒调文本;
101
122
  4. `GET /cli-bridge/status` 与 `/cli-bridge/settings` 每次轮询现读并附 `scheduling` 快照。
102
123
  - **客户端**:`providerMetas()` 以视图 `scheduling.all ∪ providers 键` 为权威 id 轴(本地 `PROVIDERS`
103
- 降级为 fallback,仅补 presetId/展示 label);标签页一排点击切换;`stale` 项目(>10 分钟无活动)
104
- 不再渲染常亮 pill(弹窗里仍保留历史)。
124
+ 降级为 fallback,仅补 presetId/展示 label);标签页一排点击切换;`stale` 项目不再渲染常亮 pill
125
+ (弹窗里仍保留历史)。**stale 判定见 §6.1(0.2.4 起两态 TTL)**。
126
+ - **新增 provider 自动纳入**:新 id 进 `PROVIDER_IDS` 后,`priority` 会自动补上它(漏写排末尾),
127
+ 但 `enabled` **不会**自动加 —— 若用户此前显式设过 `enabled`(如 `[codebuddy,agy]`),新 provider
128
+ 默认**不启用**,需在面板勾选。这不是 bug,是「显式列表被尊重」语义。
129
+
130
+ ### 6.1 状态灯 TTL(0.2.4 起两态)
131
+
132
+ | 项目状态 | TTL | 常量 | 目的 |
133
+ | --- | --- | --- | --- |
134
+ | 运行中 / 无终态 | 10 分钟 | `PROJECT_TTL_MS` | 防抖:长任务中途不闪灯 |
135
+ | **已收口**(`running=0` 且有 `lastStatus`) | **60 秒** | `PROJECT_DONE_TTL_MS` | 跑完快速淡出 |
136
+
137
+ 历史:0.2.1 用单一 10 分钟 TTL 修「一次失败后灯永久挂着」,副作用是**跑完要挂满 10 分钟才灭**,
138
+ 用户感知为「灯不灭」——0.2.4 拆成两态。淡出**只影响 pill 渲染与顶层聚合**,项目仍留在列表里
139
+ (弹窗历史不丢)。详见 `tests/keystone/indicator-state.test.mjs` 末尾 4 个用例。
140
+
141
+ ⚠️ `doneTtlMs` 缺省**跟随显式传入的 `projectTtlMs`**(仅生产默认值才取 60s)——否则
142
+ 「传了短 TTL 却不生效」会静默坑调用方(本改动第一版就踩了这个坑,测试已锁)。
105
143
 
106
144
  ## 7. 本机运行环境接线现状(机器特定)
107
145
 
108
146
  ### 7.1 安装形态
109
147
 
110
- - `dsh-home/profiles/web/package.json`:`"@luchenglong/cli-bridge": "file:C:/Users/lcl/Desktop/DSH插件开发/CLI Bridge"`,
111
- 并在 `dsh.profile.bundles` 登记 → 包内 `cordis.patch.yml` 作为 bundle 层自动挂载(宿主半 + 浏览器半 + 三 preset)。
112
- - **改完必须刷新快照**(pnpm 对 `file:` 依赖按**版本**键控缓存):
148
+ - `dsh-home/profiles/web/package.json`:`"@luchenglong/cli-bridge": "0.2.4"`(**npm registry 标准依赖**,
149
+ 2026-10-15 起;此前是 `file:C:/Users/lcl/Desktop/DSH插件开发/CLI Bridge`),
150
+ 并在 `dsh.profile.bundles` 登记 → 包内 `cordis.patch.yml` 作为 bundle 层自动挂载
151
+ (宿主半 + 浏览器半 + 四 preset)。
152
+ - **⚠️ 因为装的是 npm 包,本地仓库改代码不会自动生效** —— 必须先发布新版本再装(见 §3 方框)。
153
+ - **升版/重装修快照**(pnpm 按**版本**键控缓存,版本不变时可能复用旧快照 —— 必须先删再装):
113
154
  ```powershell
114
- $installed = "$env:APPDATA\DSH Desktop\dsh-home\profiles\web\node_modules\@luchenglong\cli-bridge"
155
+ $web = "$env:APPDATA\DSH Desktop\dsh-home\profiles\web"
156
+ $installed = "$web\node_modules\@luchenglong\cli-bridge"
157
+ # 1) 改 $web\package.json 里的版本号 2) 删快照 3) 重装
115
158
  Remove-Item $installed -Recurse -Force
116
- Push-Location "$env:APPDATA\DSH Desktop\dsh-home\profiles\web"; pnpm install --prefer-offline; Pop-Location
117
- # 校验:Get-FileHash 对比仓库与快照同名文件
159
+ Push-Location $web; pnpm install --prefer-offline; Pop-Location
160
+ # 校验:版本 + 关键文件内容(例:PROVIDER_IDS 是否含新 provider)
161
+ ```
162
+ - 校验安装是真的 registry 包(不是链接/本地目录):
163
+ ```powershell
164
+ (Get-Item $installed -Force).LinkType # 空 = 真实目录(hoisted 模式正确)
165
+ Select-String "$web\pnpm-lock.yaml" -Pattern 'cli-bridge' # 应见 specifier/version + integrity
118
166
  ```
119
167
 
120
168
  ### 7.2 生效时机(**最容易踩**)
@@ -123,23 +171,31 @@ Invoke-WebRequest "$env:DSH_WEB_URL/cli-bridge/settings" -UseBasicParsing | Sele
123
171
  | --- | --- |
124
172
  | `lib/client.js`(浏览器半) | **刷新页面(F5)** 即生效,不用重启 |
125
173
  | `lib/index.mjs`、`lib/settings.mjs`、`preset-declarations.mjs` | **必须重启 DSH**(宿主启动时加载) |
174
+ | `providers/registry.mjs`、描述符、`core/*` | 同上(宿主启动时加载)→ **必须重启 DSH** |
175
+ | 仅发布新版本但没重装/没重启 | **完全不生效**(0.2.3 踩过:面板看不到 opencode) |
126
176
  | `dsh-home/cordis.patch.yml`(家级补丁/插件行) | **必须重启 DSH** |
127
177
  | 家级 `bin\*-mcp-server.mjs` | 子进程重启;但调度门禁是**每请求**读盘,改设置不用重启 |
128
178
  | 统一 `mcp/cli-bridge-mcp.mjs` | 子进程重启后读新配置 |
129
179
 
130
180
  ### 7.3 家级补丁(`dsh-home\cordis.patch.yml`)
131
181
 
132
- 活跃 8 条:`session-cleaner`、`local-llm-bridge`、`bot-gateway`、`mobile-companion`,
133
- 以及 **四条 MCP 行**:`mcp-agy-global` / `mcp-codebuddy-global` / `mcp-playwright-global` / `mcp-mimo-global`
134
- (2026-10-14 从注释状态恢复;备份 `cordis.patch.yml.bak-before-mcp-restore-20261014-234959`)。
182
+ **实测状态(2026-10-16 用规范 YAML 解析器确认):活跃条目只有 4 条** ——
183
+ `session-cleaner`、`local-llm-bridge`、`bot-gateway`、`mobile-companion`。
184
+
185
+ ⚠️ **四条 MCP 行目前仍是注释状态**(`mcp-agy-global` / `mcp-codebuddy-global` /
186
+ `mcp-playwright-global` / `mcp-mimo-global`,形如 `# - id: mcp-agy-global`,
187
+ 位于约 L236/L261/L363/L430/L442/L454/L486)。这与旧版交接文档「2026-10-14 已恢复」的描述
188
+ **不符** —— 实测为准。**用户明确表示此项不由本包管理,不要擅自恢复。**
135
189
 
136
- ⚠️ 该文件里 agy/codebuddy/playwright **各另有一份注释留档副本**(历史)。启用时**只能启用一份**,
137
- 否则同 id 重复 → loader 致命错误。改完必须用规范 YAML 解析器验证:
190
+ ⚠️ 该文件里 agy/codebuddy/playwright **各另有一份注释留档副本**(历史)。将来若真要启用,
191
+ **只能启用一份**,否则同 id 重复 → loader 致命错误。改完必须用规范 YAML 解析器验证:
138
192
 
139
193
  ```powershell
140
194
  node -e "const y=require('$env:APPDATA/DSH Desktop/backend/dsh/node_modules/yaml');const d=y.parse(require('fs').readFileSync('$env:APPDATA/DSH Desktop/dsh-home/cordis.patch.yml','utf8'));const ids=[];for(const e of d)for(const k of Object.keys(e))for(const i of e[k])ids.push(i.id);console.log(d.length, ids.join(','), 'dup:', ids.filter((x,n)=>ids.indexOf(x)!==n))"
141
195
  ```
142
196
 
197
+ (这解释了为什么会话里可能看不到 `mcp__*` 前缀工具 —— 根源在家级补丁,不在本包。)
198
+
143
199
  ### 7.4 家级 MCP 材料
144
200
 
145
201
  `dsh-home\bin\agy-mcp-server.mjs`、`bin\codebuddy-mcp-server.mjs`、`bin\cli-bridge-scheduling.mjs`(门禁 helper)、
@@ -148,10 +204,17 @@ node -e "const y=require('$env:APPDATA/DSH Desktop/backend/dsh/node_modules/yaml
148
204
 
149
205
  ### 7.5 状态与设置
150
206
 
151
- - 设置文件:`dsh-home\cli-bridge-settings.json`(交接时:`enabled=[codebuddy,agy]`、
152
- `priority=[agy,codebuddy,mimo]` → `order=[agy,codebuddy]`,即 **mimo 停用、agy 优先级最高**)。
207
+ - 设置文件:`dsh-home\cli-bridge-settings.json`(当前:`enabled=[agy,codebuddy,opencode]`、
208
+ `priority=[agy,codebuddy,opencode,mimo]` → `order=[agy,codebuddy,opencode]`,
209
+ 即 **mimo 停用**;opencode 于 2026-10-16 手动启用)。
210
+ `opencode` 分区当前 `defaultModel=opencode/nemotron-3-ultra-free`。
153
211
  - 状态路由:`GET /cli-bridge/status`(含 `scheduling`);旧路由 `/<id>-indicator/status` 仍返回裸桶。
212
+ - **灯的实时数据看项目层**:顶层只给 `state/running/lastStatus` 聚合摘要,**`trail`(步骤)和
213
+ `current` 在 `projects[]` 里**。查"它在做什么"要看 `projects[0].trail`(§10)。
154
214
  - GUI 需要登录态;**用 HTTP 探针即可**(不要把无凭据浏览器指向 GUI,会 401)。
215
+ - MCP 形态的状态经快照文件 `{DSH_HOME}/cli-bridge-mcp-<id>.json` 跨进程上报(`buildMcpBridgePayload`
216
+ + 原子写 tmp→rename)。**测试若把 `io.homeDir` 指向临时目录,家级灯就读不到** —— 排查"灯不亮"
217
+ 先看这个文件是否存在于 `dsh-home`。
155
218
 
156
219
  ## 8. 发布流程
157
220
 
@@ -164,6 +227,30 @@ node -e "const y=require('$env:APPDATA/DSH Desktop/backend/dsh/node_modules/yaml
164
227
  4. npm 首次创建 scoped 包返回 `PUT 202 Accepted`("may take a few minutes")**属正常,勿重复发布**。
165
228
 
166
229
  本机发布通道:DSH 工具 `repo_push_access`(GUI 插件 “repo-push-access”,凭据存 DSH 凭据库)。
230
+ 推 git 用插件很稳;**发 npm 建议用命令行** `npm publish --access public`(插件通道曾报
231
+ `published:true` 但实际未生效,也曾报 `fetch failed`)。
232
+
233
+ ### 8.1 npm 登录态(本机特有的坑)
234
+
235
+ 命令行侧**默认没有** npm 登录态,且本机 npm 读的 userconfig **不是** `C:\Users\lcl\.npmrc` ——
236
+ 环境变量 `userconfig` 被 DSH 指向
237
+ `C:\Users\lcl\AppData\Roaming\DSH Desktop\backend\.npmrc`。把 token 写错地方会白忙。
238
+
239
+ ```powershell
240
+ # 从凭据库取 token 写进【npm 实际读取的那份】npmrc,再验证
241
+ $cred = "$env:APPDATA\DSH Desktop\dsh-home\.credentials.yaml"
242
+ $txt = Get-Content $cred -Raw
243
+ $m = [regex]::Match($txt, 'repo-push-access/registry-npmjs-org:(.*?)(?=\n\S|\z)', 16)
244
+ $tok = [regex]::Match($m.Groups[1].Value, 'token:\s*["'']?([A-Za-z0-9_\-\.]+)["'']?').Groups[1].Value
245
+ $t = "C:\Users\lcl\AppData\Roaming\DSH Desktop\backend\.npmrc"
246
+ Set-Content $t -Value (@(Get-Content $t | Where-Object { $_ -notmatch '_authToken' }) + "//registry.npmjs.org/:_authToken=$tok") -Encoding utf8
247
+ npm whoami # 应输出 luchenglong
248
+ ```
249
+
250
+ ⚠️ 若 `.npmrc` 里有**两条** `_authToken`(历史遗留),写之前先去重,只留一条。
251
+ ⚠️ `npm publish` 报 `You cannot publish over the previously published versions: X` = **该版本其实
252
+ 已经发布成功了**(插件通道误报 + CDN 延迟导致你以为失败)。去 registry 核对 shasum,
253
+ **切勿重复发布**。
167
254
 
168
255
  ## 9. 凭据与安全
169
256
 
@@ -181,24 +268,34 @@ node -e "const y=require('$env:APPDATA/DSH Desktop/backend/dsh/node_modules/yaml
181
268
  | 设置面板白屏 / 整个插件页崩 | client.js 引用了未声明标识符,或 load id ≠ 包名 | 跑 `tests/keystone/client-ui-surface.test.mjs`(含标识符扫描)与 `client-id.test.mjs` |
182
269
  | 面板没有横向标签 / 没有调度卡 | 浏览器仍在用旧 client.js | F5 刷新;核对 `GET /cli-bridge/settings` 是否含 `scheduling`(不含=宿主也是旧代码 → 重启) |
183
270
  | 面板改了启用/优先级但没生效 | ①POST 未成功 ②宿主形态需重启会话 ③统一 MCP 子进程需重启 | 看 `cli-bridge-settings.json` 是否写入 `_scheduling`;家级 `bin\` 服务器无需重启(每请求读盘) |
184
- | 指示灯长期亮着 | 陈旧项目(>10 分钟无活动)驱动了顶层聚合 | 0.2.1 已修(客户端过滤 + 服务端聚合只看新鲜项目);复发查 `projects[].stale` 与 `PROJECT_TTL_MS` |
185
- | `pnpm install` 后还是旧代码 | `file:` 依赖按版本键控缓存 | 先 `Remove-Item` 快照目录再 `pnpm install --prefer-offline`,然后哈希比对 |
271
+ | **本地改了代码,但设置面板没变化** | 本机 profile 装的是 **npm 包**,不是本地仓库 | 发布新版本 → 改 profile 依赖版本 → 删快照重装 → 重启 DSH(§3 方框、§7.1) |
272
+ | 面板缺某个 provider 的标签页 | ①装的 npm 包版本太旧(发布时还没该 provider)②该 provider 未启用 ③宿主未重启 | 核对快照 `PROVIDER_IDS`;`GET /cli-bridge/status` 看 `scheduling.all`;重启 DSH |
273
+ | 指示灯长期亮着 | 陈旧项目驱动了顶层聚合 | 查 `projects[].stale` 与两个 TTL(§6.1);0.2.4 起已完成项目 60s 淡出 |
274
+ | **跑完任务灯不灭** | 0.2.3 及更早:单一 10 分钟 TTL | 升到 0.2.4+(已完成项目 60 秒淡出,§6.1) |
275
+ | 想看"它正在做什么"但 `current`/`trail` 是空 | **顶层不含 trail**,步骤在 `projects[]` 里 | 看 `projects[0].trail` / `projects[0].current` |
276
+ | opencode 运行中 `current` 恒为 null | opencode 只在工具【完成时】发事件,无"进行中"事件 | 非缺陷;看 `trail` 的历史步骤即可 |
277
+ | MCP 调用后灯完全不亮 | 测试/调用把 `io.homeDir` 指向了临时目录 | 快照必须写到 `dsh-home`;检查 `cli-bridge-mcp-<id>.json`(§7.5) |
278
+ | `pnpm install` 后还是旧代码 | 依赖按版本键控缓存(版本没变就复用) | 先 `Remove-Item` 快照目录再 `pnpm install --prefer-offline`,然后哈希比对 |
279
+ | `ERR_PNPM_NO_MATCHING_VERSION` | registry CDN 还没同步刚发布的版本 | 等 1–2 分钟重试;**期间先把依赖回滚到上一个可用版本**,别让运行时空着 |
186
280
  | npm 发布 `E402 Payment Required` | scoped 包按私有发布 | `package.json` 加 `publishConfig.access="public"` |
281
+ | npm 发布 `ENEEDAUTH` 但凭据库有 token | token 写到了 `C:\Users\lcl\.npmrc`,而 npm 实际读 backend 那份 | 按 §8.1 写对位置;`.npmrc` 有重复 `_authToken` 先去重 |
187
282
  | npm 发布后 `npm view` 404 | 首次创建 scoped 包在 processing / CDN 负缓存 | 看 npm 日志 `…\.npm-cache\_logs\*` 的 `PUT 202`;等几分钟,别重复发 |
283
+ | `You cannot publish over the previously published versions` | 该版本**已经发布成功**了 | 去 registry 核对 shasum,不要重复发 |
284
+ | npm 自检报版本与 server 不一致 | 某处硬编码了版本字面量 | 都应派生自 `mcp/version.mjs`(红线 §5.8) |
188
285
  | 家级补丁报 `Sequence item without - indicator` | 解除注释时把**段落注释**也剥了(前导 `#` 被误删) | 从 `*.bak-*` 还原,按**条目行区间**精确剥离;用 §7.3 的 YAML 校验命令 |
189
- | 会话里没有 `mcp__*` 工具 | 家级补丁四条 MCP 行被注释 / 未重启 / 子进程未起 | 检查补丁活跃 id、重启 DSH、`Get-CimInstance Win32_Process` 找 node 子进程 |
286
+ | 会话里没有 `mcp__*` 工具 | 家级补丁四条 MCP 行被注释(**当前实测即如此**) / 未重启 / 子进程未起 | §7.3:该项不由本包管理,勿擅自恢复;本包能力走统一 MCP server |
190
287
  | 出现 `profiles.broken-*` | 启动期插件激活失败被桌面壳隔离 | 看 `dsh-home\logs\startup-*.log` 定位首个 error;历史根因是凭据键格式 |
191
288
 
192
289
  ## 11. 待办与已知遗留
193
290
 
194
- 1. **重启 DSH** —— 恢复的四条 MCP 行需要重启才注册(交接时点刚改完配置,尚未重启)。
195
- 重启后:新会话出现 `mcp__agy__*`(4 个,含 quota)、`mcp__codebuddy__*`(3)、`mcp__playwright__*`;
196
- `mcp__mimo__*` 因调度停用而**工具面为空**(这是设计行为,不是故障)。
291
+ 1. **家级补丁四条 MCP 行仍是注释状态**(§7.3)—— 用户明确表示**不由本包管理**,不要擅自恢复。
292
+ 因此会话里可能没有 `mcp__*` 前缀工具;本包能力走**统一 MCP server**,不受影响。
197
293
  2. 可选:`repo-push-access` 插件写临时 npmrc 时带的 `always-auth=true` 触发 npm 11 弃用警告
198
294
  (源码在 `C:\Users\lcl\Desktop\DSH插件开发\git推送管理\repo-push-access`);删掉该行即可静音。
199
- 3. 可选:npm 上的 0.2.2 比仓库少 2 个**纯文档** commit(发布后的 release notes/SOP 补充);
200
- 如需 npm 侧完全一致,可发 0.2.3(无功能改动)。
201
- 4. `dsh-home\profiles.broken-*` 是历史隔离物(用户数据,未清理)。
295
+ 3. 可选:V1/V2 opencode 并存问题 —— 本机**只应保留 V2 桌面版**(详见 §13.1)。若发现 PATH 里出现
296
+ 另一个 `opencode`,说明又误装了 V1,卸载它(V1/V2 **不能并存**,官方明确)。
297
+ 4. 可选:`dsh-home\profiles.broken-*` 是历史隔离物(用户数据,未清理)。
298
+ 5. 可选:`.snapshot-backup-before-npm-install/`(切换安装方式前的快照备份)确认无用后可删。
202
299
 
203
300
  ## 12. 事故史速查(后来者避坑)
204
301
 
@@ -211,16 +308,69 @@ node -e "const y=require('$env:APPDATA/DSH Desktop/backend/dsh/node_modules/yaml
211
308
  机器级配置一律写 `dsh-home\cordis.patch.yml`(家级,不受隔离影响)。
212
309
  - **双源挂载**:家级 `file://` 行与 bundle 层同时声明同一 id → `multiple active Loader sources` 致命错误;
213
310
  统一包只走 bundle 层,家级只保留注释留档。
214
- - **MCP 反复丢失**:家级三条 MCP 行曾在隔离重建中消失且被当基线固化;恢复时误留成注释 →
215
- 本次已恢复为活跃行(§7.3)。
311
+ - **MCP 反复丢失**:家级 MCP 行曾在隔离重建中消失且被当基线固化;恢复时误留成注释。
312
+ 旧版交接文档称"已恢复为活跃行",但 2026-10-16 实测**仍是注释** → 文档描述不可全信,
313
+ 以 YAML 解析器实测为准(§7.3)。
314
+ - **改了代码却以为会生效**:本机装 npm 包而非本地目录,且 0.2.3 时两边版本号**都是 0.2.2**
315
+ → 极具迷惑性。现在 §3 方框写明"发布→重装→重启"三步。
316
+ - **版本号多处硬编码**:8 处版本字面量 → 0.2.3 出现"server 报 0.2.3、provider 面报 0.2.2"。
317
+ 已收敛到 `mcp/version.mjs` 单一来源(红线 §5.8)。
318
+ - **测试里硬编码 id 列表**:4 个 keystone 测试钉死三家 id,新增 provider 即红 → 违反单一 id 轴。
319
+ 已全部改为从 `PROVIDER_IDS` 派生(红线 §5.4)。
320
+
321
+ ## 13. OpenCode provider(0.2.3 新增)
322
+
323
+ 统一包首个**新增** provider(前三个由旧包合并而来,无此兼容包袱):
324
+ 描述符 `providers/opencode.mjs`、preset `opencode-first`、动态宿主、MCP 面 —— 四形态齐全。
325
+ 工具面 `opencode_run` / `opencode_continue` / `opencode_status` + **`opencode_models`**。
326
+
327
+ ### 13.1 本机 OpenCode 事实(重要,别搞错版本)
328
+
329
+ - 装的是 **V2 桌面版**:`C:\Users\lcl\AppData\Roaming\ai.opencode.desktop\cli\2.0.29-x64\opencode-cli.exe`
330
+ (203 MB)。**二进制名是 `opencode-cli.exe`,不是 `opencode.exe`**。
331
+ - **凭据已登录**(OpenCode Console / Personal),`opencode auth list` 可见。
332
+ - ⚠️ **V1(`opencode-ai` npm 包)与 V2(`@opencode/cli` 或桌面版)不能并存安装**,且都叫 `opencode`。
333
+ 曾误装 V1 污染 PATH(导致误判"0 credentials"),已卸载并清理 npm shim。**不要再装 V1。**
334
+ - exe 解析优先级:`OPENCODE_BIN`/`OPENCODE_CLI` env > 桌面版已知路径(版本目录按 semver 降序)
335
+ > PATH 查找 `opencode-cli`。不做 `opencode.exe` 兜底(会误命中另一个版本)。
336
+
337
+ ### 13.2 CLI 接口事实(实测 v2.0.29,勿照抄 V1 文档)
338
+
339
+ - 非交互 = `opencode run "<prompt>"`,prompt 是**位置参数**。
340
+ ⚠️ `run` 子命令**没有 `--prompt`**;`-p` 是 `--password`(basic auth),**不是 prompt**。
341
+ - `--format json` → **NDJSON**(每行一个事件)。
342
+ - `--auto` = 自动批准未显式拒绝的权限(不加会卡在权限提示直到超时)。默认开启,可 `autoApprove:false` 关闭。
343
+ - 会话:`--session <id>` / `--continue`。**没有 `opencode continue` 子命令**。
344
+ - 模型:`--model provider/model`(V2 支持 `#variant` 后缀)。
345
+ - ⚠️ **V2 无 `--variant`**(并入 `--model`)、**无 `--dir`**(cwd 靠 spawn 工作目录)。
346
+ - 退出码才是成败判据:**0 成功 / 1 失败 / 130 SIGINT**。
347
+ - 事件 type 全集:`step_start` / `text` / `reasoning` / `tool_use` / `step_finish` / `error`。
348
+ ⚠️ **不存在 `result`/`complete`/`init` 事件**。
349
+ - ⚠️ 文本在 **`part.text`**;统计(`cost`/`tokens`)在 **`step_finish.part`** —— 两者是**不同**事件
350
+ (第一版实现因此产出"成功但正文为空",见 `tests/opencode/descriptor.test.mjs`)。
351
+
352
+ ### 13.3 免费模型优先
353
+
354
+ `opencode_models` 列出可用模型(本机 46 个 / 11–13 个免费),**免费模型(`-free` 后缀)排在最前**,
355
+ 支持 `freeOnly` / `provider` 过滤。CLI 缺失或未登录时明确返回 `ok:false`,**绝不伪造列表**。
356
+ 实测可用:`opencode/big-pickle`、`opencode/nemotron-3-ultra-free`。
357
+ `glm-5.3-flash` 实测报错(不是所有模型都能用)。
358
+ 设置面板「默认模型」下拉实时调 `opencode models` 填充(约 130ms)。
359
+
360
+ ### 13.4 ⚠️ 调用时必须显式传 cwd
361
+
362
+ - **显式传 `cwd` 参数最可靠**。依赖 `OPENCODE_MCP_CWD` 容易在宿主形态间不一致。
363
+ - **opencode 会谎报成功**:实测它回过 "Verification passed" 但文件其实不在预期位置(落在了进程 cwd)。
364
+ **别信它的自述,落地产物要自己验证**(`existsSync` + 实际执行)。
365
+ - 非 TTY 时 opencode 必读 stdin → 宿主用 `stdio:['ignore',…]`,不会挂起。
216
366
 
217
367
  ---
218
368
 
219
369
  ### 交接最小检查单
220
370
 
221
- - [ ] `npm run check` 全绿(271 测试)
222
- - [ ] `node mcp/cli-bridge-mcp.mjs --check` 显示 `scheduling.source`
223
- - [ ] `GET $env:DSH_WEB_URL/cli-bridge/status` 返回三家 + `scheduling`
224
- - [ ] 重启 DSH 后确认四条 MCP 工具面(含 mimo 为空)
225
- - [ ] 设置面板能勾选/排序/保存,且 `cli-bridge-settings.json` 落盘
226
- - [ ] 知道红线在 §5、生效时机在 §7.2、排障在 §10
371
+ - [ ] `npm run check` 全绿(327 测试)
372
+ - [ ] `node mcp/cli-bridge-mcp.mjs --check` 显示 `scheduling.source` 且版本与 package.json 一致
373
+ - [ ] `GET $env:DSH_WEB_URL/cli-bridge/status` 返回**四家** + `scheduling`
374
+ - [ ] 设置面板有 `总设置 | AGY | CB | MIMO | OPENCODE` 五个标签;OpenCode 卡能选模型并保存落盘
375
+ - [ ] 起一个 opencode 任务:灯亮 → 完成后 **60 秒内灭**(§6.1)
376
+ - [ ] 知道红线在 §5、生效时机在 §7.2、排障在 §10、OpenCode 在 §13
@@ -575,7 +575,7 @@ function createRunner(o) {
575
575
  // - 本文件会被 build 内联进 dynamic 沙箱(new Function):绝不出现 import.meta 字面量;
576
576
  // process 只经 deps 注入或 typeof 守卫读取;宿主能力全部走 deps/attach(hostOpts)。
577
577
  // - 只允许 import '../core/bridge-kit.mjs'。
578
-
578
+ // import { createStatusEngine, createRunner, clampInt, isLimited } from '../core/bridge-kit.mjs'
579
579
  // subprocess stdio 预算(与 bridge-kit createRunner 内部 stdio 一致;quota 脚本用)。
580
580
  const STDIO = {
581
581
  stdin: 'ignore',