tianshu-mcp 0.3.2 → 0.3.4
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.en.md +36 -4
- package/CHANGELOG.md +36 -4
- package/README.en.md +24 -10
- package/README.md +22 -10
- package/dist/agents/adapter.d.ts +1 -1
- package/dist/agents/builtin.js +8 -4
- package/dist/agents/codex/run.js +2 -0
- package/dist/agents/traework/run.js +2 -0
- package/dist/agents/zcode/cdp.d.ts +14 -7
- package/dist/agents/zcode/cdp.js +39 -20
- package/dist/agents/zcode/dialog.d.ts +8 -2
- package/dist/agents/zcode/dialog.js +103 -45
- package/dist/agents/zcode/dom.d.ts +7 -0
- package/dist/agents/zcode/dom.js +76 -0
- package/dist/agents/zcode/instance.d.ts +6 -1
- package/dist/agents/zcode/instance.js +41 -12
- package/dist/agents/zcode/model.d.ts +5 -0
- package/dist/agents/zcode/model.js +27 -2
- package/dist/agents/zcode/project.d.ts +4 -0
- package/dist/agents/zcode/project.js +24 -0
- package/dist/agents/zcode/recovery.d.ts +31 -0
- package/dist/agents/zcode/recovery.js +107 -0
- package/dist/agents/zcode/run.d.ts +2 -2
- package/dist/agents/zcode/run.js +339 -120
- package/dist/agents/zcode/selectors.js +15 -18
- package/dist/config/schema.d.ts +69 -0
- package/dist/config/schema.js +28 -0
- package/dist/tasks/task.d.ts +1 -1
- package/dist/util/path.js +4 -5
- package/dist/verify/acceptance.d.ts +1 -0
- package/dist/verify/acceptance.js +69 -6
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +2 -1
- package/scripts/release-body.mjs +2 -1
package/CHANGELOG.en.md
CHANGED
|
@@ -19,9 +19,40 @@ Chinese version: [CHANGELOG.md](CHANGELOG.md)
|
|
|
19
19
|
- Best-effort stop of a GUI-side pending session (via a temporary CDP connection) when cancelling
|
|
20
20
|
a task in the `needs_user` state.
|
|
21
21
|
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## [0.3.
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## [0.3.4] — 2026-09-13
|
|
25
|
+
|
|
26
|
+
- Fix #8/#10 project-selector ambiguity, full-path binding false negatives and contaminated model readback.
|
|
27
|
+
- Fix #9 environment recovery without a session: send full task/context/references once and locate the session through its marker or unique delta.
|
|
28
|
+
- Add initialization recovery with shared deadlines, configurable budgets and a resumable `setup_recovery` state. Reconcile native side effects after timeouts and terminate helper processes on cancellation.
|
|
29
|
+
- Handle split model labels, hidden outgoing animation text, residual project menus trapping focus and send buttons that are not yet ready.
|
|
30
|
+
- Add real DOM and recovery/cancellation regressions. Windows hardware verifies cold first import, imported-project reuse and same-task recovery with actual artifacts and 2/2 acceptance checks.
|
|
31
|
+
- Release/tarball only; no npm publication. macOS has no real-device verification for this patch and the ZCode profile remains `research`.
|
|
32
|
+
|
|
33
|
+
See [release notes](docs/release-v0.3.4.en.md) and [validation evidence](docs/zcode-issue-8-10-validation.en.md).
|
|
34
|
+
|
|
35
|
+
## [0.3.3] — 2026-09-12
|
|
36
|
+
|
|
37
|
+
Fixes issues #4 / #7 by adapting the ZCode GUI driver to the 3.11.2 model-menu and project-binding semantics, while making acceptance fail closed for zero-test and zero-change outcomes.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- ZCode provider selectors now support both `group-provider` and the 3.11.2 `group-family` prefix. Model selection tries the visible model first and only expands a provider/family group as a fallback; failures include visible text and `data-testid` candidates.
|
|
42
|
+
- New-project import dismisses the stale workspace menu before opening Add Project and uses a bounded three-round dismiss/click/verify loop, preventing the first click from being consumed as an outside click.
|
|
43
|
+
- Project binding prioritizes the composer menu's `menuitemcheckbox`; the legacy sidebar item remains a compatibility fallback. Display-name matching normalizes NFKC, whitespace, and case.
|
|
44
|
+
- Binding read-back combines the composer trigger text with the full-path fast path, rejects known unbound placeholders, and retries the idempotent bind operation for up to two rounds.
|
|
45
|
+
- Built-in ZCode and TraeWork discovery paths use `{PROGRAMFILES}` / `{PROGRAMFILES(X86)}`. Environment placeholders in custom profiles are expanded case-insensitively while unknown placeholders remain unchanged.
|
|
46
|
+
- A mandatory test check is failed when it exits with code 0 but its output reports zero executed tests.
|
|
47
|
+
- Git projects now require a change relative to the pre-work baseline by default. Pure question/analysis tasks can explicitly opt out with `"requireChanges": false` in `.tianshu-mcp/acceptance.json`.
|
|
48
|
+
|
|
49
|
+
### Tests
|
|
50
|
+
|
|
51
|
+
- Regression coverage now models ZCode 3.11.2 flat family layouts, legacy provider fallback, testid diagnostics, swallowed project clicks, composer binding and read-back retries, plus zero-test/zero-change acceptance gates.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## [0.3.2] — 2026-09-12
|
|
25
56
|
|
|
26
57
|
Fixes for issues #5 / #6: **the MCP task model was disconnected from the state of the turn inside
|
|
27
58
|
the Codex GUI**. The former is the completion-detection deadlock that kept reporting `running`
|
|
@@ -494,7 +525,8 @@ project → pick model and reasoning level → send instructions → run detecti
|
|
|
494
525
|
|
|
495
526
|
---
|
|
496
527
|
|
|
497
|
-
[Unreleased]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.
|
|
528
|
+
[Unreleased]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.3...HEAD
|
|
529
|
+
[0.3.3]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.2...v0.3.3
|
|
498
530
|
[0.3.2]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.1...v0.3.2
|
|
499
531
|
[0.3.1]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.0...v0.3.1
|
|
500
532
|
[0.3.0]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.2.0...v0.3.0
|
package/CHANGELOG.md
CHANGED
|
@@ -17,9 +17,40 @@
|
|
|
17
17
|
- Codex GUI 的 macOS 真机验证(当前内置状态为 `research`)。
|
|
18
18
|
- needs_user 状态下取消任务时经临时 CDP 连接尽力停止 GUI 内等待中的会话。
|
|
19
19
|
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## [0.3.
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [0.3.4] — 2026-09-13
|
|
23
|
+
|
|
24
|
+
- 修复 #8/#10 的项目选择器歧义、完整路径绑定误判和模型混合文本回读。
|
|
25
|
+
- 修复 #9 的无会话环境恢复:完整任务、上下文和引用只发送一次,使用任务标记或唯一会话差集定位。
|
|
26
|
+
- 增加共用截止时间的初始化恢复、可配置预算及 `setup_recovery` 暂停;原生操作超时先复检副作用,取消回收辅助进程。
|
|
27
|
+
- 修复拆分模型标签、隐藏动画旧值、项目菜单焦点截获和发送按钮尚未就绪的问题。
|
|
28
|
+
- 补充真实 DOM、取消及恢复回归,Windows 真机覆盖新项目冷导入、已导入项目复用和同任务恢复,实际产物与 2/2 验收通过。
|
|
29
|
+
- 本版本发布 Release/tarball,不发布 npm;macOS 尚无本次真机验证,ZCode profile 保持 `research`。
|
|
30
|
+
|
|
31
|
+
详见 [发布说明](docs/release-v0.3.4.md) 和 [验收证据](docs/zcode-issue-8-10-validation.md)。
|
|
32
|
+
|
|
33
|
+
## [0.3.3] — 2026-09-12
|
|
34
|
+
|
|
35
|
+
修复 issue #4 / #7:适配 ZCode 3.11.2 的模型菜单与项目绑定语义,并将验收引擎收紧为 fail-closed,防止零用例、零变更任务假绿。
|
|
36
|
+
|
|
37
|
+
### 修复
|
|
38
|
+
|
|
39
|
+
- ZCode 供应商分组选择器同时兼容 `group-provider` 与 3.11.2 `group-family`;模型流程改为直选优先、供应商分组展开兜底,并在失败信息中附带可见文本与 `data-testid` 候选。
|
|
40
|
+
- 添加新项目前收起残留工作区菜单,以最多三轮“收起—点击—验证”闭环消除首次点击被吞。
|
|
41
|
+
- 项目选择优先点击 composer 菜单的 `menuitemcheckbox`,旧版侧栏项仅作回退;项目名称按 NFKC、空白和大小写归一化。
|
|
42
|
+
- 项目绑定回读同时校验 composer 触发器文本与完整路径,未绑定占位词不再误判;绑定失败最多两轮幂等重试。
|
|
43
|
+
- ZCode/TraeWork 内置发现目录统一为 `{PROGRAMFILES}` / `{PROGRAMFILES(X86)}`;自定义 profile 的环境占位符按大小写不敏感方式展开,未知占位符保留原样。
|
|
44
|
+
- 非 optional 测试检查在退出码为 0 但输出显示零用例时改判失败。
|
|
45
|
+
- Git 项目默认要求相对动工前基线产生变更;纯问答/分析任务可在 `.tianshu-mcp/acceptance.json` 设置 `"requireChanges": false` 显式关闭。
|
|
46
|
+
|
|
47
|
+
### 测试
|
|
48
|
+
|
|
49
|
+
- 回归覆盖 ZCode 3.11.2 family 平铺模型、旧版分组兜底、testid 诊断、项目点击被吞、composer 绑定与回读重试,以及零用例/零变更门禁。
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## [0.3.2] — 2026-09-12
|
|
23
54
|
|
|
24
55
|
修复 issue #5 / #6:**MCP 任务模型与 Codex GUI 内 turn 的状态脱节**。
|
|
25
56
|
前者是"等用户确认时恒报 running"的完成判定死锁,后者是 `cancel_task` 只停 MCP 侧等待、
|
|
@@ -431,7 +462,8 @@ Codex 桌面端改为 **GUI 驱动**:新增 `codex-gui` adapter,通过 MSIX
|
|
|
431
462
|
|
|
432
463
|
---
|
|
433
464
|
|
|
434
|
-
[未发布]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.
|
|
465
|
+
[未发布]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.3...HEAD
|
|
466
|
+
[0.3.3]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.2...v0.3.3
|
|
435
467
|
[0.3.2]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.1...v0.3.2
|
|
436
468
|
[0.3.1]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.3.0...v0.3.1
|
|
437
469
|
[0.3.0]: https://github.com/lanlan0811/tianshu-mcp/compare/v0.2.0...v0.3.0
|
package/README.en.md
CHANGED
|
@@ -36,7 +36,7 @@ Tianshu plays the role of the overall commander; this MCP server is the **schedu
|
|
|
36
36
|
|
|
37
37
|
- **9 MCP tools**: `run_task / continue_task / query_task / list_tasks / get_task_report / cancel_task / verify_task / rework_task / get_profiles`.
|
|
38
38
|
- **Async contract**: `run_task` returns a `taskId` immediately; long-running work is polled via `query_task` (never blocks `tools/call`).
|
|
39
|
-
- **Objective acceptance**: automated command checks (typecheck/lint/test/build — skipped when absent, plus tech-stack derivation) + programmatic code analysis (changed-file list / diffstat / suspicious signals such as TODO, debugger, secret-like patterns), all relative to a **git baseline**; never auto-commits or stashes.
|
|
39
|
+
- **Objective acceptance**: automated command checks (typecheck/lint/test/build — skipped when absent, plus tech-stack derivation) + programmatic code analysis (changed-file list / diffstat / suspicious signals such as TODO, debugger, secret-like patterns), all relative to a **git baseline**; never auto-commits or stashes. The acceptance engine is **fail-closed**: a test check fails when its output reports zero executed tests even if the exit code is 0; git projects must produce changes relative to the pre-work baseline by default (pure analysis tasks can opt out with `"requireChanges": false` in `.tianshu-mcp/acceptance.json`).
|
|
40
40
|
- **Rework loop**: automatic rework (`autoFixRounds`) + manual `rework_task`; on verification failure a repair-plan file is generated and fed back to the agent; when rounds run out → `needs_attention` awaiting Tianshu's verdict.
|
|
41
41
|
- **Execution surfaces**: `driver: "gui"` selects an explicit, isolated Codex/TraeWork/ZCode CDP adapter; `driver: "spawn"` runs an external CLI child process.
|
|
42
42
|
- **Scheduling discipline**: per-project serial queue + global concurrency cap (default 2, configurable).
|
|
@@ -63,7 +63,7 @@ git clone https://github.com/lanlan0811/tianshu-mcp.git
|
|
|
63
63
|
cd tianshu-mcp
|
|
64
64
|
npm ci
|
|
65
65
|
npm run build # sync-version + tsc → dist/
|
|
66
|
-
npm test #
|
|
66
|
+
npm test # 366 tests across 39 files, including Codex/ZCode unit/fake-CDP/restart/repair coverage
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Install the npm package
|
|
@@ -122,6 +122,11 @@ run_task(projectPath=D:/xxx/my-app, task=「…task brief…」, agentId=codex,
|
|
|
122
122
|
> COM activation with a dedicated `--user-data-dir` before CDP can drive it. `planDoc` / `designSystem` are
|
|
123
123
|
> appended to the initial instruction. See [docs/codex-gui-cdp.en.md](docs/codex-gui-cdp.en.md) and the
|
|
124
124
|
> [hardware acceptance record](docs/codex-windows-smoke.en.md). Projects not yet registered on the Codex side are **registered automatically** — no manual project creation needed.
|
|
125
|
+
> When Codex parks on a "waiting for user confirmation" screen (plan-confirmation card, subscription checkout, etc.),
|
|
126
|
+
> the task turns `needs_user(user_confirmation)`; after handling it in the Codex window call `continue_task(taskId)`
|
|
127
|
+
> to resume observation. `cancel_task` clicks the in-app stop control over CDP and bounded-waits for the GUI to go
|
|
128
|
+
> idle; before dispatching, a still-running managed instance is stopped best-effort, and if it never goes idle the
|
|
129
|
+
> dispatch is rejected with `instance_busy`.
|
|
125
130
|
|
|
126
131
|
When driving TraeWork, `model` and `mode` are available:
|
|
127
132
|
|
|
@@ -140,18 +145,18 @@ run_task(projectPath=D:/xxx/my-app, agentId=zcode, task="Implement `./plan.md`",
|
|
|
140
145
|
model=DeepSeek/deepseek-flash, autoVerify=true)
|
|
141
146
|
```
|
|
142
147
|
|
|
143
|
-
Questions, login, an existing non-CDP instance, or system permission pause as `needs_user`; call `continue_task(taskId, message)` to resume the recorded session. See [docs/zcode-cdp.en.md](docs/zcode-cdp.en.md).
|
|
148
|
+
Questions, login, an existing non-CDP instance, or system permission pause as `needs_user`; call `continue_task(taskId, message)` to resume the recorded session. Model selection is adapted to ZCode 3.11.2: flat models are selected directly first, with provider/family group expansion as a fallback — both new and legacy layouts are supported. See [docs/zcode-cdp.en.md](docs/zcode-cdp.en.md).
|
|
144
149
|
|
|
145
150
|
## Tool surface (9 tools)
|
|
146
151
|
|
|
147
152
|
| Tool | Capability / approval | Purpose |
|
|
148
153
|
|---|---|---|
|
|
149
154
|
| `run_task` | write + approval | Dispatch work (optional auto-verify / auto-rework); returns `taskId` asynchronously |
|
|
150
|
-
| `continue_task` | write + approval | Resume the
|
|
155
|
+
| `continue_task` | write + approval | Resume the session behind `needs_user` (ZCode resumes the recorded session; Codex re-observes for `user_confirmation` / re-dispatches for `login_required`) |
|
|
151
156
|
| `query_task` | read | Poll status / progress / log tail |
|
|
152
157
|
| `list_tasks` | read | Filtered history of tasks |
|
|
153
158
|
| `get_task_report` | read | Full text of a verification round's report (`report.md`) |
|
|
154
|
-
| `cancel_task` | write + approval | Cancel a running task
|
|
159
|
+
| `cancel_task` | write + approval | Cancel a running task: CLI agents kill the process tree; GUI agents click the in-app stop control over CDP and bounded-wait (`gui.cancelWaitMs`, default 15s) for the GUI to go idle, stating so explicitly in the final message when the stop is unconfirmed |
|
|
155
160
|
| `verify_task` | read | Run one verification pass on a task/project path (no source changes) |
|
|
156
161
|
| `rework_task` | write + approval | Manual rework (feed the failure report back to the same agent) |
|
|
157
162
|
| `get_profiles` | read | Inspect agent adapters and executable discovery results |
|
|
@@ -182,6 +187,8 @@ Use `server.log` when troubleshooting connections; do not treat stderr output it
|
|
|
182
187
|
| [docs/zcode-windows-smoke.en.md](docs/zcode-windows-smoke.en.md) | ZCode Windows hardware record for development, same-session repair, and question continuation |
|
|
183
188
|
| [docs/codex-gui-cdp.en.md](docs/codex-gui-cdp.en.md) | Codex desktop GUI driver: MSIX COM activation, CDP attach, selectors, run detection, verify/repair |
|
|
184
189
|
| [docs/codex-windows-smoke.en.md](docs/codex-windows-smoke.en.md) | Codex Windows hardware record (incl. verify-fail → auto plan → repair-pass loop) |
|
|
190
|
+
| [docs/release-v0.3.3.en.md](docs/release-v0.3.3.en.md) | v0.3.3 release notes (ZCode 3.11.2 adaptation + fail-closed acceptance engine) |
|
|
191
|
+
| [docs/release-v0.3.2.en.md](docs/release-v0.3.2.en.md) | v0.3.2 release notes (Codex wait-user detection + cancel truly stops the GUI) |
|
|
185
192
|
| [docs/release-v0.3.1.en.md](docs/release-v0.3.1.en.md) | v0.3.1 release notes (skill docs rewrite + release automation fixes) |
|
|
186
193
|
| [docs/release-v0.3.0.en.md](docs/release-v0.3.0.en.md) | v0.3.0 release notes (Codex desktop GUI adapter, incl. BREAKING) |
|
|
187
194
|
| [docs/release-v0.2.0.en.md](docs/release-v0.2.0.en.md) | v0.2.0 release notes (unified ZCode GUI loop) |
|
|
@@ -258,14 +265,21 @@ Use `server.log` when troubleshooting connections; do not treat stderr output it
|
|
|
258
265
|
- **M13 — Skill docs alignment + release automation fixes + v0.3.1** (2026-09-12, see [release-v0.3.1.en.md](docs/release-v0.3.1.en.md))
|
|
259
266
|
- Self-installed skill docs (SKILL.md / usage-examples.md) rewritten item by item against the v0.3.0 tool surface (codex GUI params, needs_user handling, verify/list usage)
|
|
260
267
|
- Bilingual release bodies, Full Changelog & CI link fixes, and automated Gitee releases
|
|
268
|
+
- **M14 — Codex wait-user detection + cancel truly stops the GUI + v0.3.2** (2026-09-12, fixes issues #5 / #6, see [release-v0.3.2.en.md](docs/release-v0.3.2.en.md))
|
|
269
|
+
- Issue #5: Codex parked on a "waiting for user confirmation" screen no longer dead-locks in `running` — with the stop button visible and the conversation hash unchanged for `gui.stallTimeoutMs` (default 5 min), the task turns `needs_user(user_confirmation)`; added a configurable `gui.selectors.userGate` UI gate; `continue_task` now supports codex (`user_confirmation` re-observes / `login_required` re-dispatches)
|
|
270
|
+
- Issue #6: `cancel_task` for GUI agents clicks the in-app stop control over CDP and bounded-waits (`gui.cancelWaitMs`, default 15s) for the GUI to go idle before recording `cancelled`; dispatch-time liveness checks reject with `instance_busy` when a managed instance is still running, preventing overlapping turns
|
|
271
|
+
- **M15 — ZCode 3.11.2 adaptation + fail-closed acceptance engine + v0.3.3** (2026-09-12, fixes issues #4 / #7, see [release-v0.3.3.en.md](docs/release-v0.3.3.en.md)) — **366 tests**
|
|
272
|
+
- Issue #4: the model menu supports both `group-provider` and 3.11.2 `group-family` groups, selecting flat models directly first with group expansion as a fallback; project binding now keys on the composer's `menuitemcheckbox` with read-back checking trigger text + full path and up to two idempotent retries; stale workspace menus are dismissed before adding a project
|
|
273
|
+
- Issue #7: a test check that exits 0 but reports zero executed tests fails; git projects must produce changes relative to the pre-work baseline by default (opt out with `requireChanges: false`) — zero tests and zero changes no longer pass silently
|
|
274
|
+
- `{PROGRAMFILES}` placeholders normalized to uppercase and environment-variable expansion made case-insensitive
|
|
261
275
|
|
|
262
276
|
## Agent support status
|
|
263
277
|
|
|
264
|
-
| agentId | driver | status | Notes |
|
|
278
|
+
| agentId | driver / adapter | status | Notes |
|
|
265
279
|
|---|---|---|---|
|
|
266
|
-
| `codex` | `gui` | **ready** | Desktop GUI over CDP (MSIX COM activation + dedicated profile); supports `model`/`reasoningLevel`/`planDoc`/`designSystem`; Windows machine-verified |
|
|
267
|
-
| `zcode` | `zcode-gui` | **research** | CDP GUI adapter
|
|
268
|
-
| `traework` |
|
|
280
|
+
| `codex` | `gui` / `codex-gui` | **ready** | Desktop GUI over CDP (MSIX COM activation + dedicated profile); supports `model`/`reasoningLevel`/`planDoc`/`designSystem`; wait-user, cancel and dispatch-guard semantics machine-verified (v0.3.2); Windows machine-verified |
|
|
281
|
+
| `zcode` | `gui` / `zcode-gui` | **research** | CDP GUI adapter with the Windows hardware loop passed; adapted to ZCode 3.11.2 model menu and project binding (v0.3.3); remains non-ready until macOS hardware passes |
|
|
282
|
+
| `traework` | `gui` / `traework-gui` | **ready** | CDP-driven TRAE SOLO CN desktop UI; all three panel modes machine-verified |
|
|
269
283
|
| `stub` | `spawn` | tests only | `test/stub-agent/stub-agent.mjs` with 3 playbooks (good/fix-on-first/never) |
|
|
270
284
|
|
|
271
285
|
> Adding an agent usually needs only a profile — see [docs/agent-profiles.en.md](docs/agent-profiles.en.md) and [CONTRIBUTING.en.md](CONTRIBUTING.en.md).
|
|
@@ -280,7 +294,7 @@ Use `server.log` when troubleshooting connections; do not treat stderr output it
|
|
|
280
294
|
|
|
281
295
|
| Document | Content |
|
|
282
296
|
|---|---|
|
|
283
|
-
| [CHANGELOG.en.md](CHANGELOG.en.md) | Version history (v0.1.0 → v0.3.
|
|
297
|
+
| [CHANGELOG.en.md](CHANGELOG.en.md) | Version history (v0.1.0 → v0.3.3) |
|
|
284
298
|
| [CONTRIBUTING.en.md](CONTRIBUTING.en.md) | Dev setup, conventions, commit/release flow, adding an agent |
|
|
285
299
|
| [SECURITY.en.md](SECURITY.en.md) | Security model (zero credentials / command whitelist / process & desktop-automation boundaries) and private reporting |
|
|
286
300
|
| [CODE_OF_CONDUCT.en.md](CODE_OF_CONDUCT.en.md) | Contributor Code of Conduct |
|
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
- **9 个 MCP 工具**:`run_task / continue_task / query_task / list_tasks / get_task_report / cancel_task / verify_task / rework_task / get_profiles`。
|
|
38
38
|
- **异步契约**:`run_task` 秒回 `taskId`,长任务用 `query_task` 轮询(长任务不卡 `tools/call`)。
|
|
39
|
-
- **客观验收**:自动命令检查(typecheck/lint/test/build,缺则跳过 + 技术栈推导)+ 程序化代码分析(变更清单/diffstat/TODO·debugger·密钥形态等可疑标记),全部相对 **git 基线**,不自动 commit/stash
|
|
39
|
+
- **客观验收**:自动命令检查(typecheck/lint/test/build,缺则跳过 + 技术栈推导)+ 程序化代码分析(变更清单/diffstat/TODO·debugger·密钥形态等可疑标记),全部相对 **git 基线**,不自动 commit/stash。验收引擎 **fail-closed**:测试命令退出码为 0 但输出显示零用例时判失败;git 项目默认要求相对动工前基线产生变更(纯分析任务可在 `.tianshu-mcp/acceptance.json` 设 `"requireChanges": false` 显式关闭)。
|
|
40
40
|
- **失败返修闭环**:自动返修(`autoFixRounds`)+ 手动 `rework_task`;验收失败时自动生成修复计划文件并回填给 agent;轮次用尽 → `needs_attention` 等天枢裁决。
|
|
41
41
|
- **执行面**:`driver: "gui"` 由显式 adapter 驱动桌面 UI(Codex / TraeWork / ZCode 各自使用隔离的 CDP 流程);`driver: "spawn"` 走外部 CLI 子进程。
|
|
42
42
|
- **调度纪律**:每项目串行队列 + 全局并发上限(默认 2,可配)。
|
|
@@ -63,7 +63,7 @@ git clone https://github.com/lanlan0811/tianshu-mcp.git
|
|
|
63
63
|
cd tianshu-mcp
|
|
64
64
|
npm ci
|
|
65
65
|
npm run build # sync-version + tsc → dist/
|
|
66
|
-
npm test #
|
|
66
|
+
npm test # 366 项测试:39 个文件,含 Codex/ZCode 单元/假 CDP/重启/返修闭环
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### 安装 npm 包
|
|
@@ -120,6 +120,9 @@ run_task(projectPath=D:/xxx/my-app, task=「…任务书…」, agentId=codex,
|
|
|
120
120
|
> 其 `ChatGPT.exe` 无法直接启动(被策略拒绝),须经 COM 激活并注入专属 `--user-data-dir` 后方可
|
|
121
121
|
> 用 CDP 驱动。可传 `planDoc` / `designSystem` 拼进初始指令。详见 [docs/codex-gui-cdp.md](docs/codex-gui-cdp.md)
|
|
122
122
|
> 与 [真机验收记录](docs/codex-windows-smoke.md)。项目未在 Codex 侧登记时会**自动登记**,无需手动建项目。
|
|
123
|
+
> Codex 停在「等待用户确认」界面(方案确认卡/订阅结账页等)会转 `needs_user(user_confirmation)`,
|
|
124
|
+
> 用户在 Codex 窗口处理完后调 `continue_task(taskId)` 恢复观察;`cancel_task` 会经 CDP 点击停止并
|
|
125
|
+
> 有界等待 GUI 空闲,派发前若受管实例仍在运行会先尽力停止,仍不空闲则以 `instance_busy` 拒绝派发。
|
|
123
126
|
|
|
124
127
|
驱动 TraeWork 时可用 `model` 与 `mode`:
|
|
125
128
|
|
|
@@ -138,18 +141,18 @@ run_task(projectPath=D:/xxx/my-app, agentId=zcode, task=「按 `./plan.md` 完
|
|
|
138
141
|
model=DeepSeek/deepseek-flash, autoVerify=true)
|
|
139
142
|
```
|
|
140
143
|
|
|
141
|
-
ZCode 提问或需要用户处理登录、旧实例、系统权限时进入 `needs_user`;处理后调用 `continue_task(taskId, message)`
|
|
144
|
+
ZCode 提问或需要用户处理登录、旧实例、系统权限时进入 `needs_user`;处理后调用 `continue_task(taskId, message)` 恢复原会话。模型选择已适配 ZCode 3.11.2:直选平铺模型优先,展开 provider/family 分组兜底,新旧布局均兼容。完整约束见 [docs/zcode-cdp.md](docs/zcode-cdp.md)。
|
|
142
145
|
|
|
143
146
|
## 工具面(9 个)
|
|
144
147
|
|
|
145
148
|
| 工具 | 能力 / 审批 | 作用 |
|
|
146
149
|
|---|---|---|
|
|
147
150
|
| `run_task` | write + 审批 | 派活(可带自动验收/自动返修),异步返回 `taskId` |
|
|
148
|
-
| `continue_task` | write + 审批 | 恢复 `needs_user`
|
|
151
|
+
| `continue_task` | write + 审批 | 恢复 `needs_user` 的原会话(ZCode 恢复原会话;Codex 按 `user_confirmation` 重新观察 / `login_required` 重派) |
|
|
149
152
|
| `query_task` | read | 轮询状态 / 进度 / 日志尾 |
|
|
150
153
|
| `list_tasks` | read | 历史任务过滤列表 |
|
|
151
154
|
| `get_task_report` | read | 某轮验收报告全文(`report.md`) |
|
|
152
|
-
| `cancel_task` | write + 审批 |
|
|
155
|
+
| `cancel_task` | write + 审批 | 取消运行中任务:CLI agent kill 进程树;GUI agent 经 CDP 点击停止并在 `gui.cancelWaitMs`(默认 15s)内有界等待 GUI 空闲,未确认停止时终态明示 |
|
|
153
156
|
| `verify_task` | read | 对任务/项目路径做一次验收(不改源码) |
|
|
154
157
|
| `rework_task` | write + 审批 | 手动返修(把失败报告喂回同一 agent) |
|
|
155
158
|
| `get_profiles` | read | 查看 agent 适配与可执行探测结果 |
|
|
@@ -180,6 +183,8 @@ ZCode 提问或需要用户处理登录、旧实例、系统权限时进入 `nee
|
|
|
180
183
|
| [docs/zcode-windows-smoke.md](docs/zcode-windows-smoke.md) | ZCode Windows 真机开发、同会话返修与提问续跑验收记录 |
|
|
181
184
|
| [docs/codex-gui-cdp.md](docs/codex-gui-cdp.md) | Codex 桌面端 GUI 驱动:MSIX COM 激活、CDP 接管、选择器、运行检测、验收返修 |
|
|
182
185
|
| [docs/codex-windows-smoke.md](docs/codex-windows-smoke.md) | Codex Windows 真机验收记录(含验收失败→自动生成计划→返修通过闭环) |
|
|
186
|
+
| [docs/release-v0.3.3.md](docs/release-v0.3.3.md) | v0.3.3 发布说明(ZCode 3.11.2 适配 + 验收引擎 fail-closed) |
|
|
187
|
+
| [docs/release-v0.3.2.md](docs/release-v0.3.2.md) | v0.3.2 发布说明(Codex 等待用户检测 + cancel 真停 GUI) |
|
|
183
188
|
| [docs/release-v0.3.1.md](docs/release-v0.3.1.md) | v0.3.1 发布说明(技能文档重写 + 发布自动化修复) |
|
|
184
189
|
| [docs/release-v0.3.0.md](docs/release-v0.3.0.md) | v0.3.0 发布说明(Codex 桌面端 GUI 适配,含 BREAKING) |
|
|
185
190
|
| [docs/release-v0.2.0.md](docs/release-v0.2.0.md) | v0.2.0 发布说明(ZCode GUI 统一闭环) |
|
|
@@ -258,14 +263,21 @@ ZCode 提问或需要用户处理登录、旧实例、系统权限时进入 `nee
|
|
|
258
263
|
- **M13 — 技能文档对齐 + 发布自动化修复 + v0.3.1**(2026-09-12,见 [release-v0.3.1.md](docs/release-v0.3.1.md))
|
|
259
264
|
- 技能自检安装文档(SKILL.md / usage-examples.md)对照 v0.3.0 工具面逐项重写(codex GUI 参数、needs_user 处理、verify/list 用法)
|
|
260
265
|
- Release 正文双语合成、Full Changelog 与 CI 链接修复、Gitee 发行版纳入自动化
|
|
266
|
+
- **M14 — Codex 等待用户检测 + 取消真停 GUI + v0.3.2**(2026-09-12,修复 issue #5 / #6,见 [release-v0.3.2.md](docs/release-v0.3.2.md))
|
|
267
|
+
- issue #5:Codex 停在「等待用户确认」界面不再死锁在 `running`——停止按钮可见且对话哈希 `gui.stallTimeoutMs`(默认 5 分钟)不变 → 转 `needs_user(user_confirmation)`;新增可配置 `gui.selectors.userGate` 界面检测;`continue_task` 扩展支持 codex(`user_confirmation` 重新观察 / `login_required` 重派)
|
|
268
|
+
- issue #6:`cancel_task` 对 GUI agent 经 CDP 尽力点击停止并在 `gui.cancelWaitMs`(默认 15s)内有界等待 GUI 空闲后才落 `cancelled`;派发前检测受管实例运行态,仍运行则以 `instance_busy` 拒绝,杜绝新旧 turn 交叠
|
|
269
|
+
- **M15 — ZCode 3.11.2 适配 + 验收引擎 fail-closed + v0.3.3**(2026-09-12,修复 issue #4 / #7,见 [release-v0.3.3.md](docs/release-v0.3.3.md))— **366 测试**
|
|
270
|
+
- issue #4:模型菜单同时兼容 `group-provider` 与 3.11.2 `group-family` 分组,直选平铺模型优先、分组展开兜底;项目绑定改以 composer 复选项为主判据,回读校验触发器文本 + 完整路径,失败最多两轮幂等重试;添加项目前先收起残留菜单并重试
|
|
271
|
+
- issue #7:测试检查退出码 0 但输出零用例时改判失败;git 项目默认要求相对基线产生变更(`requireChanges: false` 可显式关闭),零用例与零变更不再假绿
|
|
272
|
+
- `{PROGRAMFILES}` 占位符统一大写且环境变量展开大小写不敏感
|
|
261
273
|
|
|
262
274
|
## Agent 适配现状
|
|
263
275
|
|
|
264
|
-
| agentId | driver | status | 说明 |
|
|
276
|
+
| agentId | driver / adapter | status | 说明 |
|
|
265
277
|
|---|---|---|---|
|
|
266
|
-
| `codex` |
|
|
267
|
-
| `zcode` | `zcode-gui` | **research** | CDP GUI adapter 已实现且 Windows
|
|
268
|
-
| `traework` |
|
|
278
|
+
| `codex` | `gui` / `codex-gui` | **ready** | Codex 桌面端 GUI(MSIX COM 激活 + CDP);支持 `model`/`reasoningLevel`/`planDoc`/`designSystem`;等待用户确认、取消与重派护栏均已真机验证(v0.3.2);Windows 真机已验证 |
|
|
279
|
+
| `zcode` | `gui` / `zcode-gui` | **research** | CDP GUI adapter 已实现且 Windows 真机闭环通过;已适配 ZCode 3.11.2 模型菜单与项目绑定(v0.3.3);macOS 真机完成前不标 `ready` |
|
|
280
|
+
| `traework` | `gui` / `traework-gui` | **ready** | CDP 驱动 TRAE SOLO CN 桌面 UI;三种面板模式真机验证通过 |
|
|
269
281
|
| `stub` | `spawn` | 仅测试 | `test/stub-agent/stub-agent.mjs` 三剧本(good/fix-on-first/never) |
|
|
270
282
|
|
|
271
283
|
> 新增 agent 通常只需加一个 profile,详见 [docs/agent-profiles.md](docs/agent-profiles.md) 与 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
@@ -281,7 +293,7 @@ ZCode 提问或需要用户处理登录、旧实例、系统权限时进入 `nee
|
|
|
281
293
|
| 文档 | 内容 |
|
|
282
294
|
|---|---|
|
|
283
295
|
| [HANDOFF.md](HANDOFF.md) | 项目交接文档:当前状态快照、架构导览、硬性红线、已知限制、接手建议 |
|
|
284
|
-
| [CHANGELOG.md](CHANGELOG.md) | 版本变更日志(v0.1.0 → v0.3.
|
|
296
|
+
| [CHANGELOG.md](CHANGELOG.md) | 版本变更日志(v0.1.0 → v0.3.3) |
|
|
285
297
|
| [CONTRIBUTING.md](CONTRIBUTING.md) | 开发环境、工程规范、提交与发布流程、如何新增 agent |
|
|
286
298
|
| [SECURITY.md](SECURITY.md) | 安全模型(凭证零管理/命令白名单/进程与桌面自动化边界)与私密报告渠道 |
|
|
287
299
|
| [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) | 贡献者行为准则 |
|
package/dist/agents/adapter.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export interface AgentRunResult {
|
|
|
80
80
|
endReason?: string;
|
|
81
81
|
/** GUI 实例是否因任务未真正完成而被保留 */
|
|
82
82
|
keptInstance?: boolean;
|
|
83
|
-
needsUserKind?: "agent_question" | "close_existing_instance" | "login_required" | "system_permission" | "user_confirmation";
|
|
83
|
+
needsUserKind?: "agent_question" | "close_existing_instance" | "login_required" | "system_permission" | "setup_recovery" | "user_confirmation";
|
|
84
84
|
pendingQuestion?: string;
|
|
85
85
|
/**
|
|
86
86
|
* 取消路径的 GUI 侧停止结果(GUI agent 专用):
|
package/dist/agents/builtin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ZCODE_SETUP_DEFAULTS } from "../config/schema.js";
|
|
1
2
|
export const BUILTIN_PROFILES = {
|
|
2
3
|
codex: {
|
|
3
4
|
displayName: "Codex (ChatGPT 桌面端 GUI)",
|
|
@@ -27,6 +28,7 @@ export const BUILTIN_PROFILES = {
|
|
|
27
28
|
scanPattern: "OpenAI.Codex_*_x64__*/app/ChatGPT.exe",
|
|
28
29
|
},
|
|
29
30
|
gui: {
|
|
31
|
+
...ZCODE_SETUP_DEFAULTS,
|
|
30
32
|
cdpPort: 9333,
|
|
31
33
|
cdpPortAuto: true,
|
|
32
34
|
cdpPortRange: 30,
|
|
@@ -73,8 +75,8 @@ export const BUILTIN_PROFILES = {
|
|
|
73
75
|
authNote: "复用本机 ZCode 登录态",
|
|
74
76
|
executableDiscovery: {
|
|
75
77
|
dirs: [
|
|
76
|
-
"{
|
|
77
|
-
"{
|
|
78
|
+
"{PROGRAMFILES}/ZCode",
|
|
79
|
+
"{PROGRAMFILES(X86)}/ZCode",
|
|
78
80
|
"{LOCALAPPDATA}/Programs/ZCode",
|
|
79
81
|
"{LOCALAPPDATA}/ZCode",
|
|
80
82
|
"/Applications/ZCode.app/Contents/MacOS",
|
|
@@ -87,6 +89,7 @@ export const BUILTIN_PROFILES = {
|
|
|
87
89
|
scanRoots: [],
|
|
88
90
|
},
|
|
89
91
|
gui: {
|
|
92
|
+
...ZCODE_SETUP_DEFAULTS,
|
|
90
93
|
cdpPort: 9333,
|
|
91
94
|
cdpPortAuto: true,
|
|
92
95
|
cdpPortRange: 30,
|
|
@@ -129,8 +132,8 @@ export const BUILTIN_PROFILES = {
|
|
|
129
132
|
executableDiscovery: {
|
|
130
133
|
dirs: [
|
|
131
134
|
"D:/TRAE Work CN",
|
|
132
|
-
"{
|
|
133
|
-
"{
|
|
135
|
+
"{PROGRAMFILES}/TRAE WORK CN",
|
|
136
|
+
"{PROGRAMFILES(X86)}/TRAE WORK CN",
|
|
134
137
|
"{LOCALAPPDATA}/Programs/TRAE WORK CN",
|
|
135
138
|
"{LOCALAPPDATA}/TRAE WORK CN",
|
|
136
139
|
"{APPDATA}/TRAE SOLO CN",
|
|
@@ -145,6 +148,7 @@ export const BUILTIN_PROFILES = {
|
|
|
145
148
|
scanRoots: [],
|
|
146
149
|
},
|
|
147
150
|
gui: {
|
|
151
|
+
...ZCODE_SETUP_DEFAULTS,
|
|
148
152
|
cdpPort: 9222,
|
|
149
153
|
cdpPortAuto: true,
|
|
150
154
|
cdpPortRange: 20,
|
package/dist/agents/codex/run.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ZCODE_SETUP_DEFAULTS } from "../../config/schema.js";
|
|
1
2
|
/**
|
|
2
3
|
* Codex GUI 任务编排(开发计划 §4,对应需求 9 步)。
|
|
3
4
|
*
|
|
@@ -41,6 +42,7 @@ const DEFAULT_DEPS = {
|
|
|
41
42
|
export function codexGuiOf(resolved) {
|
|
42
43
|
const g = resolved.profile.gui;
|
|
43
44
|
return {
|
|
45
|
+
...ZCODE_SETUP_DEFAULTS,
|
|
44
46
|
cdpPort: g?.cdpPort ?? 9333,
|
|
45
47
|
cdpPortAuto: g?.cdpPortAuto ?? true,
|
|
46
48
|
cdpPortRange: g?.cdpPortRange ?? 30,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ZCODE_SETUP_DEFAULTS } from "../../config/schema.js";
|
|
1
2
|
/**
|
|
2
3
|
* TraeWork 单轮任务执行(开发计划 §4.5)。
|
|
3
4
|
*
|
|
@@ -90,6 +91,7 @@ async function waitForUi(cdp, selectors, logger, timeoutMs = 60_000) {
|
|
|
90
91
|
function guiOf(resolved) {
|
|
91
92
|
const g = resolved.profile.gui;
|
|
92
93
|
return {
|
|
94
|
+
...ZCODE_SETUP_DEFAULTS,
|
|
93
95
|
cdpPort: g?.cdpPort ?? 9222,
|
|
94
96
|
cdpPortAuto: g?.cdpPortAuto ?? true,
|
|
95
97
|
cdpPortRange: g?.cdpPortRange ?? 20,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CdpDisconnectedError, CdpUnavailableError } from "../traework/cdp/client.js";
|
|
2
2
|
import { type ZcodeSelectorKey } from "./selectors.js";
|
|
3
3
|
import type { ZcodePoll } from "./liveness.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type ZcodeProjectItem } from "./project.js";
|
|
5
5
|
export { CdpDisconnectedError, CdpUnavailableError };
|
|
6
6
|
export declare function retryZcodeEvaluation<T>(operation: () => Promise<T>, sleep?: (ms: number) => Promise<void>): Promise<T>;
|
|
7
7
|
export interface ZcodeSessionItem {
|
|
@@ -14,6 +14,12 @@ export interface ZcodeQuestionAnswerResult {
|
|
|
14
14
|
available: string[];
|
|
15
15
|
error?: string;
|
|
16
16
|
}
|
|
17
|
+
export interface ZcodeClickExactResult {
|
|
18
|
+
clicked: boolean;
|
|
19
|
+
count: number;
|
|
20
|
+
available: string[];
|
|
21
|
+
testids?: string[];
|
|
22
|
+
}
|
|
17
23
|
export declare class ZcodeCdpClient {
|
|
18
24
|
private readonly selectors;
|
|
19
25
|
private readonly inner;
|
|
@@ -27,18 +33,19 @@ export declare class ZcodeCdpClient {
|
|
|
27
33
|
exists(key: ZcodeSelectorKey): Promise<boolean>;
|
|
28
34
|
text(key: ZcodeSelectorKey): Promise<string>;
|
|
29
35
|
click(key: ZcodeSelectorKey): Promise<boolean>;
|
|
30
|
-
clickExact(key: ZcodeSelectorKey, value: string): Promise<
|
|
31
|
-
|
|
32
|
-
count: number;
|
|
33
|
-
available: string[];
|
|
34
|
-
}>;
|
|
35
|
-
selection(key: ZcodeSelectorKey): Promise<{
|
|
36
|
+
clickExact(key: ZcodeSelectorKey, value: string): Promise<ZcodeClickExactResult>;
|
|
37
|
+
selection(_key: ZcodeSelectorKey): Promise<{
|
|
36
38
|
display: string;
|
|
37
39
|
internal: string;
|
|
38
40
|
}>;
|
|
39
41
|
projects(): Promise<ZcodeProjectItem[]>;
|
|
40
42
|
clickProject(id: string | undefined, projectPath: string | undefined): Promise<boolean>;
|
|
41
43
|
boundProjectPath(): Promise<string>;
|
|
44
|
+
workspaceBinding(): Promise<{
|
|
45
|
+
triggerText: string;
|
|
46
|
+
projectPath: string;
|
|
47
|
+
ambiguous?: boolean;
|
|
48
|
+
}>;
|
|
42
49
|
session(): Promise<{
|
|
43
50
|
id?: string;
|
|
44
51
|
title?: string;
|
package/dist/agents/zcode/cdp.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { TraeworkCdpClient, CdpDisconnectedError, CdpUnavailableError, } from "../traework/cdp/client.js";
|
|
2
2
|
import { candidateExpr } from "./selectors.js";
|
|
3
|
+
import { projectDisplayName } from "./project.js";
|
|
3
4
|
import { normalizeZcodeModelSelection } from "./model.js";
|
|
5
|
+
import { projectTriggerDom, workspaceBindingExpression, modelSelectionExpression, sendButtonPointExpression, } from "./dom.js";
|
|
4
6
|
export { CdpDisconnectedError, CdpUnavailableError };
|
|
5
7
|
export async function retryZcodeEvaluation(operation, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
6
8
|
for (let attempt = 0;; attempt++) {
|
|
@@ -83,6 +85,13 @@ export class ZcodeCdpClient {
|
|
|
83
85
|
return ((await this.evaluate(`(function(){for(const s of ${candidateExpr(key, this.selectors)}){for(const e of document.querySelectorAll(s)){const r=e.getBoundingClientRect();if(r.width&&r.height)return (e.value||e.textContent||e.getAttribute('title')||'').trim()}}return ''})()`)) || "");
|
|
84
86
|
}
|
|
85
87
|
async click(key) {
|
|
88
|
+
if (key === "projectTrigger") {
|
|
89
|
+
const point = await this.evaluate(`(function(){${projectTriggerDom(this.selectors)}if(!trigger)return null;const r=trigger.getBoundingClientRect();return {x:r.left+r.width/2,y:r.top+r.height/2}})()`);
|
|
90
|
+
if (!point)
|
|
91
|
+
return false;
|
|
92
|
+
await this.clickAt(point.x, point.y);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
86
95
|
const point = await this.evaluate(`(function(){for(const s of ${candidateExpr(key, this.selectors)}){for(const e of document.querySelectorAll(s)){const r=e.getBoundingClientRect();if(r.width&&r.height)return {x:r.left+r.width/2,y:r.top+r.height/2}}}return null})()`);
|
|
87
96
|
if (!point)
|
|
88
97
|
return false;
|
|
@@ -90,28 +99,42 @@ export class ZcodeCdpClient {
|
|
|
90
99
|
return true;
|
|
91
100
|
}
|
|
92
101
|
async clickExact(key, value) {
|
|
93
|
-
const found = await this.evaluate(`(async function(){const norm=s=>(s||'').normalize('NFKC').trim().toLocaleLowerCase();const target=norm(${JSON.stringify(value)});const sels=${candidateExpr(key, this.selectors)};const visible=e=>{const r=e.getBoundingClientRect();if(!(r.width>0&&r.height>0&&r.bottom>0&&r.right>0&&r.top<innerHeight&&r.left<innerWidth))return false;const x=Math.max(0,Math.min(innerWidth-1,r.left+r.width/2)),y=Math.max(0,Math.min(innerHeight-1,r.top+r.height/2)),hit=document.elementFromPoint(x,y);return !!hit&&(hit===e||e.contains(hit))};const items=()=>{const out=[];for(const s of sels)for(const e of document.querySelectorAll(s))if(visible(e)&&!out.includes(e))out.push(e);return out};const leafTexts=e=>[...e.querySelectorAll('*')].filter(n=>n.children.length===0).map(n=>(n.textContent||'').trim()).filter(Boolean);const label=e=>(e.getAttribute('data-value')||e.getAttribute('data-model')||e.getAttribute('data-provider')||leafTexts(e)[0]||e.textContent||'').trim();let nodes=items();let scroller=nodes[0];while(scroller&&scroller!==document.body&&scroller.scrollHeight<=scroller.clientHeight)scroller=scroller.parentElement;const start=scroller?.scrollTop||0;if(scroller)scroller.scrollTop=0;const seen=new Map(),matches=new Map();for(let i=0;i<60;i++){await new Promise(r=>setTimeout(r,25));nodes=items();for(const e of nodes){const text=label(e);
|
|
102
|
+
const found = await this.evaluate(`(async function(){const norm=s=>(s||'').normalize('NFKC').trim().toLocaleLowerCase();const target=norm(${JSON.stringify(value)});const sels=${candidateExpr(key, this.selectors)};const visible=e=>{const r=e.getBoundingClientRect();if(!(r.width>0&&r.height>0&&r.bottom>0&&r.right>0&&r.top<innerHeight&&r.left<innerWidth))return false;const x=Math.max(0,Math.min(innerWidth-1,r.left+r.width/2)),y=Math.max(0,Math.min(innerHeight-1,r.top+r.height/2)),hit=document.elementFromPoint(x,y);return !!hit&&(hit===e||e.contains(hit))};const items=()=>{const out=[];for(const s of sels)for(const e of document.querySelectorAll(s))if(visible(e)&&!out.includes(e))out.push(e);return out};const leafTexts=e=>[...e.querySelectorAll('*')].filter(n=>n.children.length===0).map(n=>(n.textContent||'').trim()).filter(Boolean);const label=e=>(e.getAttribute('data-value')||e.getAttribute('data-model')||e.getAttribute('data-provider')||leafTexts(e)[0]||e.textContent||'').trim();let nodes=items();let scroller=nodes[0];while(scroller&&scroller!==document.body&&scroller.scrollHeight<=scroller.clientHeight)scroller=scroller.parentElement;const start=scroller?.scrollTop||0;if(scroller)scroller.scrollTop=0;const seen=new Map(),seenTestids=new Map(),matches=new Map();for(let i=0;i<60;i++){await new Promise(r=>setTimeout(r,25));nodes=items();for(const e of nodes){const text=label(e);const testid=e.getAttribute('data-testid')||'';const id=e.getAttribute('data-id')||e.getAttribute('data-model-id')||e.getAttribute('data-value')||e.getAttribute('data-model')||e.getAttribute('data-provider')||testid||text;const key=norm(id)+'|'+norm(text);if(testid)seenTestids.set(key,testid);if(!text)continue;seen.set(key,text);if(norm(text)===target)matches.set(key,{top:scroller?.scrollTop||0,text})}if(!scroller||scroller.scrollTop+scroller.clientHeight>=scroller.scrollHeight-1)break;const before=scroller.scrollTop;scroller.scrollTop=Math.min(scroller.scrollTop+Math.max(100,scroller.clientHeight*.8),scroller.scrollHeight);if(scroller.scrollTop===before)break}const result={available:[...seen.values()],testids:[...seenTestids.values()]};const matchesFound=[...matches.values()];if(matchesFound.length===1){if(scroller)scroller.scrollTop=matchesFound[0].top;await new Promise(r=>setTimeout(r,50));const exact=items().filter(e=>norm(label(e))===target);if(exact.length===1){exact[0].scrollIntoView({block:'center'});const r=exact[0].getBoundingClientRect();return {count:1,...result,point:{x:r.left+r.width/2,y:r.top+r.height/2}}}}if(scroller)scroller.scrollTop=start;return {count:matchesFound.length,...result}})()`);
|
|
94
103
|
if (!found.point)
|
|
95
|
-
return {
|
|
104
|
+
return {
|
|
105
|
+
clicked: false,
|
|
106
|
+
count: found.count,
|
|
107
|
+
available: found.available,
|
|
108
|
+
testids: found.testids,
|
|
109
|
+
};
|
|
96
110
|
await this.clickAt(found.point.x, found.point.y);
|
|
97
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
clicked: true,
|
|
113
|
+
count: found.count,
|
|
114
|
+
available: found.available,
|
|
115
|
+
testids: found.testids,
|
|
116
|
+
};
|
|
98
117
|
}
|
|
99
|
-
async selection(
|
|
100
|
-
const raw = await this.evaluate(
|
|
118
|
+
async selection(_key) {
|
|
119
|
+
const raw = await this.evaluate(modelSelectionExpression(this.selectors));
|
|
101
120
|
return normalizeZcodeModelSelection(raw);
|
|
102
121
|
}
|
|
103
122
|
async projects() {
|
|
104
123
|
return this.evaluate(`(function(){const out=[];for(const s of ${candidateExpr("projectItem", this.selectors)})for(const e of document.querySelectorAll(s)){const testid=e.getAttribute('data-testid')||'';const testPath=testid.startsWith('workspace-item-')?testid.slice('workspace-item-'.length):undefined;const name=(e.getAttribute('data-project-name')||e.querySelector('[class*=name]')?.textContent||e.textContent||'').trim();const p=e.getAttribute('data-project-path')||e.querySelector('[data-project-path]')?.getAttribute('data-project-path')||testPath||e.getAttribute('title')||undefined;const id=e.getAttribute('data-project-id')||e.getAttribute('data-id')||testid||undefined;if(name)out.push({name,path:p,id})}return out})()`);
|
|
105
124
|
}
|
|
106
125
|
async clickProject(id, projectPath) {
|
|
107
|
-
const
|
|
126
|
+
const displayName = projectPath ? projectDisplayName(projectPath) : "";
|
|
127
|
+
const point = await this.evaluate(`(async function(){${projectTriggerDom(this.selectors)}if(!trigger)return null;const targetName=norm(${JSON.stringify(displayName)});let containers=[];const controlledId=trigger?.getAttribute('aria-controls')||'';if(controlledId){const controlled=document.getElementById(controlledId);if(controlled&&visible(controlled))containers.push(controlled)}if(trigger){let parent=trigger.parentElement;while(parent&&!containers.length){if(parent.querySelector('[role="menuitemcheckbox"]'))containers.push(parent);parent=parent.parentElement}}if(!containers.length)containers=[...document.querySelectorAll('[role="menu"]')].filter(e=>visible(e)&&e.querySelector('[role="menuitemcheckbox"]'));const checkboxMatches=[];for(const container of containers)for(const e of container.querySelectorAll('[role="menuitemcheckbox"]'))if(visible(e)&&norm(e.getAttribute('aria-label')||e.getAttribute('data-value')||e.textContent||'')===targetName&&!checkboxMatches.includes(e))checkboxMatches.push(e);let selected=checkboxMatches.length===1?checkboxMatches[0]:null;if(!selected){const matches=[];for(const s of ${candidateExpr("projectItem", this.selectors)})for(const e of document.querySelectorAll(s)){const testid=e.getAttribute('data-testid')||'';const itemId=e.getAttribute('data-project-id')||e.getAttribute('data-id')||testid;const p=e.getAttribute('data-project-path')||e.querySelector('[data-project-path]')?.getAttribute('data-project-path')||(testid.startsWith('workspace-item-')?testid.slice('workspace-item-'.length):'')||e.getAttribute('title')||'';if(((${JSON.stringify(id ?? "")}&&itemId===${JSON.stringify(id ?? "")})||(${JSON.stringify(projectPath ?? "")}&&p===${JSON.stringify(projectPath ?? "")}))&&!matches.includes(e))matches.push(e)}if(matches.length===1)selected=matches[0]}if(!selected)return null;selected.scrollIntoView({block:'center'});await new Promise(r=>setTimeout(r,50));const r=selected.getBoundingClientRect();return visible(selected)?{x:r.left+r.width/2,y:r.top+r.height/2}:null})()`);
|
|
108
128
|
if (!point)
|
|
109
129
|
return false;
|
|
110
130
|
await this.clickAt(point.x, point.y);
|
|
111
131
|
return true;
|
|
112
132
|
}
|
|
113
133
|
async boundProjectPath() {
|
|
114
|
-
return (
|
|
134
|
+
return (await this.workspaceBinding()).projectPath;
|
|
135
|
+
}
|
|
136
|
+
async workspaceBinding() {
|
|
137
|
+
return this.evaluate(workspaceBindingExpression(this.selectors));
|
|
115
138
|
}
|
|
116
139
|
async session() {
|
|
117
140
|
return this.evaluate(`(function(){const stable=id=>id&&id!=='draft'?id:undefined;const idOf=e=>{if(!e)return undefined;const explicit=stable(e.getAttribute('data-session-id'));if(explicit)return explicit;const testid=e.getAttribute('data-testid')||'';return testid.startsWith('task-item-')?stable(testid.slice('task-item-'.length)):undefined};const selectors=['[data-testid^="v4-session-pane"][data-session-id]','[data-session-id][aria-current=true]','[data-session-id][aria-selected=true]','[data-testid^="task-item-"][aria-current=true]','[data-testid^="task-item-"][aria-selected=true]','[data-testid^="task-item-"][data-state=active]','[data-testid^="task-item-"][data-state=selected]'];let active=null;for(const s of selectors){active=[...document.querySelectorAll(s)].find(e=>{const r=e.getBoundingClientRect();return r.width&&r.height})||null;if(active)break}const id=idOf(active)||stable(history.state?.sessionId)||stable(history.state?.taskId)||undefined;const item=id?[...document.querySelectorAll('[data-testid^="task-item-"]')].find(e=>(e.getAttribute('data-testid')||'').slice('task-item-'.length)===id):null;return {id,title:id?(item?.getAttribute('title')||item?.textContent||active?.getAttribute('title')||'').trim().slice(0,240)||undefined:undefined}})()`);
|
|
@@ -141,20 +164,16 @@ export class ZcodeCdpClient {
|
|
|
141
164
|
await this.send("Input.insertText", { text });
|
|
142
165
|
}
|
|
143
166
|
async sendMessage() {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
await this.
|
|
152
|
-
type: "keyUp",
|
|
153
|
-
key: "Enter",
|
|
154
|
-
code: "Enter",
|
|
155
|
-
windowsVirtualKeyCode: 13,
|
|
156
|
-
});
|
|
167
|
+
const deadline = Date.now() + 10_000;
|
|
168
|
+
for (let attempt = 0; attempt < 50 && Date.now() < deadline; attempt++) {
|
|
169
|
+
const point = await this.evaluate(sendButtonPointExpression(this.selectors));
|
|
170
|
+
if (point) {
|
|
171
|
+
await this.clickAt(point.x, point.y);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
await this.evaluate("new Promise(resolve=>setTimeout(resolve,200))");
|
|
157
175
|
}
|
|
176
|
+
throw new Error("ZCode 发送按钮未在观察期内启用或被遮挡;未发送任务");
|
|
158
177
|
}
|
|
159
178
|
async answerQuestion(answer) {
|
|
160
179
|
const located = await this.evaluate(`(function(){const norm=s=>(s||'').normalize('NFKC').trim().toLocaleLowerCase();const target=norm(${JSON.stringify(answer)});const visible=e=>{const r=e.getBoundingClientRect(),s=getComputedStyle(e);return r.width>0&&r.height>0&&r.bottom>0&&r.right>0&&r.top<innerHeight&&r.left<innerWidth&&s.display!=='none'&&s.visibility!=='hidden'};const boxes=[...document.querySelectorAll('[role="listbox"][aria-label]:has([role="option"])')].filter(visible);if(boxes.length!==1)return {count:boxes.length,available:[],buttonStates:[]};const box=boxes[0];const options=[...box.querySelectorAll('[role="option"]')].filter(visible);const label=e=>{const leaves=[...e.querySelectorAll('*')].filter(n=>n.children.length===0).map(n=>(n.textContent||'').trim()).filter(Boolean);return leaves.find(x=>norm(x)===target)||e.getAttribute('aria-label')||e.getAttribute('data-value')||''};const available=options.map(e=>{const leaves=[...e.querySelectorAll('*')].filter(n=>n.children.length===0).map(n=>(n.textContent||'').trim()).filter(Boolean);return leaves.find(x=>x&&!/^\\d+[.\uff0e]$/.test(x))||(e.textContent||'').trim()}).filter(Boolean);const matches=options.filter(e=>norm(label(e))===target);let scope=box,buttons=[];while(scope.parentElement){scope=scope.parentElement;buttons=[...scope.querySelectorAll('button:not([role="option"])')].filter(visible);if(buttons.length)break}if(matches.length!==1)return {count:matches.length,available,buttonStates:buttons.map(e=>e.disabled||e.getAttribute('aria-disabled')==='true')};matches[0].scrollIntoView({block:'center'});const r=matches[0].getBoundingClientRect();return {count:1,available,point:{x:r.left+r.width/2,y:r.top+r.height/2},buttonStates:buttons.map(e=>e.disabled||e.getAttribute('aria-disabled')==='true')}})()`);
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export interface NativeDialogOptions {
|
|
2
|
+
timeoutMs?: number;
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
platform?: NodeJS.Platform;
|
|
5
|
+
onProgress?: (stage: string) => void;
|
|
6
|
+
}
|
|
1
7
|
export declare function parseMacSheetBaseline(baseline: string[]): number | null;
|
|
2
8
|
export declare function validateMacSheetBaseline(baseline: string[]): {
|
|
3
9
|
ok: true;
|
|
@@ -6,8 +12,8 @@ export declare function validateMacSheetBaseline(baseline: string[]): {
|
|
|
6
12
|
ok: false;
|
|
7
13
|
message: string;
|
|
8
14
|
};
|
|
9
|
-
export declare function listOwnedDialogs(pids: number[]): Promise<string[]>;
|
|
10
|
-
export declare function selectZcodeFolder(folder: string, ownerPids: number[], baseline: string[]): Promise<{
|
|
15
|
+
export declare function listOwnedDialogs(pids: number[], options?: NativeDialogOptions): Promise<string[]>;
|
|
16
|
+
export declare function selectZcodeFolder(folder: string, ownerPids: number[], baseline: string[], options?: NativeDialogOptions): Promise<{
|
|
11
17
|
ok: boolean;
|
|
12
18
|
needsPermission?: boolean;
|
|
13
19
|
message: string;
|