harness-mix 0.1.0 → 0.1.2
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 +10 -0
- package/README.md +52 -21
- package/config/codex-desktop-compatibility.json +6 -0
- package/docs/harness-management.md +4 -1
- package/docs/kiro-cursor-integration.md +3 -0
- package/docs/multi-agent-collaboration.md +4 -4
- package/docs/native-acp.md +122 -0
- package/docs/native-codex.md +29 -6
- package/docs/superpowers/specs/2026-09-13-native-hardening-design.md +326 -0
- package/output/native-build/desktop-controller.mjs +21 -15
- package/output/native-build/harness-mix-secret.exe +0 -0
- package/output/native-build/harness-mix-shim.exe +0 -0
- package/output/native-build/renderer-extension.js +102 -70
- package/package.json +22 -4
- package/scripts/antigravity-adapter-test.cjs +507 -152
- package/scripts/build-native.cjs +53 -46
- package/scripts/codebuddy-migration-test.cjs +47 -0
- package/scripts/collaboration-test.cjs +13 -7
- package/scripts/concurrency-layers-test.cjs +2 -0
- package/scripts/diagnostics.cjs +60 -0
- package/scripts/dsh-adapter-test.cjs +3 -0
- package/scripts/dsh-collaboration-acp-test.cjs +19 -0
- package/scripts/e2e-collaboration.cjs +5 -3
- package/scripts/e2e-native-acp.cjs +42 -0
- package/scripts/harness-health-check.cjs +72 -0
- package/scripts/kiro-cursor-adapters-test.cjs +82 -0
- package/scripts/native-acp-depth-test.cjs +189 -0
- package/scripts/native-job-object-test.cjs +79 -0
- package/scripts/native-protocol-test.cjs +7 -2
- package/scripts/native-secret-test.cjs +57 -0
- package/scripts/native-update-apply-test.cjs +215 -0
- package/scripts/native-updater-test.cjs +10 -1
- package/scripts/native-vendor-adapters-test.cjs +49 -1
- package/scripts/thread-title-test.cjs +93 -0
- package/scripts/tool-concurrency-regression-test.cjs +3 -2
- package/src/assets/icons/codebuddy-color.svg +1 -0
- package/src/assets/icons/cursor-cli-color.svg +1 -0
- package/src/assets/icons/cursor.svg +1 -0
- package/src/assets/icons/kiro-cli-color.svg +11 -0
- package/src/assets/icons/model-grok.svg +1 -0
- package/src/main/adapters/acp-interactions.js +132 -0
- package/src/main/adapters/acp.js +28 -8
- package/src/main/adapters/antigravity.js +1330 -888
- package/src/main/adapters/claude.js +136 -16
- package/src/main/adapters/codebuddy-history.js +90 -0
- package/src/main/adapters/codebuddy.js +2 -0
- package/src/main/adapters/codex.js +12 -2
- package/src/main/adapters/cursor.js +2 -0
- package/src/main/adapters/dsh-web-host.js +3 -10
- package/src/main/adapters/dsh.js +35 -5
- package/src/main/adapters/grok.js +126 -10
- package/src/main/adapters/index.js +4 -2
- package/src/main/adapters/kiro-history.js +43 -0
- package/src/main/adapters/kiro.js +2 -0
- package/src/main/adapters/native-acp-command.js +47 -0
- package/src/main/adapters/native-acp.js +435 -0
- package/src/main/adapters/openclaw-gateway.js +2 -6
- package/src/main/adapters/opencode-server.js +7 -6
- package/src/main/adapters/opencode.js +6 -2
- package/src/main/adapters/pi-family.js +34 -7
- package/src/main/adapters/qoder.js +5 -33
- package/src/main/adapters/trae.js +5 -17
- package/src/main/adapters/workbuddy.js +2 -67
- package/src/main/adapters/zcode.js +5 -36
- package/src/main/harness-adapter/event-normalizer.js +31 -12
- package/src/main/host/collaboration-worktree.js +12 -2
- package/src/main/host/jsonl.js +2 -1
- package/src/main/host/native-history.js +5 -0
- package/src/main/host/runtime.js +108 -6
- package/src/main/host/session-history.js +2 -2
- package/src/main/host/store.js +73 -42
- package/src/main/native/config.js +53 -49
- package/src/main/native/host.js +46 -2
- package/src/main/native/icons.js +105 -101
- package/src/main/native/launcher.js +79 -13
- package/src/main/native/process-utils.js +30 -0
- package/src/main/native/protocol.js +607 -552
- package/src/main/native/redact.js +35 -0
- package/src/main/native/rs/Cargo.lock +4 -0
- package/src/main/native/rs/Cargo.toml +1 -1
- package/src/main/native/rs/crates/secret/Cargo.toml +10 -0
- package/src/main/native/rs/crates/secret/src/main.rs +262 -0
- package/src/main/native/rs/crates/shim/src/job.rs +83 -0
- package/src/main/native/rs/crates/shim/src/main.rs +236 -207
- package/src/main/native/secure-store.js +58 -0
- package/src/main/native/update-state.js +110 -0
- package/src/main/native/updater.js +300 -8
- package/src/main/native/usage.js +84 -8
- package/src/main/shared-contracts/item.js +1 -0
- package/src/main/workspace/files.js +76 -65
- package/src/main/workspace/review-controller.js +102 -102
- package/src/main/workspace/review.js +88 -83
- package/src/main/workspace/terminal.js +2 -4
- package/src/native-ui/README.md +4 -0
- package/src/native-ui/desktop-control/dist/main-process-title-policy.d.ts.map +1 -1
- package/src/native-ui/desktop-control/dist/main-process-title-policy.js +38 -0
- package/src/native-ui/desktop-control/dist/main-process-title-policy.js.map +1 -1
- package/src/native-ui/desktop-control/dist/production-controller.d.ts.map +1 -1
- package/src/native-ui/desktop-control/dist/production-controller.js +4 -1
- package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.d.ts +1 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.d.ts.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.js +22 -11
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.js.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.d.ts +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.d.ts.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +7 -3
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -1
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/desktop-control/src/main-process-title-policy.ts +38 -0
- package/src/native-ui/desktop-control/src/production-controller.ts +4 -1
- package/src/native-ui/desktop-control/src/renderer-draft-prewarm-policy.ts +19 -11
- package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +7 -4
- package/src/native-ui/desktop-control/test/main-process-title-policy.test.ts +2 -0
- package/src/native-ui/desktop-control/test/production-controller.test.ts +4 -1
- package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +36 -5
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +6 -4
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/harness-mix-icons.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-icon.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts +3 -3
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +3 -3
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/src/agent-group-preference.ts +1 -1
- package/src/native-ui/renderer-extension/src/agent-selection-state.ts +33 -16
- package/src/native-ui/renderer-extension/src/harness-mix-icons.ts +8 -1
- package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +146 -145
- package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +2 -1
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +2934 -2929
- package/src/native-ui/renderer-extension/src/renderer-new-thread-preference.ts +2 -0
- package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +2 -1
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +1355 -1354
- package/src/native-ui/renderer-extension/src/settings/icons.ts +141 -141
- package/src/native-ui/renderer-extension/src/settings/localization.ts +752 -752
- package/src/native-ui/renderer-extension/src/settings/pages.ts +615 -615
- package/src/native-ui/renderer-extension/src/settings/shell.css +2176 -2149
- package/src/native-ui/renderer-extension/src/settings/shell.ts +329 -329
- package/src/native-ui/renderer-extension/src/settings/trigger.ts +323 -323
- package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +13 -2
- package/src/native-ui/renderer-extension/test/codebuddy-migration.test.ts +22 -0
- package/src/native-ui/renderer-extension/test/harness-mix-icons.test.ts +58 -0
- package/src/native-ui/renderer-extension/test/settings/harness-settings.test.ts +7 -3
- package/src/native-ui/renderer-extension/test/settings/localization.test.ts +80 -80
- package/src/native-ui/renderer-extension/test/settings/pages.test.ts +2000 -2000
- package/src/native-ui/renderer-extension/test/versioned-renderer-adapter.test.ts +13 -0
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/shared-contracts/dist/updates.js +1 -1
- package/src/native-ui/shared-contracts/dist/updates.js.map +1 -1
- package/src/native-ui/shared-contracts/src/updates.ts +78 -78
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Harness Mix 原生加固设计:自更新 / 进程监管 / 安全存储
|
|
2
|
+
|
|
3
|
+
- 日期:2026-09-13
|
|
4
|
+
- 状态:设计已评审通过(用户确认),待实现
|
|
5
|
+
- 约束:Windows 优先;保持零外部 crate(GNU 工具链可构建)、零新增 npm 依赖
|
|
6
|
+
|
|
7
|
+
## 1. 背景与现状
|
|
8
|
+
|
|
9
|
+
### 1.1 自更新(`src/main/native/updater.js`)
|
|
10
|
+
|
|
11
|
+
现状:每次 `npm start` 前检查 `origin` 上游;可快进且工作区干净时 `merge --ff-only`,按需
|
|
12
|
+
`npm install` 与 `npm run build:native`;dirty / ahead / diverged 跳过;失败仅打印、不阻塞启动。
|
|
13
|
+
|
|
14
|
+
问题:
|
|
15
|
+
|
|
16
|
+
1. npm 安装形态(包根无 `.git`)无法自动更新,每次启动还会打印"检查失败"。
|
|
17
|
+
2. 无锁、无校验、无回滚;`install` / `build` 失败会留下半更新状态。
|
|
18
|
+
3. 无 pending / 重试机制;桌面进程或文件占用时无处置。
|
|
19
|
+
4. `scripts/native-updater-test.cjs` 只覆盖决策矩阵,没有应用与回滚路径。
|
|
20
|
+
|
|
21
|
+
### 1.2 进程监管
|
|
22
|
+
|
|
23
|
+
现状(链路:Codex Desktop → `harness-mix-shim.exe` → `node scripts/native-host.cjs` →
|
|
24
|
+
HostRuntime → 各家 CLI / pwsh 终端):
|
|
25
|
+
|
|
26
|
+
- 优雅关闭:`host.js` 在 stdin close / SIGTERM / SIGINT 时 `runtime.close()` → 各
|
|
27
|
+
`adapter.close()` → 官方 app-server `stdin.end()` + 2s 后 `kill()`。
|
|
28
|
+
- 问题:
|
|
29
|
+
1. 强杀(任务管理器、OOM、shim 超时)时后代进程不在同一终止语义内。Windows 上
|
|
30
|
+
`child.kill()` 只杀单进程,harness CLI 会成为孤儿继续执行(仍可能修改工作区文件)。
|
|
31
|
+
2. 适配器终止方式不统一:`dsh-web-host` / `openclaw-gateway` / `opencode-server` /
|
|
32
|
+
`terminal` 用 `taskkill /T /F`;`antigravity` / `jsonl` 等用裸 `child.kill()`。
|
|
33
|
+
3. 无心跳、无启动清扫、无单实例保护、无崩溃报告与恢复标记。
|
|
34
|
+
|
|
35
|
+
### 1.3 安全存储
|
|
36
|
+
|
|
37
|
+
现状:设计上不落盘凭据(`harness-mix-settings.json` 只存可执行文件路径;协作密钥、
|
|
38
|
+
Antigravity question token 均为内存生成、按需注入子进程环境)。
|
|
39
|
+
|
|
40
|
+
风险:
|
|
41
|
+
|
|
42
|
+
1. 明文落盘的敏感数据:会话存储(`mix-core`)、`host-traffic.jsonl`(含 prompt /
|
|
43
|
+
工具参数摘要,单字段最长 1500 字符)、`shim-invocations.log`(argv + stderr 尾巴)。
|
|
44
|
+
2. 没有平台级保险箱可承接未来的凭据(私有 registry token、直连模型密钥等)。
|
|
45
|
+
|
|
46
|
+
## 2. 目标与非目标
|
|
47
|
+
|
|
48
|
+
目标:
|
|
49
|
+
|
|
50
|
+
- 自更新:双通道(git / npm)可靠更新——锁、重试、挂起、校验(npm integrity)、
|
|
51
|
+
崩溃自动回滚;任何失败不阻塞启动。
|
|
52
|
+
- 进程监管:系统级级联清理(Job Object)+ 心跳 / 启动清扫 / 恢复标记 / 崩溃报告。
|
|
53
|
+
- 安全存储:平台保险箱 primitive + 日志脱敏与轮转。
|
|
54
|
+
|
|
55
|
+
非目标:
|
|
56
|
+
|
|
57
|
+
- Windows 安装包、代码签名、CI 发布流水线(属 `NEXT-STEPS.md` P3 后续)。
|
|
58
|
+
- portable(解压即用)通道的自替换器——待该分发形态出现再做;本设计不手写
|
|
59
|
+
tarball 下载 / 原子换位。
|
|
60
|
+
- Linux / macOS 原生实现(保持 Windows 优先)。
|
|
61
|
+
- 会话库加密、凭据迁移(当前没有需要迁移的凭据)。
|
|
62
|
+
|
|
63
|
+
## 3. 总体设计
|
|
64
|
+
|
|
65
|
+
三块遵循同一原则:
|
|
66
|
+
|
|
67
|
+
- **Rust 只做 OS 边界能力**:Job Object(shim 增量)、DPAPI(新 secret crate)。
|
|
68
|
+
零外部 crate,`x86_64-pc-windows-gnu` 干净工具链可构建。
|
|
69
|
+
- **编排与状态机在 JS**:更新状态机、心跳、清扫、脱敏。
|
|
70
|
+
- **失败一律不阻塞**:任何加固失败只降级 / 告警,不影响启动与协议链路。
|
|
71
|
+
- 新增 Rust 产物:`harness-mix-secret.exe`;shim 增加 Job Object 与日志轮转,
|
|
72
|
+
不新增 crate 依赖。
|
|
73
|
+
|
|
74
|
+
## 4. 自更新 v2
|
|
75
|
+
|
|
76
|
+
### 4.1 通道识别
|
|
77
|
+
|
|
78
|
+
`detectChannel(root)`:
|
|
79
|
+
|
|
80
|
+
- `root/.git` 存在 → `git`;
|
|
81
|
+
- 否则 `basename(dirname(root)) === 'node_modules'` → `npm`;
|
|
82
|
+
- 否则 → `portable`(本设计不做,打印"暂不支持自动更新")。
|
|
83
|
+
|
|
84
|
+
### 4.2 锁与状态
|
|
85
|
+
|
|
86
|
+
- 锁:`<data>/update.lock`(内容 `{pid, ts, op}`),互斥所有变更操作
|
|
87
|
+
(apply / rollback / repair)。stale 判定:pid 不存在,或 ts 超过 30 分钟。
|
|
88
|
+
只读检查不取锁。
|
|
89
|
+
- 状态:`<data>/update-state.json`:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"schema": 1,
|
|
94
|
+
"channel": "git|npm",
|
|
95
|
+
"phase": "idle|applying",
|
|
96
|
+
"appliedVersion": "0.2.0",
|
|
97
|
+
"prevVersion": "0.1.1",
|
|
98
|
+
"appliedAt": 0,
|
|
99
|
+
"attempts": 0,
|
|
100
|
+
"lastBootOkAt": 0,
|
|
101
|
+
"pendingVersion": null,
|
|
102
|
+
"lastCheckAt": 0,
|
|
103
|
+
"preUpdateHead": "<sha>"
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`<data>` = `CODEXHOST_DATA_DIR`(默认 `%APPDATA%\harness-mix\codexhost`)。
|
|
108
|
+
|
|
109
|
+
### 4.3 git 通道
|
|
110
|
+
|
|
111
|
+
保留现有决策矩阵(current / ahead / diverged / dirty / available),新增:
|
|
112
|
+
|
|
113
|
+
1. `merge --ff-only` 前记录 `preUpdateHead`,置 `phase:"applying"`。
|
|
114
|
+
2. `install` / `build` 失败 → `git reset --hard <preUpdateHead>`,恢复
|
|
115
|
+
`phase:"idle"`,打印原因,继续启动。
|
|
116
|
+
3. 全程持锁;完成或回滚后释放。
|
|
117
|
+
|
|
118
|
+
### 4.4 npm 通道
|
|
119
|
+
|
|
120
|
+
- 安装范围检测:仅当包根位于 npm 全局目录(`npm root -g`)时才自动应用;
|
|
121
|
+
本地依赖 / `npm link` 形态只打印提示 `npm install -g harness-mix@latest`。
|
|
122
|
+
- 发现:读当前 `package.json.version` → `GET {registry}/harness-mix`
|
|
123
|
+
(`Accept: application/vnd.npm.install-v1+json`,5s 超时,ETag 缓存于
|
|
124
|
+
`<data>/update-etag.json`)→ 比较 `dist-tags.latest`(预发布版本需
|
|
125
|
+
`HARNESS_MIX_UPDATE_PRERELEASE=1`)。
|
|
126
|
+
- registry 解析顺序:`HARNESS_MIX_UPDATE_REGISTRY` → `npm_config_registry` →
|
|
127
|
+
`npm config get registry`(结果缓存)→ `https://registry.npmjs.org/`。
|
|
128
|
+
- 应用(先落盘 `phase:"applying"`):
|
|
129
|
+
1. 先停止桌面(复用 launcher 现有的按 ExePath 精确 `Stop-Process` 逻辑)并等待 3s;
|
|
130
|
+
2. `npm install -g harness-mix@<version> --no-audit --no-fund`
|
|
131
|
+
(`windowsHide`,超时 180s);失败重试 ≤3 次,间隔 2s;
|
|
132
|
+
3. 仍失败 → 写 `pendingVersion`,恢复 `phase:"idle"`,继续用旧版启动;
|
|
133
|
+
4. 成功 → 记录 `prevVersion` 与 `appliedAt`,re-exec 同一 bin
|
|
134
|
+
(`process.execPath scripts/launch-codex.cjs` + 原参数),env 带
|
|
135
|
+
`HARNESS_MIX_UPDATED=1`(防循环)。
|
|
136
|
+
- 校验:由 npm 自身的 integrity(sha512)承担;本设计不手写下载 / 解包。
|
|
137
|
+
- 启动时若存在 `pendingVersion`,按 4.5 先尝试应用;失败则继续旧版并保留 pending。
|
|
138
|
+
|
|
139
|
+
### 4.5 回滚状态机
|
|
140
|
+
|
|
141
|
+
launcher 入口(持锁):
|
|
142
|
+
|
|
143
|
+
1. **中断修复**:`phase:"applying"` 残留 → 上次应用中断,按通道修复
|
|
144
|
+
(npm:重装 `appliedVersion || prevVersion`;git:`reset --hard preUpdateHead`)。
|
|
145
|
+
2. **崩溃循环回滚**:若 `appliedVersion` 存在且 `attempts ≥ 2` 且
|
|
146
|
+
`lastBootOkAt < appliedAt` → 自动回滚:npm 重装 `prevVersion`、git
|
|
147
|
+
`reset --hard preUpdateHead` + 重建;记录并横幅告警。没有 `prevVersion`
|
|
148
|
+
时只告警不回滚。
|
|
149
|
+
3. **计数**:每次启动,当 `appliedVersion` 存在且尚未记录 boot-ok 时
|
|
150
|
+
`attempts += 1`;启动成功(桌面激活 + controller 存活 ≥20s)写
|
|
151
|
+
`lastBootOkAt = now`、`attempts = 0`。
|
|
152
|
+
4. **外部版本变化**:`package.json.version` 与 `appliedVersion` / `prevVersion`
|
|
153
|
+
都不一致(用户手动升级)→ 重置状态为 idle,不触发回滚。
|
|
154
|
+
|
|
155
|
+
### 4.6 launcher 顺序变化
|
|
156
|
+
|
|
157
|
+
现状:`autoUpdate` → inspect → 停桌面 → 激活。
|
|
158
|
+
|
|
159
|
+
改为:
|
|
160
|
+
|
|
161
|
+
1. 只读检查(含 pending);
|
|
162
|
+
2. 有更新 / 待应用时:**停桌面 → 应用(持锁)→ re-exec**;
|
|
163
|
+
3. (新进程)inspect → 兼容检查 → 激活。
|
|
164
|
+
|
|
165
|
+
`--update`(`npm run update`)语义更新为"立即检查并应用(必要时停一次桌面),
|
|
166
|
+
不启动"。`--no-update` / `HARNESS_MIX_AUTO_UPDATE=0` 保持不变。
|
|
167
|
+
|
|
168
|
+
### 4.7 错误与降级矩阵
|
|
169
|
+
|
|
170
|
+
| 场景 | 行为 |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| 离线 / registry 超时 | 打印原因,跳过本次,继续启动 |
|
|
173
|
+
| 无权限 / 文件占用 | 重试 3 次 → 写 pending → 继续旧版启动 |
|
|
174
|
+
| npm 不存在 | 提示手动命令,本次弃用 npm 通道 |
|
|
175
|
+
| 应用中断(进程被杀) | 下次启动 repair(4.5-1) |
|
|
176
|
+
| 连续 2 次启动失败 | 回滚到 prevVersion(4.5-2) |
|
|
177
|
+
| 状态文件损坏 | 视为 idle 重建,不阻塞 |
|
|
178
|
+
|
|
179
|
+
### 4.8 测试
|
|
180
|
+
|
|
181
|
+
- 扩展 `scripts/native-updater-test.cjs`(mock):通道识别、semver 比较、
|
|
182
|
+
锁 stale 判定、状态机(attempts / 回滚 / repair / 外部版本变化)。
|
|
183
|
+
- 新增 `scripts/native-update-apply-test.cjs`(离线):
|
|
184
|
+
1. `npm pack` 造 fixture tarball,本地 http server 冒充 registry 元数据;
|
|
185
|
+
2. `npm install --prefix <tmp> harness-mix@file:<tarball>` 造临时安装;
|
|
186
|
+
3. 覆盖:应用到新版本、挂起(模拟占用失败)、回滚、`phase:"applying"` 修复。
|
|
187
|
+
- git 通道回退:本地 bare 仓 fixture 造 build 失败 → 断言 reset 回旧 HEAD。
|
|
188
|
+
|
|
189
|
+
## 5. 进程监管 v2
|
|
190
|
+
|
|
191
|
+
### 5.1 Job Object(shim,Rust 零依赖 FFI)
|
|
192
|
+
|
|
193
|
+
- `run()` 启动即执行:
|
|
194
|
+
1. `CreateJobObjectW(null, null)`;
|
|
195
|
+
2. `SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
|
|
196
|
+
LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)`;
|
|
197
|
+
3. `AssignProcessToJobObject(hJob, GetCurrentProcess())`。
|
|
198
|
+
- 效果:node 宿主、官方 app-server、各家 CLI、pwsh 终端全部随 spawn 自动继承
|
|
199
|
+
job;**shim 无论以何种方式消亡(强杀 / 崩溃),句柄关闭即触发系统级联杀死
|
|
200
|
+
全部成员**。
|
|
201
|
+
- 失败降级:任一步失败 → stderr 告警,继续运行,不阻塞 CLI。
|
|
202
|
+
- 兼容性:Win8+ 支持嵌套 job(桌面自身若在 job 中不受影响);绝不把 Codex
|
|
203
|
+
Desktop 本体放入 job。
|
|
204
|
+
- 测试钩子:新增 `HARNESS_MIX_HOST_SCRIPT` env(server 模式下覆盖默认
|
|
205
|
+
`scripts/native-host.cjs` 路径,仅测试用;默认行为不变)。
|
|
206
|
+
- shim 日志:`shim-invocations.log` 增加 5MB × 3 轮转。
|
|
207
|
+
|
|
208
|
+
### 5.2 心跳与崩溃报告(`host.js`)
|
|
209
|
+
|
|
210
|
+
- `<data>/runtime/instance.json`:`{pid, version, startedAt, beatAt, mode}`,
|
|
211
|
+
每 5s 更新;正常关闭时删除。
|
|
212
|
+
- `uncaughtException` / `unhandledRejection` / `SIGBREAK` → 写
|
|
213
|
+
`<data>/runtime/crash-<ts>.json`(error / stack / pid / version)→ 尝试
|
|
214
|
+
`close()` → 退出码 1。既有 SIGTERM / SIGINT 处理保持不变。
|
|
215
|
+
|
|
216
|
+
### 5.3 启动清扫(`launcher.js`)
|
|
217
|
+
|
|
218
|
+
停桌面之后、激活之前:
|
|
219
|
+
|
|
220
|
+
1. 读 `instance.json`:心跳新鲜且 pid 存活 → 记录"另一实例仍在运行"(更新锁同理),
|
|
221
|
+
继续现有流程(桌面重启会令旧宿主自行退出)。
|
|
222
|
+
2. 清扫:PowerShell CIM 查询匹配(本项目 `output/native-build` 下的
|
|
223
|
+
`harness-mix-shim.exe`、命令行含 `native-host.cjs` 或 `desktop-controller.mjs`
|
|
224
|
+
的 node 进程),排除当前进程链后 `taskkill /T /F`;记录数量与耗时至启动日志。
|
|
225
|
+
3. 心跳陈旧(>30s)或 pid 已死 → 必定清扫(含实例记录 pid)。
|
|
226
|
+
|
|
227
|
+
### 5.4 进程树终止统一
|
|
228
|
+
|
|
229
|
+
- 新增 `src/main/native/process-utils.js`:`terminateTree(pid)`(win32:
|
|
230
|
+
`taskkill /PID <pid> /T /F` 等待完成;其他平台 `process.kill(-pid)` 兜底)。
|
|
231
|
+
- 替换调用点:`antigravity.js`、`jsonl.js`、`host.js`(official 的 2s 超时
|
|
232
|
+
kill),以及已用 taskkill 的 `dsh-web-host.js`、`openclaw-gateway.js`、
|
|
233
|
+
`opencode-server.js`、`workspace/terminal.js` 统一走该函数(行为不变、实现单点)。
|
|
234
|
+
|
|
235
|
+
### 5.5 启动恢复
|
|
236
|
+
|
|
237
|
+
- `HostRuntime` 加载存储时,把 `working` 状态的线程 / 轮次统一标记为
|
|
238
|
+
`interrupted`(附"宿主异常退出"说明),与 `collaboration` 现有
|
|
239
|
+
`interrupted` 语义一致(`src/main/host/collaboration.js` 已实现,线程侧补齐)。
|
|
240
|
+
|
|
241
|
+
### 5.6 测试
|
|
242
|
+
|
|
243
|
+
- `scripts/native-job-object-test.cjs`(e2e,需要已构建 shim):
|
|
244
|
+
1. fixture 假宿主(spawn 一个长命孙进程并写 pid 文件),shim 以 server 模式 +
|
|
245
|
+
`HARNESS_MIX_HOST_SCRIPT` 启动;
|
|
246
|
+
2. `taskkill /F` 强杀 shim;
|
|
247
|
+
3. 断言宿主与孙进程 3s 内消失(`process.kill(pid, 0)` 抛 ESRCH)。
|
|
248
|
+
- 心跳 / 清扫 / 锁:mock exec 单测;清扫命令的真实语法用 `--check` 冒烟验证
|
|
249
|
+
(不杀真实进程)。
|
|
250
|
+
|
|
251
|
+
## 6. 安全存储 v2(保险箱 + 落盘治理)
|
|
252
|
+
|
|
253
|
+
### 6.1 `harness-mix-secret`(新 Rust bin,零依赖)
|
|
254
|
+
|
|
255
|
+
- FFI:`crypt32.CryptProtectData` / `CryptUnprotectData`
|
|
256
|
+
(`CRYPTPROTECT_UI_FORBIDDEN`,当前用户范围)。
|
|
257
|
+
- CLI:`set <name>`(值从 stdin)| `get <name>`(stdout;缺失退出码 2)|
|
|
258
|
+
`delete <name>` | `list`(仅名称)。名称规则 `[A-Za-z0-9._-]{1,64}`。
|
|
259
|
+
- 存储:`<data>/secrets.dat`,格式
|
|
260
|
+
`{"version":1,"entries":{"name":"<base64 dpapi blob>"}}`,
|
|
261
|
+
写临时文件 + 原子替换。
|
|
262
|
+
- 构建:加入现有 workspace 与 `build-native.cjs` 拷贝列表;
|
|
263
|
+
`package.json` 的 `files` 增加该 exe。
|
|
264
|
+
|
|
265
|
+
### 6.2 `src/main/native/secure-store.js`
|
|
266
|
+
|
|
267
|
+
- `getSecret / setSecret / deleteSecret / listSecrets`,execFile 调 exe;
|
|
268
|
+
helper 缺失 → 抛"运行 npm run build:native"的可操作错误。
|
|
269
|
+
- 本阶段无消费者(当前没有需要持久化的凭据);为后续私有 registry token、
|
|
270
|
+
直连模型密钥预留。
|
|
271
|
+
|
|
272
|
+
### 6.3 日志脱敏与轮转
|
|
273
|
+
|
|
274
|
+
- 脱敏器(`src/main/native/redact.js`)规则:
|
|
275
|
+
- `Bearer <token>`、`sk-*`、`ghp_*`、`xox*-*` 等模式 → `[redacted]`;
|
|
276
|
+
- 对象键匹配 `/(token|secret|password|passwd|apikey|api_key|authorization|credential)/i`
|
|
277
|
+
→ `[redacted]`。
|
|
278
|
+
- 应用点:`host.js` 的 `slim()`(traffic 日志写盘前);诊断导出复用同一脱敏器。
|
|
279
|
+
- 轮转:`host-traffic.jsonl` 与 `shim-invocations.log` 均为 5MB × 3。
|
|
280
|
+
|
|
281
|
+
### 6.4 测试
|
|
282
|
+
|
|
283
|
+
- 脱敏单测:真实模式样例 + 误伤样例(普通文本中的 `sk-` 前缀等)。
|
|
284
|
+
- `scripts/native-secret-test.cjs`:真 exe round-trip(set / get / delete /
|
|
285
|
+
list)、缺名报错、helper 缺失路径(临时改名模拟)。
|
|
286
|
+
|
|
287
|
+
## 7. 交付阶段
|
|
288
|
+
|
|
289
|
+
- **Phase 1**:自更新 v2(§4)+ 监管核心(§5.1 / 5.2 / 5.3 / 5.5)。
|
|
290
|
+
验收:现有测试全绿;新增单测 / 集成测试全绿;手工场景(离线、文件占用、
|
|
291
|
+
崩溃循环回滚、强杀级联)通过。
|
|
292
|
+
- **Phase 2**:终止统一(§5.4)+ 崩溃报告细化 + `npm run diagnostics`
|
|
293
|
+
(打包版本、兼容矩阵、脱敏日志、崩溃报告,tar 打包)。
|
|
294
|
+
- **Phase 3**:保险箱与落盘治理(§6)+ 文档更新(`docs/native-codex.md`
|
|
295
|
+
增补安全存储、进程监管、更新机制三节)。
|
|
296
|
+
|
|
297
|
+
## 8. 风险与缓解
|
|
298
|
+
|
|
299
|
+
| 风险 | 缓解 |
|
|
300
|
+
| --- | --- |
|
|
301
|
+
| `npm install -g` 自身文件占用 | 先停桌面 + 重试 + pending + 回滚;最坏给出手动命令 |
|
|
302
|
+
| 嵌套 Job 兼容性 | 仅 Win8+;失败仅告警,功能退回现状(不劣化) |
|
|
303
|
+
| 崩溃循环误判 | 阈值 2 次且必须有 `prevVersion`;回滚目标为上次成功版本 |
|
|
304
|
+
| boot-ok 判据(controller 存活 20s)为启发式 | 先上线,后续按需收紧 |
|
|
305
|
+
| `--update` 现在会停桌面 | 行为变化,写入文档与 `--check` 帮助输出 |
|
|
306
|
+
|
|
307
|
+
## 9. 文件清单(预告)
|
|
308
|
+
|
|
309
|
+
新增:
|
|
310
|
+
|
|
311
|
+
- `docs/superpowers/specs/2026-09-13-native-hardening-design.md`(本文)
|
|
312
|
+
- `src/main/native/process-utils.js`、`src/main/native/redact.js`、
|
|
313
|
+
`src/main/native/secure-store.js`
|
|
314
|
+
- `src/main/native/rs/crates/secret/`(新 crate)
|
|
315
|
+
- `scripts/native-update-apply-test.cjs`、`scripts/native-job-object-test.cjs`、
|
|
316
|
+
`scripts/native-secret-test.cjs`
|
|
317
|
+
|
|
318
|
+
修改:
|
|
319
|
+
|
|
320
|
+
- `src/main/native/updater.js`、`src/main/native/launcher.js`、
|
|
321
|
+
`src/main/native/host.js`、`src/main/native/config.js`(nativePaths 增加 secret)
|
|
322
|
+
- `src/main/native/rs/crates/shim/src/main.rs`(Job Object、日志轮转、
|
|
323
|
+
HARNESS_MIX_HOST_SCRIPT)
|
|
324
|
+
- `src/main/host/runtime.js`(恢复标记)、各 adapter 的终止调用点
|
|
325
|
+
- `scripts/build-native.cjs`、`package.json`(files / 测试脚本)
|
|
326
|
+
- `docs/native-codex.md`
|
|
@@ -300,6 +300,9 @@ function installDraftPrewarmPolicyBridge(manager, bridge, hostId, target, prewar
|
|
|
300
300
|
let selectedCodexAccountId = null;
|
|
301
301
|
const isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
302
302
|
const isRemoteControlHost = hostId.startsWith("remote-control:");
|
|
303
|
+
if (isRemoteControlHost && typeof originalDispatchAppServerResponse !== "function") {
|
|
304
|
+
throw new Error("Renderer Remote Control approval response bridge is unavailable");
|
|
305
|
+
}
|
|
303
306
|
const knownExternalThreadIds = /* @__PURE__ */ new Set();
|
|
304
307
|
const knownOfficialThreadIds = /* @__PURE__ */ new Set();
|
|
305
308
|
const threadOwnershipResolutions = /* @__PURE__ */ new Map();
|
|
@@ -719,10 +722,10 @@ function installDraftPrewarmPolicyBridge(manager, bridge, hostId, target, prewar
|
|
|
719
722
|
}
|
|
720
723
|
if (response.id.startsWith(bridgeServerRequestIdPrefix)) return void 0;
|
|
721
724
|
}
|
|
722
|
-
return originalDispatchAppServerResponse
|
|
725
|
+
return originalDispatchAppServerResponse?.call(manager, method, response);
|
|
723
726
|
};
|
|
724
727
|
if (!observesWindowNotifications) manager.onNotification = routedOnNotification;
|
|
725
|
-
manager.dispatchAppServerResponse = routedDispatchAppServerResponse;
|
|
728
|
+
if (originalDispatchAppServerResponse) manager.dispatchAppServerResponse = routedDispatchAppServerResponse;
|
|
726
729
|
const policy = Object.freeze({
|
|
727
730
|
state: "ready",
|
|
728
731
|
hostId,
|
|
@@ -762,7 +765,7 @@ function installDraftPrewarmPolicyBridge(manager, bridge, hostId, target, prewar
|
|
|
762
765
|
} else if (manager.onNotification === routedOnNotification) {
|
|
763
766
|
manager.onNotification = originalOnNotification;
|
|
764
767
|
}
|
|
765
|
-
if (manager.dispatchAppServerResponse === routedDispatchAppServerResponse) {
|
|
768
|
+
if (originalDispatchAppServerResponse && manager.dispatchAppServerResponse === routedDispatchAppServerResponse) {
|
|
766
769
|
manager.dispatchAppServerResponse = originalDispatchAppServerResponse;
|
|
767
770
|
}
|
|
768
771
|
if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
|
|
@@ -812,6 +815,15 @@ function selectRendererRequestManager(candidates, activeHostIds) {
|
|
|
812
815
|
function isRecord2(value) {
|
|
813
816
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
814
817
|
}
|
|
818
|
+
function rendererRequestManagerFromHook(value) {
|
|
819
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
820
|
+
const hook = value;
|
|
821
|
+
const manager = "manager" in hook ? hook.manager : hook;
|
|
822
|
+
if (typeof manager !== "object" || manager === null) return null;
|
|
823
|
+
if (manager !== hook && (hook.status !== "ready" || typeof hook.hostId !== "string" || manager.getHostId?.() !== hook.hostId)) return null;
|
|
824
|
+
if (typeof manager.requestClient?.prewarmThreadStart !== "function" || typeof manager.requestClient?.sendRequest !== "function" || typeof manager.requestClient?.enqueueRequest !== "function" || typeof manager.prewarmedThreadManager?.discardAllPrewarmedThreads !== "function" || typeof manager.sendRequest !== "function") return null;
|
|
825
|
+
return manager;
|
|
826
|
+
}
|
|
815
827
|
var FIND_REQUEST_MANAGER_EXPRESSION = `(() => {
|
|
816
828
|
const editors = [...document.querySelectorAll(
|
|
817
829
|
'[data-codex-composer], [contenteditable="true"][role="textbox"]',
|
|
@@ -840,17 +852,8 @@ var FIND_REQUEST_MANAGER_EXPRESSION = `(() => {
|
|
|
840
852
|
}
|
|
841
853
|
let hook = fiber.memoizedState;
|
|
842
854
|
for (let index = 0; hook != null && index < 120; index += 1, hook = hook.next) {
|
|
843
|
-
const value = hook.memoizedState;
|
|
844
|
-
if (
|
|
845
|
-
value != null &&
|
|
846
|
-
typeof value === 'object' &&
|
|
847
|
-
value.requestClient != null &&
|
|
848
|
-
typeof value.requestClient.prewarmThreadStart === 'function' &&
|
|
849
|
-
typeof value.requestClient.sendRequest === 'function' &&
|
|
850
|
-
typeof value.requestClient.enqueueRequest === 'function' &&
|
|
851
|
-
typeof value.prewarmedThreadManager?.discardAllPrewarmedThreads === 'function' &&
|
|
852
|
-
typeof value.sendRequest === 'function'
|
|
853
|
-
) {
|
|
855
|
+
const value = (${rendererRequestManagerFromHook.toString()})(hook.memoizedState);
|
|
856
|
+
if (value != null) {
|
|
854
857
|
managers.add(value);
|
|
855
858
|
}
|
|
856
859
|
}
|
|
@@ -1357,7 +1360,10 @@ ${rendererSource}`,
|
|
|
1357
1360
|
"hermes",
|
|
1358
1361
|
"antigravity",
|
|
1359
1362
|
"qoder",
|
|
1360
|
-
"
|
|
1363
|
+
"codebuddy",
|
|
1364
|
+
"kiro-cli",
|
|
1365
|
+
"cursor-cli",
|
|
1366
|
+
"codex-harness",
|
|
1361
1367
|
"zcode",
|
|
1362
1368
|
"trae"
|
|
1363
1369
|
],
|
|
Binary file
|
|
Binary file
|