@xdxer/dingtalk-agent 0.1.5-beta.1 → 0.1.5-beta.11
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 +209 -0
- package/README.en.md +99 -66
- package/README.md +99 -66
- package/dist/bin/dingtalk-agent.js +735 -149
- package/dist/bin/dingtalk-agent.js.map +1 -1
- package/dist/src/actions.js +3 -2
- package/dist/src/actions.js.map +1 -1
- package/dist/src/agent-audit.js +202 -85
- package/dist/src/agent-audit.js.map +1 -1
- package/dist/src/agent-bindings.js +4 -8
- package/dist/src/agent-bindings.js.map +1 -1
- package/dist/src/agent-definition.js +7 -3
- package/dist/src/agent-definition.js.map +1 -1
- package/dist/src/agent-enhance.js +51 -32
- package/dist/src/agent-enhance.js.map +1 -1
- package/dist/src/agent-platform.js +3 -3
- package/dist/src/agent-platform.js.map +1 -1
- package/dist/src/bootstrap.js +6 -2
- package/dist/src/bootstrap.js.map +1 -1
- package/dist/src/development-workspace.js +210 -34
- package/dist/src/development-workspace.js.map +1 -1
- package/dist/src/doctor.js +65 -9
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/dws.js +67 -3
- package/dist/src/dws.js.map +1 -1
- package/dist/src/init.js +2 -1
- package/dist/src/init.js.map +1 -1
- package/dist/src/map.js +157 -0
- package/dist/src/map.js.map +1 -0
- package/dist/src/memory/candidates.js +8 -0
- package/dist/src/memory/candidates.js.map +1 -1
- package/dist/src/memory/noop-receipt.js +320 -0
- package/dist/src/memory/noop-receipt.js.map +1 -0
- package/dist/src/memory/operational.js +27 -3
- package/dist/src/memory/operational.js.map +1 -1
- package/dist/src/memory/remote-state.js +2 -1
- package/dist/src/memory/remote-state.js.map +1 -1
- package/dist/src/multica-deploy.js +692 -125
- package/dist/src/multica-deploy.js.map +1 -1
- package/dist/src/multica-provider.js +303 -25
- package/dist/src/multica-provider.js.map +1 -1
- package/dist/src/multica-runtime-vocabulary.js +110 -0
- package/dist/src/multica-runtime-vocabulary.js.map +1 -0
- package/dist/src/opencode-evals.js +6 -6
- package/dist/src/opencode-evals.js.map +1 -1
- package/dist/src/opencode-provider.js +21 -7
- package/dist/src/opencode-provider.js.map +1 -1
- package/dist/src/opencode-workspace.js +3 -3
- package/dist/src/opencode-workspace.js.map +1 -1
- package/dist/src/personal-event-evals.js +4 -2
- package/dist/src/personal-event-evals.js.map +1 -1
- package/dist/src/promotion.js +2 -1
- package/dist/src/promotion.js.map +1 -1
- package/dist/src/remote-semantic-state-live-evals.js +14 -8
- package/dist/src/remote-semantic-state-live-evals.js.map +1 -1
- package/dist/src/remote-state-evals.js +2 -2
- package/dist/src/remote-state-evals.js.map +1 -1
- package/dist/src/robot-evals.js +3 -3
- package/dist/src/robot-evals.js.map +1 -1
- package/dist/src/schedule-plan.js +380 -0
- package/dist/src/schedule-plan.js.map +1 -0
- package/dist/src/sessions.js +1 -1
- package/dist/src/sessions.js.map +1 -1
- package/dist/src/skill-manager.js +145 -13
- package/dist/src/skill-manager.js.map +1 -1
- package/dist/src/skills.js +2 -0
- package/dist/src/skills.js.map +1 -1
- package/dist/src/state-dir.js +120 -0
- package/dist/src/state-dir.js.map +1 -0
- package/dist/src/tui.js +369 -0
- package/dist/src/tui.js.map +1 -0
- package/dist/src/upgrade.js +113 -33
- package/dist/src/upgrade.js.map +1 -1
- package/dist/src/waits.js +2 -1
- package/dist/src/waits.js.map +1 -1
- package/dist/src/workspace.js +12 -7
- package/dist/src/workspace.js.map +1 -1
- package/docs/AGENT-IN-PRODUCTION.md +255 -0
- package/docs/ARCHITECTURE.md +165 -31
- package/docs/INSTALLATION.md +7 -7
- package/docs/PLATFORM-GUARDRAILS.md +188 -0
- package/docs/PRIOR-ART.md +4 -0
- package/docs/SELF-TEST.md +4 -4
- package/docs/architecture/agent-platform-connection-layer.svg +120 -0
- package/docs/architecture/digital-employee-composition.svg +92 -0
- package/docs/architecture/dingtalk-agent-architecture.svg +125 -0
- package/docs/assets/digital-employee-at-work.svg +77 -0
- package/docs/schemas/multica-deployment-plan.schema.json +3 -1
- package/docs/schemas/multica-deployment-receipt.schema.json +17 -3
- package/docs/schemas/multica-deployment-status.schema.json +6 -2
- package/docs/schemas/multica-workspace-inspection.schema.json +16 -0
- package/docs/schemas/multica-workspace-run-plan.schema.json +31 -0
- package/docs/schemas/multica-workspace-run.schema.json +161 -0
- package/docs/schemas/multica-workspace-status.schema.json +2 -0
- package/docs/schemas/project.schema.json +54 -3
- package/docs/schemas/workspace-scaffold.schema.json +38 -0
- package/examples/agents/README.md +10 -8
- package/examples/agents/fde-coach/AGENTS.md +2 -34
- package/examples/agents/fde-coach/agent/AGENTS.md +35 -0
- package/examples/agents/fde-coach/agent.bindings.json +10 -0
- package/examples/agents/release-manager/AGENTS.md +2 -34
- package/examples/agents/release-manager/agent/AGENTS.md +35 -0
- package/examples/agents/release-manager/agent.bindings.json +10 -0
- package/lab/agent-eval/catalog.json +5 -5
- package/lab/agent-eval/classic-failures.json +4 -4
- package/lab/agent-eval/completion-gate-regression.json +9 -9
- package/lab/agent-eval/personal-event-live.example.json +3 -3
- package/lab/agent-eval/remote-semantic-state-live.example.json +1 -1
- package/lab/agent-eval/workspace/opencode.json +2 -2
- package/lab/project-workspace/fake-multica-provider.mjs +171 -17
- package/lab/project-workspace/multica-deploy.fixture.json +2 -2
- package/lab/project-workspace/multica-readonly.fixture.json +4 -16
- package/lab/project-workspace/opencode-provider-suite.json +3 -3
- package/lab/project-workspace/project.fixture.json +2 -6
- package/lab/robot-eval/suite.json +1 -1
- package/lab/robot-eval/workspace/AGENTS.md +1 -1
- package/lab/robot-eval/workspace/opencode.json +2 -2
- package/lab/schemas/personal-event-eval.schema.json +1 -1
- package/package.json +12 -9
- package/skills/README.md +10 -8
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/SKILL.md +48 -23
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/AGENTS.template.md +1 -1
- package/skills/core/dta-agent-compose/assets/REPOSITORY.template.md +10 -0
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/agent.bindings.dingtalk-doc.template.json +2 -2
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/agent.bindings.local.template.json +2 -2
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/hosts/opencode/opencode.template.json +3 -2
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/evals/evals.json +4 -4
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/agent-definition-contract.md +7 -7
- package/skills/core/dta-agent-compose/references/drive-and-schedules.md +166 -0
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/host-loading-contract.md +11 -13
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/hosts/claude-code.md +13 -12
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/hosts/opencode.md +14 -13
- package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/SKILL.md +2 -2
- package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/assets/eval-catalog.template.json +1 -1
- package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/evals/evals.json +1 -1
- package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/eval-topology.md +3 -3
- package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/interactive-debug-channels.md +10 -4
- package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/SKILL.md +21 -5
- package/skills/core/dta-basic-behavior/references/event-to-behavior.md +38 -0
- package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/memory-and-evolution.md +3 -1
- package/skills/core/dta-basic-behavior/references/perception-and-gates.md +87 -0
- package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/risk-authority-and-privacy.md +12 -0
- package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/truth-and-recovery.md +4 -2
- package/skills/core/dta-people-group-memory/COMPLETENESS.md +36 -0
- package/skills/core/dta-people-group-memory/SKILL.md +69 -0
- package/skills/core/dta-people-group-memory/references/adapters.md +273 -0
- package/skills/core/dta-people-group-memory/references/assembly-guidance.md +40 -0
- package/skills/core/dta-people-group-memory/references/binding.md +110 -0
- package/skills/core/dta-people-group-memory/references/cold-start.md +70 -0
- package/skills/core/dta-people-group-memory/references/config-binding.md +89 -0
- package/skills/core/dta-people-group-memory/references/consent-and-visibility.md +83 -0
- package/skills/core/dta-people-group-memory/references/consolidation.md +162 -0
- package/skills/core/dta-people-group-memory/references/event-ingest.md +103 -0
- package/skills/core/dta-people-group-memory/references/guided-setup.md +70 -0
- package/skills/core/dta-people-group-memory/references/model.md +148 -0
- package/skills/core/dta-people-group-memory/references/storage-port.md +107 -0
- package/skills/platforms/deap/PLATFORM.md +30 -1
- package/skills/platforms/multica-dingtalk/PLATFORM.md +34 -8
- package/skills/platforms/multica-dingtalk/{dingtalk-agent-deploy-multica → dta-deploy-multica}/SKILL.md +10 -8
- package/skills/platforms/multica-dingtalk/dta-deploy-multica/references/multica-deployment-contract.md +67 -0
- package/skills/platforms/multica-dingtalk/{multica-external → dta-ops-multica}/SKILL.md +81 -11
- package/skills/platforms/multica-dingtalk/{multica-external → dta-ops-multica}/scripts/bootstrap.sh +2 -2
- package/skills/platforms/multica-dingtalk/{multica-external → dta-ops-multica}/scripts/multica_ext.py +264 -16
- package/docs/assets/agent-delivery-lifecycle.svg +0 -103
- package/skills/core/dingtalk-basic-behavior/references/event-to-behavior.md +0 -24
- package/skills/core/dingtalk-basic-behavior/references/perception-and-gates.md +0 -28
- package/skills/platforms/multica-dingtalk/dingtalk-agent-boot-multica/SKILL.md +0 -40
- package/skills/platforms/multica-dingtalk/dingtalk-agent-deploy-multica/references/multica-deployment-contract.md +0 -49
- /package/examples/agents/fde-coach/{skills → agent/skills}/fde-coach/SKILL.md +0 -0
- /package/examples/agents/release-manager/{skills → agent/skills}/release-manager/SKILL.md +0 -0
- /package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/role-skill.template.md +0 -0
- /package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/storage-routing.md +0 -0
- /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/evidence-contract.md +0 -0
- /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/failure-to-case.md +0 -0
- /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/local-connector-smoke.md +0 -0
- /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/scenario-taxonomy.md +0 -0
- /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/storage-modes.md +0 -0
- /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/assets/memory-candidate-proposal.json +0 -0
- /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/assets/task-checkpoint.json +0 -0
- /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/action-contract.md +0 -0
- /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/runtime-modes.md +0 -0
- /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/task-lifecycle.md +0 -0
- /package/skills/platforms/multica-dingtalk/{dingtalk-agent-deploy-multica → dta-deploy-multica}/references/promotion-observation-contract.md +0 -0
|
@@ -31,16 +31,20 @@ import { runRobotEvaluation } from '../src/robot-evals.js';
|
|
|
31
31
|
import { runOpenCodeEvaluation } from '../src/opencode-evals.js';
|
|
32
32
|
import { runStorageEvaluation } from '../src/storage-evals.js';
|
|
33
33
|
import { runPersonalEventEvaluation } from '../src/personal-event-evals.js';
|
|
34
|
-
import { doctor } from '../src/doctor.js';
|
|
34
|
+
import { CHECK_LABELS, doctor } from '../src/doctor.js';
|
|
35
35
|
import { doctorDevelopmentWorkspace, inspectAgentProject, showDevelopmentWorkspace, } from '../src/development-workspace.js';
|
|
36
|
+
import { buildScheduleShow, buildSchedulePlan, scheduleBlockers } from '../src/schedule-plan.js';
|
|
36
37
|
import { applyOpenCodeWorkspace, evalOpenCodeWorkspace, planOpenCodeWorkspace, runOpenCodeWorkspace, useOpenCodeWorkspace, } from '../src/opencode-provider.js';
|
|
37
|
-
import { inspectMulticaWorkspace, listMulticaWorkspaceResources, planMulticaWorkspace, statusMulticaWorkspace, } from '../src/multica-provider.js';
|
|
38
|
-
import { applyMulticaDeployment, listMulticaDeployments, planMulticaDeployment, statusMulticaDeployment, } from '../src/multica-deploy.js';
|
|
38
|
+
import { inspectMulticaWorkspace, listMulticaWorkspaceResources, planMulticaWorkspace, scaffoldMulticaWorkspace, statusMulticaWorkspace, } from '../src/multica-provider.js';
|
|
39
|
+
import { applyMulticaDeployment, listMulticaDeployments, planMulticaDeployment, runMulticaWorkspaceIssue, statusMulticaDeployment, statusMulticaWorkspaceRun, } from '../src/multica-deploy.js';
|
|
39
40
|
import { applyObservation, applyPromotion, listPromotions, planObservation, planPromotion, statusPromotion, } from '../src/promotion.js';
|
|
40
41
|
import { setup } from '../src/setup.js';
|
|
41
|
-
import { upgradeAgent } from '../src/upgrade.js';
|
|
42
|
+
import { checkUpgrade, upgradeAgent } from '../src/upgrade.js';
|
|
43
|
+
// out/err 在本文件里改名导入:printX 的形参普遍叫 out,直接同名会被遮蔽。
|
|
44
|
+
import { configureTui, confirm, displayWidth, padRight, renderPairs, StepReporter, err as stderrUi, out as stdoutUi, } from '../src/tui.js';
|
|
42
45
|
import { checkpointTask, showTaskCheckpoint } from '../src/memory/task-checkpoints.js';
|
|
43
46
|
import { upsertOperationalMemory } from '../src/memory/operational.js';
|
|
47
|
+
import { listMemoryNoops, recordMemoryNoop, traceMemoryNoop } from '../src/memory/noop-receipt.js';
|
|
44
48
|
import { proposeMemoryCandidate, publishMemoryCandidate, reviewMemoryCandidate, showMemoryCandidate, } from '../src/memory/candidates.js';
|
|
45
49
|
import { installBundledSkills, installGlobalSkill, skillStatus, skillSuiteStatus, uninstallBundledSkills, uninstallGlobalSkill, upgradeBundledSkills, upgradeGlobalSkill, } from '../src/skill-manager.js';
|
|
46
50
|
import { agentPlatformReadiness, MANAGED_AGENT_PLATFORMS, multicaTargets, platformDocPath, platformSkillForRole, requireSupportedAgentPlatform, resolveAgentPlatform, resolveEndpoint, statusLabel as platformStatusLabel, useAgentPlatform, } from '../src/agent-platform.js';
|
|
@@ -64,7 +68,7 @@ dta —— 给 Agent 安装钉钉员工的基础行为和工作环境
|
|
|
64
68
|
dta setup 首次配置 CLI、DWS 和内置 Skill(基础行为 + Agent 装配 + 交付后评测)
|
|
65
69
|
dta bootstrap --json 为当前 Agent 会话加载工作区
|
|
66
70
|
dta agent enhance / audit 安全增强已有仓库,再审计定义、存储与 Skill 真加载
|
|
67
|
-
dta info /
|
|
71
|
+
dta info / workspace doctor / schedule show <name> 只读诊断 Project、Workspace 与声明的常驻节律
|
|
68
72
|
dta agent-platform use <platform> → dta deploy / dta promote 声明平台归属并装平台技能包,再受控部署与晋级
|
|
69
73
|
|
|
70
74
|
维护环境:
|
|
@@ -86,6 +90,7 @@ dingtalk-agent 可信事件运行时(可选宿主能力)
|
|
|
86
90
|
task checkpoint --input checkpoint.json --expect-revision N
|
|
87
91
|
memory operational upsert --provider provider.json --input record.json [--live --yes]
|
|
88
92
|
memory candidate propose|show|review|publish
|
|
93
|
+
memory noop record|list|trace
|
|
89
94
|
act ack|reply|ask|silence
|
|
90
95
|
|
|
91
96
|
这些命令负责目标冻结、Session/Run、Wait、幂等和回执;普通 Agent Session 不必使用。
|
|
@@ -155,7 +160,7 @@ const DEPLOY_HELP = `
|
|
|
155
160
|
dingtalk-agent Multica 受控部署
|
|
156
161
|
|
|
157
162
|
deploy --workspace <name> --dry-run [--json]
|
|
158
|
-
deploy --workspace <name> --plan-id <id> --yes [--no-wait] [--json]
|
|
163
|
+
deploy --workspace <name> --plan-id <id> --yes [--wait | --no-wait] [--json]
|
|
159
164
|
deploy --workspace <name> --status [--operation-id <id>] [--json]
|
|
160
165
|
deploy --workspace <name> --status --operation-id <id> --execute --yes [--json]
|
|
161
166
|
deploy --workspace <name> --list [--json]
|
|
@@ -163,6 +168,9 @@ dingtalk-agent Multica 受控部署
|
|
|
163
168
|
deploy --workspace <name> --retire --plan-id <id> --yes [--json]
|
|
164
169
|
|
|
165
170
|
dry-run 不启动 Multica。apply/retire 必须携带当前 planId;scope 或源码漂移会在写入前拒绝。
|
|
171
|
+
apply 默认在同一条命令内等 load smoke 到 ~30 秒;云端 runtime 冷启常超 30 秒会返回 verifying,
|
|
172
|
+
再用 --status --operation-id 独立 reconcile。加 --wait 把等待预算放宽到 4 分钟,一条命令直接到 ready;
|
|
173
|
+
--no-wait 立即返回 verifying(异步/CI)。--smoke-timeout <ms> 精确覆盖等待预算(上限 5 分钟)。
|
|
166
174
|
status 默认只验本地脱敏 operation/Receipt;--execute --yes 才做远端独立回读。
|
|
167
175
|
deploy 只管理 Agent、完整 Skill tree、assignment 和 load smoke,不创建机器人、Webhook、Autopilot 或定时 Trigger。
|
|
168
176
|
`;
|
|
@@ -179,13 +187,100 @@ dingtalk-agent 已验版本晋级与反馈候选
|
|
|
179
187
|
Promotion 只接受指定 suite 的 load/hard/surface gate 与精确 W2 evidence hash,并委托 W4 deploy。
|
|
180
188
|
Observe 只写 gitignored、proposed、不可直接发布的 Eval candidate;不修改 AGENTS.md、Prompt、Skill 或 Trigger。
|
|
181
189
|
`;
|
|
190
|
+
const UPGRADE_HELP = `
|
|
191
|
+
dta upgrade —— 自升级 CLI,再重新检查完整环境
|
|
192
|
+
|
|
193
|
+
用法:
|
|
194
|
+
dta upgrade 升级到当前通道的最新版本
|
|
195
|
+
dta upgrade --check 只查有没有新版本,不安装
|
|
196
|
+
dta upgrade --dry-run 只预览将执行的命令,不安装
|
|
197
|
+
dta upgrade --channel beta 指定通道(beta | latest)
|
|
198
|
+
dta upgrade --version <x.y.z> 升级到指定版本
|
|
199
|
+
dta upgrade --version <x.y.z> --force 允许降级(两者必须同时给)
|
|
200
|
+
dta upgrade --yes 跳过确认提示(AI Agent 模式)
|
|
201
|
+
dta upgrade --json 机器通道;输出 upgrade@1,不打任何进度
|
|
202
|
+
|
|
203
|
+
参数:
|
|
204
|
+
--check 只解析升级目标并对比当前版本
|
|
205
|
+
--channel <name> beta 或 latest;不给时按当前版本是否带 - 推断
|
|
206
|
+
--version <ver> 精确版本;与 --channel 互斥
|
|
207
|
+
--prefix <dir> 安装前缀,默认 ~/.local
|
|
208
|
+
--dry-run 不安装,只回报将执行的 install/verify 命令
|
|
209
|
+
--force 强制重装当前版本;配合 --version 才允许降级
|
|
210
|
+
--yes 跳过确认;非交互环境本来就不提示
|
|
211
|
+
--json 输出 JSON,不混入任何日志
|
|
212
|
+
--no-color 关闭颜色(也支持 NO_COLOR 环境变量)
|
|
213
|
+
|
|
214
|
+
升级过程分 7 步:冻结 Skill 版本 → 解析目标 → 检查版本关系 → 安装
|
|
215
|
+
→ 定位新 CLI → 校验版本 → 复核环境。前 3 步失败不会改动机器;从第 4 步
|
|
216
|
+
(安装)起失败可能留下已更新但未复核的 CLI,此时按提示重跑 setup 或
|
|
217
|
+
用 --version <旧版本> --force 回退。
|
|
218
|
+
|
|
219
|
+
非交互环境(管道 / CI / Agent 调用)不提示确认,直接执行。
|
|
220
|
+
颜色只在终端里出现;管道里想要颜色需显式 --color。
|
|
221
|
+
`;
|
|
222
|
+
/**
|
|
223
|
+
* 帮助文本的打印时装饰。**不把 ANSI 写进常量**——常量一旦带色就没法降级,
|
|
224
|
+
* 而这里 color=false 时原样返回,NO_COLOR / 管道下与改造前逐字节相同。
|
|
225
|
+
*
|
|
226
|
+
* 只染整段连续短语:命令名从行首吃到两个以上空格为止,所以
|
|
227
|
+
* `dta setup`、`dta skill status` 这类断言目标始终完整。行数不变。
|
|
228
|
+
*/
|
|
229
|
+
function renderHelp(text) {
|
|
230
|
+
const { ctx, theme } = stdoutUi();
|
|
231
|
+
if (!ctx.color)
|
|
232
|
+
return text;
|
|
233
|
+
return text.split('\n').map((line, index) => {
|
|
234
|
+
if (index === 1 && line.trim())
|
|
235
|
+
return theme.bold(line);
|
|
236
|
+
if (/^\S.*[::]$/.test(line))
|
|
237
|
+
return theme.bold(line);
|
|
238
|
+
if (/^\s+·\s/.test(line))
|
|
239
|
+
return theme.dim(line);
|
|
240
|
+
const command = line.match(/^(\s+)((?:dta|dingtalk-agent)\s\S.*?)(\s{2,})(.*)$/);
|
|
241
|
+
if (command)
|
|
242
|
+
return `${command[1]}${theme.noun(command[2])}${command[3]}${theme.dim(command[4])}`;
|
|
243
|
+
return line;
|
|
244
|
+
}).join('\n');
|
|
245
|
+
}
|
|
182
246
|
function fail(msg, code = 1, hint = '') {
|
|
183
|
-
|
|
247
|
+
// 整行染色,绝不做 danger('错误') + ': ' + msg 这种切分——
|
|
248
|
+
// 契约 eval 断言的是 stderr.includes('错误:'),切开就再也匹配不上。
|
|
249
|
+
const { theme } = stderrUi();
|
|
250
|
+
console.error(theme.danger(`错误: ${msg}`));
|
|
184
251
|
if (hint)
|
|
185
|
-
console.error(`提示: ${hint}`);
|
|
252
|
+
console.error(theme.dim(`提示: ${hint}`));
|
|
186
253
|
process.exit(code);
|
|
187
254
|
}
|
|
188
|
-
|
|
255
|
+
// --color / --no-color 在 dispatch 前预剥:parseArgs 是 strict 模式,
|
|
256
|
+
// 想让它成为全局参数就得改 23 个 options 块,预剥只需要这一处。
|
|
257
|
+
//
|
|
258
|
+
// 三条边界:`--` 之后的 token 是 positional,一律不碰;非法取值报错而不是
|
|
259
|
+
// 静默吞掉;同一参数出现多次以最后一次为准(与 parseArgs 的口径一致)。
|
|
260
|
+
const rawArgv = process.argv.slice(2);
|
|
261
|
+
const COLOR_VALUES = new Set(['auto', 'always', 'never']);
|
|
262
|
+
const terminator = rawArgv.indexOf('--');
|
|
263
|
+
const scannable = terminator === -1 ? rawArgv : rawArgv.slice(0, terminator);
|
|
264
|
+
let colorFlag = 'auto';
|
|
265
|
+
for (const item of scannable) {
|
|
266
|
+
if (item === '--no-color')
|
|
267
|
+
colorFlag = 'never';
|
|
268
|
+
else if (item === '--color')
|
|
269
|
+
colorFlag = 'always';
|
|
270
|
+
else if (item.startsWith('--color=')) {
|
|
271
|
+
const value = item.slice('--color='.length);
|
|
272
|
+
if (!COLOR_VALUES.has(value)) {
|
|
273
|
+
console.error(`错误: --color 只接受 auto | always | never,收到 ${JSON.stringify(value)}`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
colorFlag = value;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
configureTui(colorFlag);
|
|
280
|
+
const isColorToken = (item) => item === '--color' || item === '--no-color' || item.startsWith('--color=');
|
|
281
|
+
const argv = terminator === -1
|
|
282
|
+
? rawArgv.filter((item) => !isColorToken(item))
|
|
283
|
+
: [...rawArgv.slice(0, terminator).filter((item) => !isColorToken(item)), ...rawArgv.slice(terminator)];
|
|
189
284
|
const cmd = argv[0];
|
|
190
285
|
const sub = argv[1];
|
|
191
286
|
try {
|
|
@@ -198,38 +293,38 @@ catch (e) {
|
|
|
198
293
|
}
|
|
199
294
|
async function main() {
|
|
200
295
|
if (cmd === 'help' && sub === 'runtime') {
|
|
201
|
-
console.log(RUNTIME_HELP);
|
|
296
|
+
console.log(renderHelp(RUNTIME_HELP));
|
|
202
297
|
return;
|
|
203
298
|
}
|
|
204
299
|
if (cmd === 'help' && sub === 'adapters') {
|
|
205
|
-
console.log(ADAPTER_HELP);
|
|
300
|
+
console.log(renderHelp(ADAPTER_HELP));
|
|
206
301
|
return;
|
|
207
302
|
}
|
|
208
303
|
if (cmd === 'help' && sub === 'lab') {
|
|
209
|
-
console.log(LAB_HELP);
|
|
304
|
+
console.log(renderHelp(LAB_HELP));
|
|
210
305
|
return;
|
|
211
306
|
}
|
|
212
307
|
if (cmd === 'help' && sub === 'agent') {
|
|
213
|
-
console.log(AGENT_HELP);
|
|
308
|
+
console.log(renderHelp(AGENT_HELP));
|
|
214
309
|
return;
|
|
215
310
|
}
|
|
216
311
|
if (cmd === 'help' && sub === 'workspace') {
|
|
217
|
-
console.log(WORKSPACE_HELP);
|
|
312
|
+
console.log(renderHelp(WORKSPACE_HELP));
|
|
218
313
|
return;
|
|
219
314
|
}
|
|
220
315
|
if (cmd === 'help' && sub === 'deploy') {
|
|
221
|
-
console.log(DEPLOY_HELP);
|
|
316
|
+
console.log(renderHelp(DEPLOY_HELP));
|
|
222
317
|
return;
|
|
223
318
|
}
|
|
224
319
|
if (cmd === 'help' && ['promote', 'observe'].includes(sub || '')) {
|
|
225
|
-
console.log(PROMOTION_HELP);
|
|
320
|
+
console.log(renderHelp(PROMOTION_HELP));
|
|
226
321
|
return;
|
|
227
322
|
}
|
|
228
323
|
if (cmd === 'help' && sub) {
|
|
229
324
|
fail(`不认识的帮助主题: ${sub}`, 1, '运行 dta --help 回到产品入口;事件宿主可用 dta help runtime。');
|
|
230
325
|
}
|
|
231
326
|
if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
232
|
-
console.log(HELP);
|
|
327
|
+
console.log(renderHelp(HELP));
|
|
233
328
|
return;
|
|
234
329
|
}
|
|
235
330
|
if (cmd === '--version' || cmd === '-V' || cmd === 'version') {
|
|
@@ -245,10 +340,11 @@ async function main() {
|
|
|
245
340
|
workspace: { type: 'string' }, json: { type: 'boolean' },
|
|
246
341
|
'dry-run': { type: 'boolean' }, yes: { type: 'boolean' },
|
|
247
342
|
'plan-id': { type: 'string' }, 'no-wait': { type: 'boolean' },
|
|
343
|
+
wait: { type: 'boolean' },
|
|
248
344
|
status: { type: 'boolean' }, list: { type: 'boolean' },
|
|
249
345
|
execute: { type: 'boolean' }, 'operation-id': { type: 'string' },
|
|
250
346
|
timeout: { type: 'string' }, 'smoke-timeout': { type: 'string' },
|
|
251
|
-
retire: { type: 'boolean' },
|
|
347
|
+
retire: { type: 'boolean' }, 'rebind-runtime': { type: 'boolean' },
|
|
252
348
|
},
|
|
253
349
|
});
|
|
254
350
|
if (positionals.length)
|
|
@@ -265,16 +361,21 @@ async function main() {
|
|
|
265
361
|
const timeoutMs = parseOptionalTimeout(o.timeout, '--timeout');
|
|
266
362
|
const smokeTimeoutMs = parseOptionalTimeout(o['smoke-timeout'], '--smoke-timeout');
|
|
267
363
|
let out;
|
|
364
|
+
if (o.wait && o['no-wait'])
|
|
365
|
+
fail('deploy --wait 与 --no-wait 互斥');
|
|
366
|
+
if (o['rebind-runtime'] && o.retire)
|
|
367
|
+
fail('deploy --rebind-runtime 与 --retire 互斥');
|
|
268
368
|
if (o.list) {
|
|
269
|
-
if (o.execute || o['operation-id'] || o.retire || o['plan-id'] || o['no-wait'] ||
|
|
270
|
-
o.timeout || o['smoke-timeout']) {
|
|
369
|
+
if (o.execute || o['operation-id'] || o.retire || o['plan-id'] || o['no-wait'] || o.wait ||
|
|
370
|
+
o.timeout || o['smoke-timeout'] || o['rebind-runtime']) {
|
|
271
371
|
fail('deploy --list 只接受 --workspace 和 --json');
|
|
272
372
|
}
|
|
273
373
|
out = listMulticaDeployments(PROJECT_START, o.workspace, PACKAGE_JSON.version);
|
|
274
374
|
}
|
|
275
375
|
else if (o.status) {
|
|
276
|
-
if (o.retire || o['plan-id'] || o['no-wait'] || o['dry-run'] || o['smoke-timeout']
|
|
277
|
-
|
|
376
|
+
if (o.retire || o['plan-id'] || o['no-wait'] || o.wait || o['dry-run'] || o['smoke-timeout'] ||
|
|
377
|
+
o['rebind-runtime']) {
|
|
378
|
+
fail('deploy --status 不接受 retire/plan-id/no-wait/wait/dry-run/smoke-timeout/rebind-runtime');
|
|
278
379
|
}
|
|
279
380
|
if (Boolean(o.execute) !== Boolean(o.yes)) {
|
|
280
381
|
fail('deploy --status 远端 reconcile 必须同时传 --execute --yes');
|
|
@@ -285,24 +386,30 @@ async function main() {
|
|
|
285
386
|
});
|
|
286
387
|
}
|
|
287
388
|
else if (o['dry-run']) {
|
|
288
|
-
if (o.execute || o['operation-id'] || o['plan-id'] || o['no-wait'] ||
|
|
389
|
+
if (o.execute || o['operation-id'] || o['plan-id'] || o['no-wait'] || o.wait ||
|
|
289
390
|
o.timeout || o['smoke-timeout']) {
|
|
290
|
-
fail('deploy --dry-run 不接受 execute/operation-id/plan-id/no-wait/timeout/smoke-timeout');
|
|
391
|
+
fail('deploy --dry-run 不接受 execute/operation-id/plan-id/no-wait/wait/timeout/smoke-timeout');
|
|
291
392
|
}
|
|
292
393
|
out = planMulticaDeployment(PROJECT_START, o.workspace, {
|
|
293
394
|
action: o.retire ? 'retire' : 'apply', cliVersion: PACKAGE_JSON.version,
|
|
395
|
+
rebindRuntime: o['rebind-runtime'],
|
|
294
396
|
});
|
|
295
397
|
}
|
|
296
398
|
else {
|
|
297
399
|
if (o.execute || o['operation-id'] || o.status || o.list || o['dry-run']) {
|
|
298
400
|
fail('deploy apply 不接受 execute/operation-id/status/list/dry-run');
|
|
299
401
|
}
|
|
300
|
-
if (o.retire && (o['no-wait'] || o['smoke-timeout'])) {
|
|
301
|
-
fail('deploy --retire 不接受 no-wait/smoke-timeout');
|
|
402
|
+
if (o.retire && (o['no-wait'] || o.wait || o['smoke-timeout'])) {
|
|
403
|
+
fail('deploy --retire 不接受 no-wait/wait/smoke-timeout');
|
|
302
404
|
}
|
|
405
|
+
// --wait widens the in-band smoke-wait budget so one command reaches ready across a
|
|
406
|
+
// cold-start runtime; an explicit --smoke-timeout still wins. Default stays ~30s.
|
|
407
|
+
const effectiveSmokeTimeoutMs = smokeTimeoutMs !== undefined
|
|
408
|
+
? smokeTimeoutMs : o.wait ? 240_000 : undefined;
|
|
303
409
|
out = applyMulticaDeployment(PROJECT_START, o.workspace, {
|
|
304
410
|
yes: o.yes, planId: o['plan-id'], noWait: o['no-wait'],
|
|
305
|
-
timeoutMs, smokeTimeoutMs, cliVersion: PACKAGE_JSON.version,
|
|
411
|
+
timeoutMs, smokeTimeoutMs: effectiveSmokeTimeoutMs, cliVersion: PACKAGE_JSON.version,
|
|
412
|
+
rebindRuntime: o['rebind-runtime'],
|
|
306
413
|
}, o.retire ? 'retire' : 'apply');
|
|
307
414
|
}
|
|
308
415
|
if (o.json)
|
|
@@ -413,6 +520,106 @@ async function main() {
|
|
|
413
520
|
printProjectInfo(out);
|
|
414
521
|
return;
|
|
415
522
|
}
|
|
523
|
+
if (cmd === 'schedule') {
|
|
524
|
+
const sub = argv[1];
|
|
525
|
+
const { values: o, positionals } = parseArgs({
|
|
526
|
+
args: argv.slice(2), allowPositionals: true,
|
|
527
|
+
options: { json: { type: 'boolean' }, workspace: { type: 'string' } },
|
|
528
|
+
});
|
|
529
|
+
if (sub === 'show') {
|
|
530
|
+
const out = buildScheduleShow(PROJECT_START, PACKAGE_JSON.version);
|
|
531
|
+
// 有阻断问题 = 非 0 退出,json 和人类两条路都设,否则自动化用 --json 拿到 exit 0 照样部署 → fail-open。
|
|
532
|
+
// 判据来自 scheduleBlockers(含 unverified),不在这手写,避免和 plan 那边分叉。
|
|
533
|
+
if (scheduleBlockers(out).length)
|
|
534
|
+
process.exitCode = 2;
|
|
535
|
+
if (o.json) {
|
|
536
|
+
console.log(JSON.stringify(out, null, 2));
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (out.count === 0) {
|
|
540
|
+
console.log(muted('本 Project 未声明任何常驻节律(schedules)。'));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
console.log(`${strong('声明的常驻节律(本地 manifest 声明,非线上实态)')} · ${out.count} 条`);
|
|
544
|
+
for (const s of out.schedules) {
|
|
545
|
+
const metered = s.mode === 'work-item' ? '记账' : '不记账';
|
|
546
|
+
console.log(`\n ${noun(s.name)} ${s.cron} ${s.timezone} ${muted(`[${s.mode}·${metered}]`)}`);
|
|
547
|
+
if (s.wake)
|
|
548
|
+
console.log(muted(` 唤醒 ${s.wake}`));
|
|
549
|
+
if (s.completion)
|
|
550
|
+
console.log(muted(` ②判据 ${s.completion}`));
|
|
551
|
+
}
|
|
552
|
+
if (out.collidingPairs.length) {
|
|
553
|
+
console.log(`\n${mark('fail', `✗ 同刻并发: ${out.collidingPairs.join('、')} —— 外发拍同一时刻 fire 会重复送第三方,请错开`)}`);
|
|
554
|
+
}
|
|
555
|
+
if (out.missingCompletion.length) {
|
|
556
|
+
console.log(`${mark('fail', `✗ 缺完成判据: ${out.missingCompletion.join('、')} —— 没有数据派生的 completion,只能靠"跑没跑"判完成,漏拍就永久丢一期`)}`);
|
|
557
|
+
}
|
|
558
|
+
if (out.unverifiedPairs.length) {
|
|
559
|
+
console.log(`${mark('fail', `✗ 证不出是否同刻: ${out.unverifiedPairs.join('、')} —— 跨时区或 cron 扩展语法本地读不懂,一律当撞车挡住(0 9 * * * @Asia/Shanghai 和 0 1 * * * @UTC 就是同一时刻);把两条写成同一个 timezone,或换掉扩展语法`)}`);
|
|
560
|
+
}
|
|
561
|
+
for (const issue of out.completionRefIssues) {
|
|
562
|
+
console.log(`${mark('fail', `✗ 判据引用: ${issue}`)}`);
|
|
563
|
+
}
|
|
564
|
+
// 绿灯条件必须和退出码同源(scheduleBlockers),否则新增一类 blocker 时
|
|
565
|
+
// 人类输出继续报绿而 exit 2——手工操作者只看文案就会照常部署(issue #38)。
|
|
566
|
+
if (!scheduleBlockers(out).length) {
|
|
567
|
+
console.log(`\n${mark('pass', '✓ 各节律不同刻 fire、完成判据齐备且引用合法')}`);
|
|
568
|
+
}
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (sub === 'plan') {
|
|
572
|
+
const workspaceName = o.workspace || positionals[0];
|
|
573
|
+
if (!workspaceName)
|
|
574
|
+
fail('schedule plan 需要 --workspace <name>');
|
|
575
|
+
const info = inspectAgentProject(PROJECT_START, PACKAGE_JSON.version);
|
|
576
|
+
const plan = buildSchedulePlan(info, workspaceName);
|
|
577
|
+
// fail closed(json 和人类两条路都设):plan 是 operator 真正照着部署的路径,
|
|
578
|
+
// 撞车不在这拦,两条会各建一个 autopilot → 重复外发。判据同 show,见 scheduleBlockers。
|
|
579
|
+
if (scheduleBlockers(plan).length)
|
|
580
|
+
process.exitCode = 2;
|
|
581
|
+
if (o.json) {
|
|
582
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
console.log(`${strong('schedule 部署计划')} · workspace=${noun(plan.workspace)} · provider=${plan.provider}`);
|
|
586
|
+
if (!plan.supported) {
|
|
587
|
+
console.log(`\n${mark('fail', `✗ 不支持: ${plan.reason}`)}`);
|
|
588
|
+
console.log(` ${noun(plan.reconcile)}`);
|
|
589
|
+
process.exitCode = 2;
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
// 阻断时不打印任何可执行命令:手工操作者会复制粘贴人类输出里的命令,
|
|
593
|
+
// 「成功版面 + 可执行命令 + exit 2」等于把 fail-closed 只留给脚本(issue #38)。
|
|
594
|
+
const blocked = scheduleBlockers(plan).length > 0;
|
|
595
|
+
if (!blocked) {
|
|
596
|
+
console.log(` ${plan.count} 条声明 → provider ops:\n`);
|
|
597
|
+
for (const s of plan.schedules) {
|
|
598
|
+
console.log(` ${muted(`# ${s.name} [${s.metered ? '记账' : '不记账·平台看不到成本'}]`)}\n ${noun(s.applyCommand)}\n`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
console.log(`\n${mark('fail', `✗ 已阻断:manifest 存在必须先修复的问题,本计划不可执行(apply 命令已隐藏)`)}`);
|
|
603
|
+
}
|
|
604
|
+
if (plan.collidingPairs.length) {
|
|
605
|
+
console.log(`${mark('fail', `✗ 同刻并发: ${plan.collidingPairs.join('、')} —— 别照此部署,两条会各建一个 autopilot → 重复外发;先错开 fire 时刻`)}`);
|
|
606
|
+
}
|
|
607
|
+
if (plan.missingCompletion.length) {
|
|
608
|
+
console.log(`${mark('fail', `✗ 缺完成判据: ${plan.missingCompletion.join('、')} —— 补上数据派生的 completion,别靠"跑没跑"判完成`)}`);
|
|
609
|
+
}
|
|
610
|
+
if (plan.unverifiedPairs.length) {
|
|
611
|
+
console.log(`${mark('fail', `✗ 证不出是否同刻: ${plan.unverifiedPairs.join('、')} —— 别照此部署。跨时区或扩展语法本地判不了,一律当撞车;把两条写成同一个 timezone 再 plan,判得出不撞就自动放行`)}`);
|
|
612
|
+
}
|
|
613
|
+
for (const issue of plan.completionRefIssues) {
|
|
614
|
+
console.log(`${mark('fail', `✗ 判据引用: ${issue}`)}`);
|
|
615
|
+
}
|
|
616
|
+
if (!blocked)
|
|
617
|
+
console.log(`执行: ${noun(plan.reconcile)}`);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
fail(`可用: dta schedule show | plan --workspace <name>${suggest(sub || '', ['show', 'plan'], 'dta schedule')}`);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
416
623
|
if (cmd === 'workspace') {
|
|
417
624
|
const { values: o, positionals } = parseArgs({
|
|
418
625
|
args: argv.slice(2), allowPositionals: true,
|
|
@@ -423,6 +630,8 @@ async function main() {
|
|
|
423
630
|
cases: { type: 'string' }, lanes: { type: 'string' }, runs: { type: 'string' },
|
|
424
631
|
out: { type: 'string' }, timeout: { type: 'string' },
|
|
425
632
|
'previous-skill': { type: 'string' },
|
|
633
|
+
status: { type: 'boolean' }, 'run-id': { type: 'string' },
|
|
634
|
+
provider: { type: 'string' }, stage: { type: 'string' }, profile: { type: 'string' },
|
|
426
635
|
},
|
|
427
636
|
});
|
|
428
637
|
if (sub === 'list') {
|
|
@@ -544,16 +753,46 @@ async function main() {
|
|
|
544
753
|
return;
|
|
545
754
|
}
|
|
546
755
|
if (sub === 'create') {
|
|
547
|
-
rejectWorkspaceOptions(o, ['json', 'yes', 'dry-run'], 'workspace create');
|
|
548
756
|
if (positionals.length !== 1)
|
|
549
757
|
fail('workspace create 需要且只接受一个 Workspace name');
|
|
550
758
|
if (o.yes && o['dry-run'])
|
|
551
759
|
fail('workspace create 的 --dry-run 与 --yes 不能同时使用');
|
|
760
|
+
const wsName = positionals[0];
|
|
761
|
+
// Resolve the provider kind to scaffold/build: explicit --provider wins, else an already
|
|
762
|
+
// declared Workspace keeps its kind, else the attributed platform's provider (registry-
|
|
763
|
+
// driven so deap inherits it), else opencode. multica has no local host — create authors
|
|
764
|
+
// its config entry; opencode keeps building the managed host.
|
|
765
|
+
if (o.provider && !['opencode', 'multica'].includes(String(o.provider))) {
|
|
766
|
+
fail('workspace create --provider 只支持 opencode 或 multica');
|
|
767
|
+
}
|
|
768
|
+
const declaredWs = inspectAgentProject(PROJECT_START, PACKAGE_JSON.version)
|
|
769
|
+
.workspaces.find((item) => item.name === wsName);
|
|
770
|
+
const platformProvider = resolveAgentPlatform(PROJECT_START).definition?.provider;
|
|
771
|
+
const kind = String(o.provider || declaredWs?.provider.kind || platformProvider || 'opencode');
|
|
772
|
+
if (declaredWs && o.provider && declaredWs.provider.kind !== kind) {
|
|
773
|
+
fail(`Workspace ${wsName} 已声明为 ${declaredWs.provider.kind},与 --provider ${kind} 冲突`);
|
|
774
|
+
}
|
|
775
|
+
if (kind === 'multica') {
|
|
776
|
+
rejectWorkspaceOptions(o, ['json', 'yes', 'dry-run', 'provider', 'stage', 'profile'], 'workspace create');
|
|
777
|
+
const out = scaffoldMulticaWorkspace(PROJECT_START, wsName, {
|
|
778
|
+
stage: o.stage, profile: o.profile, apply: o.yes, cliVersion: PACKAGE_JSON.version,
|
|
779
|
+
});
|
|
780
|
+
if (o.json)
|
|
781
|
+
console.log(JSON.stringify(out, null, 2));
|
|
782
|
+
else {
|
|
783
|
+
const tag = out.alreadyDeclared ? '已存在' : out.applied ? '已写入' : 'DRY-RUN';
|
|
784
|
+
console.log(`${tag} workspace ${out.workspace} · provider=multica · profile=${out.profile}`);
|
|
785
|
+
for (const step of out.nextSteps)
|
|
786
|
+
console.log(` ${step}`);
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
rejectWorkspaceOptions(o, ['json', 'yes', 'dry-run', 'provider'], 'workspace create');
|
|
552
791
|
const out = o.yes
|
|
553
|
-
? applyOpenCodeWorkspace(PROJECT_START,
|
|
792
|
+
? applyOpenCodeWorkspace(PROJECT_START, wsName, {
|
|
554
793
|
yes: true, cliVersion: PACKAGE_JSON.version,
|
|
555
794
|
})
|
|
556
|
-
: planOpenCodeWorkspace(PROJECT_START,
|
|
795
|
+
: planOpenCodeWorkspace(PROJECT_START, wsName, PACKAGE_JSON.version);
|
|
557
796
|
if (o.json)
|
|
558
797
|
console.log(JSON.stringify(out, null, 2));
|
|
559
798
|
else
|
|
@@ -568,30 +807,65 @@ async function main() {
|
|
|
568
807
|
if (o.json)
|
|
569
808
|
console.log(JSON.stringify(out, null, 2));
|
|
570
809
|
else
|
|
571
|
-
console.log(
|
|
810
|
+
console.log(`${mark('pass', '✅')} current workspace: ${muted(out.previous || '-')} → ${noun(out.current)}`);
|
|
572
811
|
return;
|
|
573
812
|
}
|
|
574
813
|
if (sub === 'run') {
|
|
814
|
+
const timeoutMs = o.timeout === undefined ? undefined : Number(o.timeout);
|
|
815
|
+
if (o.status) {
|
|
816
|
+
// Resume a timed-out multica run by re-reading the SAME Issue; no new Issue is created.
|
|
817
|
+
rejectWorkspaceOptions(o, ['json', 'yes', 'execute', 'out', 'timeout', 'status', 'run-id'], 'workspace run --status');
|
|
818
|
+
if (positionals.length !== 1)
|
|
819
|
+
fail('workspace run --status 需要一个 Workspace name');
|
|
820
|
+
if (o['prompt-file'])
|
|
821
|
+
fail('workspace run --status 不接受 --prompt-file;用 --run-id 续读');
|
|
822
|
+
if (!o['run-id'])
|
|
823
|
+
fail('workspace run --status 需要 --run-id <上次 run 的 runId>');
|
|
824
|
+
const out = statusMulticaWorkspaceRun(PROJECT_START, positionals[0], {
|
|
825
|
+
runId: o['run-id'], execute: o.execute, yes: o.yes, timeoutMs, out: o.out,
|
|
826
|
+
cliVersion: PACKAGE_JSON.version,
|
|
827
|
+
});
|
|
828
|
+
if (o.json)
|
|
829
|
+
console.log(JSON.stringify(out, null, 2));
|
|
830
|
+
else {
|
|
831
|
+
console.log(`${out.passed ? '✅' : '❌'} workspace run ${out.runId} (resumed)`);
|
|
832
|
+
if (out.answer)
|
|
833
|
+
console.log(out.answer);
|
|
834
|
+
}
|
|
835
|
+
if (!out.passed)
|
|
836
|
+
process.exitCode = 2;
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
575
839
|
rejectWorkspaceOptions(o, ['json', 'yes', 'execute', 'prompt-file', 'out', 'timeout'], 'workspace run');
|
|
576
840
|
if (positionals.length !== 1 || !o['prompt-file']) {
|
|
577
841
|
fail('workspace run 需要一个 Workspace name 和 --prompt-file <file>');
|
|
578
842
|
}
|
|
579
|
-
const timeoutMs = o.timeout === undefined ? undefined : Number(o.timeout);
|
|
580
843
|
const prompt = readProjectText(PROJECT_START, o['prompt-file'], 'workspace run prompt');
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
844
|
+
const workspace = showDevelopmentWorkspace(PROJECT_START, positionals[0], PACKAGE_JSON.version);
|
|
845
|
+
const out = workspace.provider.kind === 'multica'
|
|
846
|
+
? runMulticaWorkspaceIssue(PROJECT_START, positionals[0], prompt, {
|
|
847
|
+
execute: o.execute, yes: o.yes, timeoutMs, out: o.out,
|
|
848
|
+
cliVersion: PACKAGE_JSON.version,
|
|
849
|
+
})
|
|
850
|
+
: runOpenCodeWorkspace(PROJECT_START, positionals[0], prompt, {
|
|
851
|
+
execute: o.execute, yes: o.yes, timeoutMs, out: o.out,
|
|
852
|
+
});
|
|
584
853
|
if (o.json)
|
|
585
854
|
console.log(JSON.stringify(out, null, 2));
|
|
586
855
|
else if (out.$schema === 'dingtalk-agent/opencode-workspace-run-plan@1') {
|
|
587
|
-
console.log(
|
|
856
|
+
console.log(`${mark('muted', 'DRY-RUN')} workspace=${noun(out.workspace)} model=${out.model} prompt=${out.promptHash}`);
|
|
857
|
+
}
|
|
858
|
+
else if (out.$schema === 'dingtalk-agent/multica-workspace-run-plan@1') {
|
|
859
|
+
console.log(`${mark('muted', 'DRY-RUN')} workspace=${noun(out.workspace)} agent=${out.agentId || '-'} prompt=${out.promptHash}`);
|
|
588
860
|
}
|
|
589
861
|
else {
|
|
590
|
-
console.log(`${out.passed ? '✅' : '❌'} workspace run ${out.runId}`);
|
|
591
|
-
if (out.execution?.answer)
|
|
862
|
+
console.log(`${mark(levelOf(Boolean(out.passed)), out.passed ? '✅' : '❌')} workspace run ${noun(out.runId)}`);
|
|
863
|
+
if ('execution' in out && out.execution?.answer)
|
|
592
864
|
console.log(out.execution.answer);
|
|
865
|
+
if ('answer' in out && out.answer)
|
|
866
|
+
console.log(out.answer);
|
|
593
867
|
if (out.evidencePath)
|
|
594
|
-
console.log(`evidence=${out.evidencePath}`);
|
|
868
|
+
console.log(`evidence=${noun(out.evidencePath)}`);
|
|
595
869
|
}
|
|
596
870
|
if (o.execute && !out.passed)
|
|
597
871
|
process.exitCode = 2;
|
|
@@ -660,23 +934,72 @@ async function main() {
|
|
|
660
934
|
return;
|
|
661
935
|
}
|
|
662
936
|
if (cmd === 'upgrade') {
|
|
937
|
+
// parseArgs strict 模式会把 --help 当成未知参数抛掉,必须先拦。
|
|
938
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
939
|
+
console.log(renderHelp(UPGRADE_HELP));
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
663
942
|
const { values: o } = parseArgs({
|
|
664
943
|
args: argv.slice(1),
|
|
665
944
|
options: {
|
|
666
945
|
channel: { type: 'string' }, version: { type: 'string' }, prefix: { type: 'string' },
|
|
667
946
|
'dry-run': { type: 'boolean' }, force: { type: 'boolean' }, json: { type: 'boolean' },
|
|
947
|
+
check: { type: 'boolean' }, yes: { type: 'boolean' },
|
|
668
948
|
},
|
|
669
949
|
});
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
950
|
+
const channel = o.channel;
|
|
951
|
+
const json = Boolean(o.json);
|
|
952
|
+
if (o.check) {
|
|
953
|
+
if (o['dry-run'] || o.force || o.prefix || o.yes) {
|
|
954
|
+
fail('upgrade --check 只接受 --channel / --version / --json');
|
|
955
|
+
}
|
|
956
|
+
const checked = checkUpgrade(PACKAGE_ROOT, { channel, version: o.version });
|
|
957
|
+
if (json)
|
|
958
|
+
console.log(JSON.stringify(checked, null, 2));
|
|
959
|
+
else
|
|
960
|
+
printUpgradeCheck(checked);
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
// --json 是机器通道:结构性绕开 tui,连回调都不构造。
|
|
964
|
+
// 非交互(管道/CI/Agent)也不提示,保持"敲下去就装"的既有行为。
|
|
965
|
+
const interactive = !json && Boolean(process.stdin.isTTY);
|
|
966
|
+
let known = null;
|
|
967
|
+
if (interactive) {
|
|
968
|
+
known = checkUpgrade(PACKAGE_ROOT, { channel, version: o.version });
|
|
969
|
+
printUpgradePreamble(known, { dryRun: Boolean(o['dry-run']), force: Boolean(o.force) });
|
|
970
|
+
const proceed = await confirm(o['dry-run'] ? '是否预览升级?' : '是否升级?', { yes: Boolean(o.yes), fallback: true });
|
|
971
|
+
if (!proceed) {
|
|
972
|
+
console.error(stderrUi().theme.dim(' 已取消'));
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
const reporter = json ? null : new StepReporter({ total: o['dry-run'] ? 3 : 7 });
|
|
977
|
+
const result = upgradeAgent(PACKAGE_ROOT, {
|
|
978
|
+
channel, version: o.version, prefix: o.prefix,
|
|
673
979
|
dryRun: o['dry-run'], force: o.force,
|
|
980
|
+
// 已经解析过就别再问一次 registry;**同时把安装源钉死到精确版本**——
|
|
981
|
+
// 只冻结期望版本而仍按 `pkg@latest` 安装,会在用户思考的这几秒里
|
|
982
|
+
// 被移动的 tag 装进另一个版本,然后按旧期望校验失败,而机器已经改了。
|
|
983
|
+
env: known
|
|
984
|
+
? {
|
|
985
|
+
DTA_UPGRADE_TARGET_VERSION: known.targetVersion,
|
|
986
|
+
DTA_UPGRADE_PACKAGE_SOURCE: known.pinnedSource,
|
|
987
|
+
}
|
|
988
|
+
: undefined,
|
|
989
|
+
onEvent: reporter
|
|
990
|
+
? (event) => {
|
|
991
|
+
if (event.phase === 'begin')
|
|
992
|
+
reporter.begin(event.label);
|
|
993
|
+
else
|
|
994
|
+
reporter.end(event.ok !== false, event.meta || '');
|
|
995
|
+
}
|
|
996
|
+
: undefined,
|
|
674
997
|
});
|
|
675
|
-
if (
|
|
676
|
-
console.log(JSON.stringify(
|
|
998
|
+
if (json)
|
|
999
|
+
console.log(JSON.stringify(result, null, 2));
|
|
677
1000
|
else
|
|
678
|
-
printUpgrade(
|
|
679
|
-
if (!
|
|
1001
|
+
printUpgrade(result);
|
|
1002
|
+
if (!result.dryRun && !result.ready)
|
|
680
1003
|
process.exitCode = 2;
|
|
681
1004
|
return;
|
|
682
1005
|
}
|
|
@@ -719,9 +1042,16 @@ async function main() {
|
|
|
719
1042
|
}
|
|
720
1043
|
if (o.json)
|
|
721
1044
|
console.log(JSON.stringify(out, null, 2));
|
|
722
|
-
else if ('skills' in out)
|
|
1045
|
+
else if ('skills' in out) {
|
|
723
1046
|
for (const item of out.skills)
|
|
724
1047
|
printSkillStatus(item, sub, dryRun);
|
|
1048
|
+
// 静默删目录会让人以为什么都没发生;说清楚删了哪几个旧名。
|
|
1049
|
+
for (const item of out.prunedLegacy || []) {
|
|
1050
|
+
console.log(item.replacedBy
|
|
1051
|
+
? ` 已迁移 ${item.name} → ${item.replacedBy}`
|
|
1052
|
+
: ` 已清理停用的 Skill ${item.name}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
725
1055
|
else
|
|
726
1056
|
printSkillStatus(out, sub, dryRun);
|
|
727
1057
|
return;
|
|
@@ -776,7 +1106,7 @@ async function main() {
|
|
|
776
1106
|
if (o.json)
|
|
777
1107
|
console.log(JSON.stringify({ ...out, endpoint, readiness, targets, doc }, null, 2));
|
|
778
1108
|
else {
|
|
779
|
-
console.log(
|
|
1109
|
+
console.log(`${mark('pass', '✅')} 工作区已归属 ${strong(out.definition?.label)}(写入 ${noun(out.written)})`);
|
|
780
1110
|
for (const install of out.skillInstalls)
|
|
781
1111
|
printSkillStatus(install, 'install', false);
|
|
782
1112
|
if (!out.skillInstalls.length) {
|
|
@@ -816,14 +1146,14 @@ async function main() {
|
|
|
816
1146
|
if (o.json)
|
|
817
1147
|
console.log(JSON.stringify(out, null, 2));
|
|
818
1148
|
else {
|
|
819
|
-
console.log(
|
|
1149
|
+
console.log(`${mark('pass', '✅')} ${strong(out.mode === 'workspace' ? '已装载 Workspace' : '直接会话模式')}`);
|
|
820
1150
|
console.log(` scope=${out.scopeId} initialized=${out.initialized}`);
|
|
821
1151
|
console.log(` agent=${out.definition.id} definition=${out.definition.status}`);
|
|
822
1152
|
if (!out.mounts.length)
|
|
823
|
-
console.log(' 没有发现持久上下文;这不是错误,也不需要自动 init。');
|
|
1153
|
+
console.log(muted(' 没有发现持久上下文;这不是错误,也不需要自动 init。'));
|
|
824
1154
|
for (const mount of out.mounts)
|
|
825
|
-
console.log(` ${mount.slot}: ${mount.path}`);
|
|
826
|
-
console.log(` ${out.note}`);
|
|
1155
|
+
console.log(` ${mount.slot}: ${noun(mount.path)}`);
|
|
1156
|
+
console.log(` ${muted(out.note)}`);
|
|
827
1157
|
}
|
|
828
1158
|
return;
|
|
829
1159
|
}
|
|
@@ -877,6 +1207,7 @@ async function main() {
|
|
|
877
1207
|
'state-dir': { type: 'string' }, profile: { type: 'string' },
|
|
878
1208
|
'expected-user-id': { type: 'string' }, 'require-skill': { type: 'string' },
|
|
879
1209
|
'verify-load': { type: 'boolean' }, yes: { type: 'boolean' },
|
|
1210
|
+
'strict-single-run': { type: 'boolean' },
|
|
880
1211
|
model: { type: 'string' }, out: { type: 'string' },
|
|
881
1212
|
'load-report': { type: 'string' }, 'remote-report': { type: 'string' },
|
|
882
1213
|
json: { type: 'boolean' },
|
|
@@ -893,6 +1224,7 @@ async function main() {
|
|
|
893
1224
|
bindings: loaded ? { path: loaded.path, hash: loaded.hash } : undefined,
|
|
894
1225
|
requiredSkills: o['require-skill']?.split(','),
|
|
895
1226
|
verifyLoad: o['verify-load'], yes: o.yes, model: o.model, out: o.out,
|
|
1227
|
+
strictSingleRun: o['strict-single-run'],
|
|
896
1228
|
loadReport: o['load-report'], remoteReport: o['remote-report'],
|
|
897
1229
|
});
|
|
898
1230
|
if (o.json)
|
|
@@ -1209,9 +1541,9 @@ async function main() {
|
|
|
1209
1541
|
if (o.json)
|
|
1210
1542
|
console.log(JSON.stringify(out, null, 2));
|
|
1211
1543
|
else if (!out.found)
|
|
1212
|
-
console.log('当前 Session 尚无 Task checkpoint。');
|
|
1544
|
+
console.log(muted('当前 Session 尚无 Task checkpoint。'));
|
|
1213
1545
|
else {
|
|
1214
|
-
console.log(
|
|
1546
|
+
console.log(`${mark('pass', '✅')} Task ${noun(out.checkpoint.taskId)} · ${out.checkpoint.status}`);
|
|
1215
1547
|
console.log(` revision=${out.checkpoint.revision} hash=${out.hash}`);
|
|
1216
1548
|
console.log(` next=${out.checkpoint.nextAction || '-'}`);
|
|
1217
1549
|
}
|
|
@@ -1236,7 +1568,7 @@ async function main() {
|
|
|
1236
1568
|
if (o.json || !o.live)
|
|
1237
1569
|
console.log(JSON.stringify(out, null, 2));
|
|
1238
1570
|
else {
|
|
1239
|
-
console.log(`${out.verified ? '✅' : '⚠️'} Operational Memory · ${out.mode}`);
|
|
1571
|
+
console.log(`${mark(out.verified ? 'pass' : 'warn', out.verified ? '✅' : '⚠️')} ${strong('Operational Memory')} · ${out.mode}`);
|
|
1240
1572
|
console.log(` record=${out.recordId || '-'} verification=${out.verification}`);
|
|
1241
1573
|
}
|
|
1242
1574
|
if (out.mode === 'uncertain')
|
|
@@ -1290,18 +1622,78 @@ async function main() {
|
|
|
1290
1622
|
if (o.json)
|
|
1291
1623
|
console.log(JSON.stringify(out, null, 2));
|
|
1292
1624
|
else if (action === 'publish') {
|
|
1293
|
-
console.log(`${out.verified ? '✅' : out.mode === 'dry-run' ? '🔎' : '⚠️'} Candidate ${o.id}`);
|
|
1625
|
+
console.log(`${mark(out.verified ? 'pass' : out.mode === 'dry-run' ? 'muted' : 'warn', out.verified ? '✅' : out.mode === 'dry-run' ? '🔎' : '⚠️')} Candidate ${noun(o.id)}`);
|
|
1294
1626
|
console.log(` mode=${out.mode} targetHash=${out.currentTargetHash}`);
|
|
1295
1627
|
}
|
|
1296
1628
|
else {
|
|
1297
|
-
console.log(
|
|
1298
|
-
console.log(` revision=${out.candidate.revision} path=${out.path}`);
|
|
1629
|
+
console.log(`${mark('pass', '✅')} Candidate ${noun(out.candidate.id)} · ${out.candidate.status}`);
|
|
1630
|
+
console.log(` revision=${out.candidate.revision} path=${noun(out.path)}`);
|
|
1299
1631
|
}
|
|
1300
1632
|
if (action === 'publish' && out.mode === 'uncertain')
|
|
1301
1633
|
process.exitCode = 2;
|
|
1302
1634
|
return;
|
|
1303
1635
|
}
|
|
1304
|
-
|
|
1636
|
+
if (sub === 'noop') {
|
|
1637
|
+
const { values: o } = parseArgs({
|
|
1638
|
+
args: argv.slice(3),
|
|
1639
|
+
options: {
|
|
1640
|
+
input: { type: 'string' }, scope: { type: 'string' }, step: { type: 'string' },
|
|
1641
|
+
occurrence: { type: 'string' }, 'state-dir': { type: 'string' },
|
|
1642
|
+
session: { type: 'string' }, json: { type: 'boolean' },
|
|
1643
|
+
},
|
|
1644
|
+
});
|
|
1645
|
+
const noopCfg = cfgmod.load(ROOT);
|
|
1646
|
+
const options = {
|
|
1647
|
+
stateDir: o['state-dir'], runPath: process.env.DTA_RUN,
|
|
1648
|
+
sessionPath: o.session || process.env.DTA_SESSION,
|
|
1649
|
+
sessionRoot: noopCfg?.runtime?.sessionRoot,
|
|
1650
|
+
};
|
|
1651
|
+
if (action === 'record') {
|
|
1652
|
+
if (!o.input)
|
|
1653
|
+
fail('memory noop record 需要 --input(memory-noop-claim@1 JSON)');
|
|
1654
|
+
const out = recordMemoryNoop(ROOT, readEventFile(o.input), options);
|
|
1655
|
+
if (o.json)
|
|
1656
|
+
console.log(JSON.stringify(out, null, 2));
|
|
1657
|
+
else {
|
|
1658
|
+
console.log(`${mark('pass', '✅')} No-op Receipt ${noun(out.receipt.id)}${out.duplicate ? ' · duplicate(幂等命中既有痕迹)' : ''}`);
|
|
1659
|
+
console.log(` step=${out.receipt.step} occurrence=${out.receipt.occurrence} reason=${out.receipt.reason}`);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
else if (action === 'list') {
|
|
1663
|
+
const out = listMemoryNoops(ROOT, { scopeId: o.scope, step: o.step, occurrence: o.occurrence }, options);
|
|
1664
|
+
if (o.json)
|
|
1665
|
+
console.log(JSON.stringify(out, null, 2));
|
|
1666
|
+
else {
|
|
1667
|
+
console.log(`${mark('pass', '✅')} No-op Receipts · ${out.count} 条`);
|
|
1668
|
+
for (const receipt of out.receipts) {
|
|
1669
|
+
console.log(` ${receipt.id} ${receipt.step} ${receipt.occurrence} ${receipt.reason}`);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
else if (action === 'trace') {
|
|
1674
|
+
if (!o.scope || !o.occurrence)
|
|
1675
|
+
fail('memory noop trace 需要 --scope 和 --occurrence');
|
|
1676
|
+
const out = traceMemoryNoop(ROOT, { scopeId: o.scope, occurrence: o.occurrence, step: o.step }, options);
|
|
1677
|
+
if (o.json)
|
|
1678
|
+
console.log(JSON.stringify(out, null, 2));
|
|
1679
|
+
else {
|
|
1680
|
+
const ran = out.verdict === 'ran-empty';
|
|
1681
|
+
console.log(`${mark(ran ? 'pass' : 'warn', ran ? '✅' : '⚠️')} ${strong(out.verdict)} · step=${out.step} occurrence=${noun(out.occurrence)}`);
|
|
1682
|
+
if (ran)
|
|
1683
|
+
console.log(` receipt=${out.receiptId}`);
|
|
1684
|
+
else
|
|
1685
|
+
console.log(' 没有任何痕迹——「判过无内容」会留 no-op Receipt,这里连它都没有');
|
|
1686
|
+
}
|
|
1687
|
+
// 判存步骤没跑就是没跑:给脚本一个可断言的非零退出码(区别于 uncertain 的 2)。
|
|
1688
|
+
if (out.verdict === 'no-trace')
|
|
1689
|
+
process.exitCode = 3;
|
|
1690
|
+
}
|
|
1691
|
+
else {
|
|
1692
|
+
fail('memory noop 的子命令: record / list / trace');
|
|
1693
|
+
}
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
fail('memory 当前支持: operational upsert / candidate propose|show|review|publish / noop record|list|trace');
|
|
1305
1697
|
}
|
|
1306
1698
|
// ── act:元语化动作;目标/身份/预算/状态转移都由 Run 固定 ──
|
|
1307
1699
|
if (cmd === 'act') {
|
|
@@ -1326,10 +1718,10 @@ async function main() {
|
|
|
1326
1718
|
if (o.json || o['dry-run'])
|
|
1327
1719
|
console.log(JSON.stringify(out, null, 2));
|
|
1328
1720
|
else {
|
|
1329
|
-
console.log(`${out.verified ? '✅' : '⚠️'} ${out.kind} · ${out.outcome} · ${out.toState}`);
|
|
1330
|
-
console.log(` action=${out.actionId} run=${out.runId}`);
|
|
1721
|
+
console.log(`${mark(out.verified ? 'pass' : 'warn', out.verified ? '✅' : '⚠️')} ${strong(out.kind)} · ${out.outcome} · ${out.toState}`);
|
|
1722
|
+
console.log(` action=${noun(out.actionId)} run=${noun(out.runId)}`);
|
|
1331
1723
|
if (!out.verified)
|
|
1332
|
-
console.log(` ${out.verification}`);
|
|
1724
|
+
console.log(` ${muted(out.verification)}`);
|
|
1333
1725
|
}
|
|
1334
1726
|
if (out.outcome === 'uncertain')
|
|
1335
1727
|
process.exitCode = 2;
|
|
@@ -1401,11 +1793,41 @@ function editDistance(a, b) {
|
|
|
1401
1793
|
}
|
|
1402
1794
|
return row[b.length];
|
|
1403
1795
|
}
|
|
1796
|
+
/** 按语义给状态标记上色;字形原样返回,不做任何替换。 */
|
|
1797
|
+
function mark(level, text) {
|
|
1798
|
+
const { theme } = stdoutUi();
|
|
1799
|
+
return level === 'pass' ? theme.success(text)
|
|
1800
|
+
: level === 'warn' ? theme.warn(text)
|
|
1801
|
+
: level === 'fail' ? theme.danger(text)
|
|
1802
|
+
: theme.dim(text);
|
|
1803
|
+
}
|
|
1804
|
+
/** 可复制的名词:路径、版本号、ID、命令。 */
|
|
1805
|
+
function noun(text) {
|
|
1806
|
+
return stdoutUi().theme.noun(text);
|
|
1807
|
+
}
|
|
1808
|
+
/** 次要信息:补充说明、括注、下一步提示。 */
|
|
1809
|
+
function muted(text) {
|
|
1810
|
+
return stdoutUi().theme.dim(text);
|
|
1811
|
+
}
|
|
1812
|
+
/** 标题行 / key=value 的结构性强调。 */
|
|
1813
|
+
function strong(text) {
|
|
1814
|
+
return stdoutUi().theme.bold(text);
|
|
1815
|
+
}
|
|
1816
|
+
/** ready 布尔 → 语义级别,printX 里到处都是这个三态。 */
|
|
1817
|
+
function levelOf(state) {
|
|
1818
|
+
if (state === true)
|
|
1819
|
+
return 'pass';
|
|
1820
|
+
if (state === false)
|
|
1821
|
+
return 'fail';
|
|
1822
|
+
return state;
|
|
1823
|
+
}
|
|
1404
1824
|
function printDispatch(out, json = false) {
|
|
1405
1825
|
if (json)
|
|
1406
1826
|
return console.log(JSON.stringify(out, null, 2));
|
|
1407
|
-
const
|
|
1408
|
-
|
|
1827
|
+
const state = out.duplicate
|
|
1828
|
+
? mark(out.launch ? 'warn' : 'muted', out.launch ? '🔁 REDELIVER' : '↩️ DUPLICATE')
|
|
1829
|
+
: mark('pass', '✅ READY');
|
|
1830
|
+
console.log(`${state} ${noun(out.runId)}`);
|
|
1409
1831
|
console.log(` Field=${out.fieldId} Session=${out.sessionId} Event=${out.eventKind}`);
|
|
1410
1832
|
console.log(` cwd=${out.cwd}`);
|
|
1411
1833
|
console.log(` entrypoint=${out.entrypoint} queue=${out.queue.key} (max=${out.queue.maxConcurrency})`);
|
|
@@ -1415,7 +1837,7 @@ function printDispatch(out, json = false) {
|
|
|
1415
1837
|
}
|
|
1416
1838
|
function printLab(out) {
|
|
1417
1839
|
const passed = out.ready ?? out.passed ?? out.assertions?.passed;
|
|
1418
|
-
console.log(`${passed === false ? '❌' : '✅'} ${out.$schema || 'dingtalk-agent/lab'}`);
|
|
1840
|
+
console.log(`${mark(levelOf(passed !== false), passed === false ? '❌' : '✅')} ${out.$schema || 'dingtalk-agent/lab'}`);
|
|
1419
1841
|
if (out.mode)
|
|
1420
1842
|
console.log(` mode=${out.mode}`);
|
|
1421
1843
|
if (out.marker)
|
|
@@ -1424,17 +1846,33 @@ function printLab(out) {
|
|
|
1424
1846
|
console.log(` evidence=${out.evidencePath}`);
|
|
1425
1847
|
if (Array.isArray(out.checks)) {
|
|
1426
1848
|
for (const check of out.checks) {
|
|
1427
|
-
console.log(` ${check.passed ? 'PASS' : 'FAIL'} ${check.id}: ${check.detail}`);
|
|
1849
|
+
console.log(` ${mark(levelOf(Boolean(check.passed)), check.passed ? 'PASS' : 'FAIL')} ${check.id}: ${check.detail}`);
|
|
1428
1850
|
}
|
|
1429
1851
|
}
|
|
1430
1852
|
}
|
|
1431
1853
|
function printAgentAudit(out) {
|
|
1432
|
-
console.log(
|
|
1854
|
+
console.log(`${strong('dingtalk-agent agent audit')} · ${mark(out.ready ? 'pass' : 'warn', out.ready ? 'READY' : 'PARTIAL')}`);
|
|
1433
1855
|
console.log(` definition=${out.definition.hash} storage=${out.storageMode}`);
|
|
1434
1856
|
console.log(` dingtalkSideEffect=${out.dingtalkSideEffect} sideEffect=${out.sideEffect}`);
|
|
1435
1857
|
for (const check of out.checks) {
|
|
1436
|
-
const
|
|
1437
|
-
console.log(` ${mark} ${check.id}: ${check.summary}`);
|
|
1858
|
+
const word = check.level === 'pass' ? 'PASS' : check.level === 'warn' ? 'WARN' : 'FAIL';
|
|
1859
|
+
console.log(` ${mark(levelOf(check.level), word)} ${check.id}: ${check.summary}`);
|
|
1860
|
+
const evidence = check.evidence;
|
|
1861
|
+
// 失败可归因:把 failureSummary 的前几条与证据路径直接摆到失败行下面,
|
|
1862
|
+
// 不再让人去 JSON 深处找「为什么」。flaky 通过(第二次 attempt 才过)也如实标注。
|
|
1863
|
+
if (check.level !== 'pass' && check.blocking && Array.isArray(evidence?.failureSummary)) {
|
|
1864
|
+
for (const line of evidence.failureSummary.slice(0, 3)) {
|
|
1865
|
+
console.log(` · ${line}`);
|
|
1866
|
+
}
|
|
1867
|
+
if (typeof evidence.report === 'string' && evidence.report) {
|
|
1868
|
+
console.log(` evidence: ${evidence.report}`);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
const verification = evidence?.verification;
|
|
1872
|
+
if (verification && Array.isArray(verification.attempts) && verification.attempts.length > 1) {
|
|
1873
|
+
console.log(` attempts: ${verification.attempts.length}` +
|
|
1874
|
+
`(passedOnAttempt=${verification.passedOnAttempt || '-'})`);
|
|
1875
|
+
}
|
|
1438
1876
|
}
|
|
1439
1877
|
if (out.repairs.length) {
|
|
1440
1878
|
console.log('\n修复建议:');
|
|
@@ -1447,7 +1885,7 @@ function printAgentAudit(out) {
|
|
|
1447
1885
|
}
|
|
1448
1886
|
function printAgentEnhance(out) {
|
|
1449
1887
|
if (out.$schema === 'dingtalk-agent/agent-enhancement-plan@1') {
|
|
1450
|
-
console.log(
|
|
1888
|
+
console.log(`${strong('dingtalk-agent agent enhance')} · ${mark(out.ready ? 'pass' : 'fail', out.ready ? 'READY PLAN' : 'BLOCKED')}`);
|
|
1451
1889
|
console.log(` planId=${out.planId} project=${out.projectName}`);
|
|
1452
1890
|
console.log(` sideEffect=${out.sideEffect} dingtalkSideEffect=${out.dingtalkSideEffect}`);
|
|
1453
1891
|
for (const operation of out.operations) {
|
|
@@ -1457,12 +1895,12 @@ function printAgentEnhance(out) {
|
|
|
1457
1895
|
console.log(` REVIEW ${out.semanticReview.files.join(', ')}: ${out.semanticReview.why}`);
|
|
1458
1896
|
}
|
|
1459
1897
|
for (const blocker of out.blockers)
|
|
1460
|
-
console.log(` BLOCK ${blocker}`);
|
|
1898
|
+
console.log(` ${mark('fail', 'BLOCK')} ${blocker}`);
|
|
1461
1899
|
if (out.ready)
|
|
1462
1900
|
console.log(`\nApply:\n ${out.apply.command}`);
|
|
1463
1901
|
return;
|
|
1464
1902
|
}
|
|
1465
|
-
console.log('dingtalk-agent agent enhance · APPLIED');
|
|
1903
|
+
console.log(`${strong('dingtalk-agent agent enhance')} · ${mark('pass', 'APPLIED')}`);
|
|
1466
1904
|
console.log(` operation=${out.operationId} planId=${out.planId}`);
|
|
1467
1905
|
console.log(` changes=${out.changes.length} idempotent=${out.idempotent}`);
|
|
1468
1906
|
if (out.backupRoot)
|
|
@@ -1470,8 +1908,8 @@ function printAgentEnhance(out) {
|
|
|
1470
1908
|
console.log(` audit=${out.audit.status} missing=${out.audit.missing.join(', ') || '-'}`);
|
|
1471
1909
|
}
|
|
1472
1910
|
function printAgentPlatform(platform) {
|
|
1473
|
-
console.log(`${platform.status === 'supported' ? '✅' : '⏳'} ${platform.name} · ${platform.label} · ${platformStatusLabel(platform.status)}`);
|
|
1474
|
-
console.log(` ${platform.description}`);
|
|
1911
|
+
console.log(`${mark(platform.status === 'supported' ? 'pass' : 'muted', platform.status === 'supported' ? '✅' : '⏳')} ${strong(platform.name)} · ${platform.label} · ${platformStatusLabel(platform.status)}`);
|
|
1912
|
+
console.log(` ${muted(platform.description)}`);
|
|
1475
1913
|
for (const skill of platform.skills)
|
|
1476
1914
|
console.log(` 技能[${skill.role}] ${skill.name} —— ${skill.purpose}`);
|
|
1477
1915
|
if (platform.commands.length)
|
|
@@ -1479,12 +1917,12 @@ function printAgentPlatform(platform) {
|
|
|
1479
1917
|
}
|
|
1480
1918
|
function printAgentPlatformResolution(out) {
|
|
1481
1919
|
if (!out.platform) {
|
|
1482
|
-
console.log('⚠️ 当前工作区未声明 managed agent platform');
|
|
1920
|
+
console.log(mark('warn', '⚠️ 当前工作区未声明 managed agent platform'));
|
|
1483
1921
|
console.log(' 运行 `dta agent-platform use multica-dingtalk`,或设置 DTA_AGENT_PLATFORM 环境变量。');
|
|
1484
1922
|
return;
|
|
1485
1923
|
}
|
|
1486
1924
|
const known = out.definition;
|
|
1487
|
-
console.log(`${known ? '✅' : '❌'} 当前 platform: ${out.platform}${known ? ` · ${known.label} · ${platformStatusLabel(known.status)}` : '(未知)'}`);
|
|
1925
|
+
console.log(`${mark(levelOf(Boolean(known)), known ? '✅' : '❌')} 当前 platform: ${strong(out.platform)}${known ? ` · ${known.label} · ${platformStatusLabel(known.status)}` : '(未知)'}`);
|
|
1488
1926
|
console.log(` 来源: ${out.source}${out.file ? ` · ${out.file}` : ''}`);
|
|
1489
1927
|
for (const skill of known?.skills || [])
|
|
1490
1928
|
console.log(` 技能[${skill.role}] ${skill.name} —— ${skill.purpose}`);
|
|
@@ -1492,8 +1930,9 @@ function printAgentPlatformResolution(out) {
|
|
|
1492
1930
|
// 披露:dta 层平台命令关联到平台内确切的 Skill(诊断信息走 stderr,不污染 --json stdout)。
|
|
1493
1931
|
function disclosePlatformSkill(resolution, role) {
|
|
1494
1932
|
const skillName = resolution.definition ? platformSkillForRole(resolution.definition.name, role) : null;
|
|
1495
|
-
if (skillName)
|
|
1496
|
-
console.error(`提示: 平台 ${resolution.definition.name} 的该命令方法学由 Skill ${skillName} 承载`);
|
|
1933
|
+
if (skillName) {
|
|
1934
|
+
console.error(stderrUi().theme.dim(`提示: 平台 ${resolution.definition.name} 的该命令方法学由 Skill ${skillName} 承载`));
|
|
1935
|
+
}
|
|
1497
1936
|
}
|
|
1498
1937
|
function printEndpoint(endpoint) {
|
|
1499
1938
|
if (!endpoint)
|
|
@@ -1507,9 +1946,9 @@ function printEndpoint(endpoint) {
|
|
|
1507
1946
|
console.log(` Endpoint: 未解析——设 ${endpoint.envVar} 或登录 multica 后生效`);
|
|
1508
1947
|
return;
|
|
1509
1948
|
}
|
|
1510
|
-
const
|
|
1949
|
+
const glyph = mark(endpoint.confirmed ? 'pass' : 'warn', endpoint.confirmed ? '✅' : '⚠️');
|
|
1511
1950
|
const note = endpoint.confirmed ? '' : '(建议/未确认,deploy 前需显式确认是预发还是生产)';
|
|
1512
|
-
console.log(` ${
|
|
1951
|
+
console.log(` ${glyph} Endpoint: ${noun(endpoint.endpoint)} · 来源: ${endpoint.source}${muted(note)}`);
|
|
1513
1952
|
console.log(` (切换:设 ${endpoint.envVar}=<url>、写 dingtalk-agent.json#multicaEndpoint、或登录对应 profile;无需改代码)`);
|
|
1514
1953
|
}
|
|
1515
1954
|
function printPlatformDoc(platformName) {
|
|
@@ -1517,27 +1956,27 @@ function printPlatformDoc(platformName) {
|
|
|
1517
1956
|
return;
|
|
1518
1957
|
const path = platformDocPath(PACKAGE_ROOT, platformName);
|
|
1519
1958
|
if (path)
|
|
1520
|
-
console.log(` 平台说明: ${path}\n
|
|
1959
|
+
console.log(` 平台说明: ${noun(path)}\n${muted(' (切换到该平台后请阅读,了解各技能用途与交付链)')}`);
|
|
1521
1960
|
}
|
|
1522
1961
|
function printMulticaTargets(targets) {
|
|
1523
1962
|
if (!targets.length)
|
|
1524
1963
|
return;
|
|
1525
|
-
console.log(' 本机 Multica 登录目标(发布前必须与用户确认 endpoint 与 workspace):');
|
|
1964
|
+
console.log(strong(' 本机 Multica 登录目标(发布前必须与用户确认 endpoint 与 workspace):'));
|
|
1526
1965
|
for (const target of targets) {
|
|
1527
1966
|
console.log(` - profile=${target.profile} · ${target.serverUrl}${target.workspaceId ? ` · workspace=${target.workspaceId}` : ''}`);
|
|
1528
1967
|
}
|
|
1529
1968
|
}
|
|
1530
1969
|
function printAgentPlatformReadiness(checks) {
|
|
1531
1970
|
for (const check of checks) {
|
|
1532
|
-
console.log(` ${check.ok ? '✅' : '⚠️'} ${check.summary}`);
|
|
1971
|
+
console.log(` ${mark(check.ok ? 'pass' : 'warn', check.ok ? '✅' : '⚠️')} ${check.summary}`);
|
|
1533
1972
|
if (check.hint)
|
|
1534
|
-
console.log(` ${check.hint}`);
|
|
1973
|
+
console.log(` ${muted(check.hint)}`);
|
|
1535
1974
|
}
|
|
1536
1975
|
}
|
|
1537
1976
|
function printSkillStatus(out, action, dryRun) {
|
|
1538
|
-
const prefix = dryRun ? 'DRY-RUN' : '✅';
|
|
1977
|
+
const prefix = dryRun ? mark('muted', 'DRY-RUN') : mark('pass', '✅');
|
|
1539
1978
|
const verb = action === 'status' ? '全局 Skill 状态' : `skill ${action}`;
|
|
1540
|
-
console.log(`${prefix} ${verb}: ${out.name}`);
|
|
1979
|
+
console.log(`${prefix} ${verb}: ${strong(out.name)}`);
|
|
1541
1980
|
console.log(` manager=${out.manager} bundled=${out.bundled.skillVersion || '-'}`);
|
|
1542
1981
|
console.log(` canonical=${out.canonical.path}`);
|
|
1543
1982
|
console.log(` installed=${out.canonical.exists} ` +
|
|
@@ -1548,71 +1987,110 @@ function printSkillStatus(out, action, dryRun) {
|
|
|
1548
1987
|
` → ${out.operation.after.installedVersion}(${out.operation.after.state})`);
|
|
1549
1988
|
}
|
|
1550
1989
|
if (out.operation)
|
|
1551
|
-
console.log(` delegated: ${out.operation.command}`);
|
|
1990
|
+
console.log(` delegated: ${noun(out.operation.command)}`);
|
|
1552
1991
|
for (const client of out.clients) {
|
|
1553
|
-
console.log(` ${client.label}=${client.state} (${client.discovery}: ${client.path})`);
|
|
1992
|
+
console.log(` ${client.label}=${mark(client.state === 'ok' ? 'pass' : 'warn', client.state)} (${client.discovery}: ${noun(client.path)})`);
|
|
1554
1993
|
}
|
|
1555
1994
|
if (action === 'install' && !dryRun) {
|
|
1556
|
-
console.log(' 新 Agent 会话将自动发现;当前会话未发现时请重启 Claude Code/Codex。');
|
|
1995
|
+
console.log(muted(' 新 Agent 会话将自动发现;当前会话未发现时请重启 Claude Code/Codex。'));
|
|
1557
1996
|
}
|
|
1558
1997
|
}
|
|
1998
|
+
/**
|
|
1999
|
+
* doctor 的版面。改造前的问题不是"不够花哨",是三条实打实的:
|
|
2000
|
+
* 1. 同一事实说两遍——⚠️ 摘要和它的 detail 重复平台 Skill 漂移;三个客户端
|
|
2001
|
+
* 的 Skill 可见性在 check 区和「本机 Coding Agent」区各说一次。
|
|
2002
|
+
* 2. 一行塞太多——detail 用 ` | ` 把四条 canonical 路径和平台 Skill 状态拼在
|
|
2003
|
+
* 一起,200+ 字符,终端里必然折行。
|
|
2004
|
+
* 3. 分隔符混用 `·` `;` `|` 三种,没有一列是对齐的。
|
|
2005
|
+
* 所以这里按「一行一件事、明细缩进、列对齐」重排,并把客户端合并成一节。
|
|
2006
|
+
*/
|
|
1559
2007
|
function printDoctor(out) {
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
2008
|
+
const { theme } = stdoutUi();
|
|
2009
|
+
// 字形沿用全 CLI 既定的 ✅/⚠️/❌(bin 里各出现 24/11/5 次)。上一版换成
|
|
2010
|
+
// ✔/!/✗ 让 doctor 成了唯一的异类,也违反 AGENTS.md「stdout 是稳定的人/
|
|
2011
|
+
// 机器接口」——颜色可以加,承载语义的字形不能动。三者都是 2 列宽,对齐不受影响。
|
|
2012
|
+
const glyph = (level) => level === 'pass' ? mark('pass', '✅')
|
|
2013
|
+
: level === 'warn' ? mark('warn', '⚠️') : mark('fail', '❌');
|
|
2014
|
+
const clientIds = new Set(['claude-code', 'codex', 'opencode']);
|
|
2015
|
+
const envChecks = out.checks.filter((check) => !clientIds.has(check.id));
|
|
2016
|
+
const clientChecks = out.checks.filter((check) => clientIds.has(check.id));
|
|
2017
|
+
console.log(`${strong('dingtalk-agent doctor')} · ${out.ready ? mark('pass', 'READY') : mark('fail', 'NOT READY')}`);
|
|
2018
|
+
console.log('');
|
|
2019
|
+
// 左列按显示宽度对齐——中文标签用 String.length 补齐必然错位
|
|
2020
|
+
const labelWidth = Math.max(...envChecks.map((check) => displayWidth(CHECK_LABELS[check.id])));
|
|
2021
|
+
for (const check of envChecks) {
|
|
2022
|
+
const label = padRight(CHECK_LABELS[check.id], labelWidth);
|
|
2023
|
+
console.log(` ${glyph(check.level)} ${strong(label)} ${check.value || check.summary}`);
|
|
2024
|
+
const items = check.items || [];
|
|
2025
|
+
const itemWidth = items.length ? Math.max(...items.map((i) => displayWidth(i.label))) : 0;
|
|
2026
|
+
const valueWidth = items.length ? Math.max(...items.map((i) => displayWidth(i.value || ''))) : 0;
|
|
2027
|
+
for (const item of items) {
|
|
2028
|
+
console.log(` ${noun(padRight(item.label, itemWidth))} ${padRight(item.value || '', valueWidth)}${item.note ? ` ${muted(item.note)}` : ''}`);
|
|
2029
|
+
}
|
|
2030
|
+
if (check.level !== 'pass' && check.fix) {
|
|
2031
|
+
// 一条命令一行。挤在同一行里用「与」串起来的命令没法直接复制。
|
|
2032
|
+
const commands = plain(check.fix).replace(/^运行\s*/, '').split(/\s*与\s*/);
|
|
2033
|
+
commands.forEach((command, index) => {
|
|
2034
|
+
console.log(` ${muted(index === 0 ? '修复' : ' ')} ${noun(command)}`);
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if (clientChecks.length) {
|
|
2039
|
+
console.log('');
|
|
2040
|
+
console.log(` ${strong('Coding Agent')}`);
|
|
2041
|
+
const nameWidth = Math.max(...clientChecks.map((c) => displayWidth(CHECK_LABELS[c.id])));
|
|
2042
|
+
const hostOf = (id) => out.hosts.find((host) => host.name === id);
|
|
2043
|
+
// 版本列去掉冗余括注与前缀:claude 报 "2.1.218 (Claude Code)"、codex 报 "codex-cli 0.144.6"
|
|
2044
|
+
const versions = clientChecks.map((c) => (hostOf(c.id)?.cli.version || '')
|
|
2045
|
+
.replace(/\s*\(.*\)\s*$/, '').replace(/^\S+-cli\s+/, ''));
|
|
2046
|
+
const versionWidth = Math.max(...versions.map(displayWidth));
|
|
2047
|
+
clientChecks.forEach((check, index) => {
|
|
2048
|
+
const host = hostOf(check.id);
|
|
2049
|
+
const name = padRight(CHECK_LABELS[check.id], nameWidth);
|
|
2050
|
+
const version = padRight(versions[index], versionWidth);
|
|
2051
|
+
const where = host?.cli.found ? tilde(host.skillExposureRoot) : `未安装 ${host?.cli.command || ''}`;
|
|
2052
|
+
console.log(` ${glyph(check.level)} ${strong(name)} ${muted(version)} ${check.value || check.summary} ${noun(where)}`);
|
|
2053
|
+
});
|
|
1586
2054
|
}
|
|
2055
|
+
// 「下一步」此前逐字重复上面已经打过的修复命令。只留没展示过的。
|
|
2056
|
+
const shown = new Set(out.checks.filter((c) => c.level !== 'pass' && c.fix).map((c) => c.fix));
|
|
2057
|
+
const remaining = out.nextSteps.filter((step) => !shown.has(step));
|
|
2058
|
+
if (remaining.length) {
|
|
2059
|
+
console.log('');
|
|
2060
|
+
for (const step of remaining)
|
|
2061
|
+
console.log(` ${strong('下一步')} ${noun(plain(step))}`);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
/** 反引号在终端里不是强调,是噪声;着色已经承担了同一职责。 */
|
|
2065
|
+
function plain(text) {
|
|
2066
|
+
return text.replace(/`/g, '');
|
|
1587
2067
|
}
|
|
1588
|
-
/**
|
|
1589
|
-
function
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
width += /[ᄀ-ᅟ⺀-가-힣豈-︰--⦆¢-₩]/.test(char) ? 2 : 1;
|
|
1593
|
-
return width;
|
|
2068
|
+
/** 家目录缩成 ~/,省掉对读者零信息量的前缀。 */
|
|
2069
|
+
function tilde(path) {
|
|
2070
|
+
const home = process.env.HOME || '';
|
|
2071
|
+
return home && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
1594
2072
|
}
|
|
1595
2073
|
function printProjectInfo(out) {
|
|
1596
|
-
console.log(
|
|
2074
|
+
console.log(`${strong('dingtalk-agent Project')} · ${noun(out.project.name)}`);
|
|
1597
2075
|
console.log(` root=${out.root}`);
|
|
1598
2076
|
console.log(` source=${out.source} manifest=${out.manifest || '(synthetic legacy)'}`);
|
|
1599
2077
|
console.log(` hash=${out.manifestHash} dta=${out.project.dtaVersion}`);
|
|
1600
2078
|
console.log(` current=${out.currentWorkspace || '(not selected)'}`);
|
|
1601
2079
|
for (const workspace of out.workspaces) {
|
|
1602
|
-
console.log(` ${workspace.current ? '*' : '-'} ${workspace.name} · ${workspace.provider.kind} · ${workspace.status}`);
|
|
2080
|
+
console.log(` ${workspace.current ? strong('*') : muted('-')} ${workspace.name} · ${workspace.provider.kind} · ${workspace.status}`);
|
|
1603
2081
|
}
|
|
1604
2082
|
}
|
|
1605
2083
|
function printWorkspaceList(out) {
|
|
1606
|
-
console.log(
|
|
2084
|
+
console.log(`${strong('dingtalk-agent workspaces')} · ${noun(out.project.name)}`);
|
|
1607
2085
|
if (!out.workspaces.length)
|
|
1608
|
-
console.log(' 尚未声明 Development Workspace。');
|
|
2086
|
+
console.log(muted(' 尚未声明 Development Workspace。'));
|
|
1609
2087
|
for (const workspace of out.workspaces) {
|
|
1610
|
-
console.log(` ${workspace.current ? '*' : '-'} ${workspace.name} provider=${workspace.provider.kind} ` +
|
|
2088
|
+
console.log(` ${workspace.current ? strong('*') : muted('-')} ${workspace.name} provider=${workspace.provider.kind} ` +
|
|
1611
2089
|
`stage=${workspace.stage} status=${workspace.status}`);
|
|
1612
2090
|
}
|
|
1613
2091
|
}
|
|
1614
2092
|
function printDevelopmentWorkspace(out) {
|
|
1615
|
-
console.log(
|
|
2093
|
+
console.log(`${strong('dingtalk-agent workspace')} · ${noun(out.name)}`);
|
|
1616
2094
|
console.log(` provider=${out.provider.kind} stage=${out.stage} status=${out.status}`);
|
|
1617
2095
|
console.log(` desired=${out.desiredHash} observed=${out.observedHash || '-'}`);
|
|
1618
2096
|
console.log(` current=${out.current} legacy=${out.legacy}`);
|
|
@@ -1622,19 +2100,22 @@ function printDevelopmentWorkspace(out) {
|
|
|
1622
2100
|
console.log(` source=${source}`);
|
|
1623
2101
|
}
|
|
1624
2102
|
function printDevelopmentWorkspaceDoctor(out) {
|
|
1625
|
-
console.log(
|
|
2103
|
+
console.log(`${strong('dingtalk-agent workspace doctor')} · ${noun(out.workspace)} · ${mark(out.ready ? 'pass' : 'warn', out.ready ? 'READY' : 'PARTIAL')}`);
|
|
1626
2104
|
console.log(` provider=${out.provider} status=${out.status}`);
|
|
1627
2105
|
console.log(` sideEffect=${out.sideEffect} dingtalkSideEffect=${out.dingtalkSideEffect}`);
|
|
1628
2106
|
for (const check of out.checks) {
|
|
1629
|
-
const
|
|
1630
|
-
console.log(` ${mark} ${check.id}: ${check.summary}`);
|
|
2107
|
+
const word = check.level === 'pass' ? 'PASS' : check.level === 'warn' ? 'WARN' : 'FAIL';
|
|
2108
|
+
console.log(` ${mark(levelOf(check.level), word)} ${check.id}: ${check.summary}`);
|
|
1631
2109
|
if (check.fix && check.level !== 'pass')
|
|
1632
|
-
console.log(` fix: ${check.fix}`);
|
|
2110
|
+
console.log(` fix: ${noun(check.fix)}`);
|
|
1633
2111
|
}
|
|
1634
2112
|
}
|
|
1635
2113
|
function printOpenCodeWorkspaceOperation(out) {
|
|
1636
2114
|
const applied = out.$schema === 'dingtalk-agent/opencode-workspace-apply@1';
|
|
1637
|
-
|
|
2115
|
+
const glyph = applied
|
|
2116
|
+
? mark(out.applied ? 'pass' : 'muted', out.applied ? '✅' : '↩️')
|
|
2117
|
+
: mark('muted', 'DRY-RUN');
|
|
2118
|
+
console.log(`${glyph} OpenCode Workspace ${noun(out.workspace)}`);
|
|
1638
2119
|
console.log(` action=${out.action} model=${out.model}`);
|
|
1639
2120
|
console.log(` desired=${out.desiredHash} observed=${out.observedHash || '-'}`);
|
|
1640
2121
|
console.log(` managed=${out.managedPath}`);
|
|
@@ -1644,7 +2125,7 @@ function printOpenCodeWorkspaceOperation(out) {
|
|
|
1644
2125
|
function printMulticaWorkspaceOperation(out) {
|
|
1645
2126
|
const passed = out.passed ?? out.readyForApply ?? out.blocking?.length === 0;
|
|
1646
2127
|
const mode = out.remoteRead ? 'REMOTE-READ' : 'DRY-RUN';
|
|
1647
|
-
console.log(`${passed ? '✅' : '⚠️'} ${mode} Multica Workspace ${out.workspace}`);
|
|
2128
|
+
console.log(`${mark(passed ? 'pass' : 'warn', passed ? '✅' : '⚠️')} ${mode} Multica Workspace ${noun(out.workspace)}`);
|
|
1648
2129
|
console.log(` schema=${out.$schema} profile=${out.profile || '-'}`);
|
|
1649
2130
|
if (out.planId)
|
|
1650
2131
|
console.log(` plan=${out.planId}`);
|
|
@@ -1660,12 +2141,41 @@ function printMulticaWorkspaceOperation(out) {
|
|
|
1660
2141
|
console.log(` blocking=${out.blocking.join(', ')}`);
|
|
1661
2142
|
if (out.failures?.length)
|
|
1662
2143
|
console.log(` failures=${out.failures.join(', ')}`);
|
|
2144
|
+
printFailureDetails(out.failureDetails, ' ');
|
|
2145
|
+
// 同名候选就在 JSON 里,人类输出却只给一个枚举串——把出路直接摆出来(B10/C3)。
|
|
2146
|
+
if (out.failures?.includes('agent.unbound-name-candidates') &&
|
|
2147
|
+
Array.isArray(out.resources?.agentCandidates) && out.resources.agentCandidates.length) {
|
|
2148
|
+
for (const candidate of out.resources.agentCandidates) {
|
|
2149
|
+
console.log(` candidate: ${candidate.id} ${candidate.name}${candidate.archived ? ' (archived)' : ''}`);
|
|
2150
|
+
}
|
|
2151
|
+
console.log(' fix: 核对后在 manifest 声明 provider.agentIdFrom 指向正确 ID' +
|
|
2152
|
+
'(或依赖首次 deploy 写入的 state.binding.agentId);不要按名字猜绑定');
|
|
2153
|
+
}
|
|
1663
2154
|
if (out.evidencePath)
|
|
1664
2155
|
console.log(` evidence=${out.evidencePath}`);
|
|
1665
2156
|
console.log(` remoteWrite=${out.remoteWrite} dingtalkSideEffect=${out.dingtalkSideEffect}`);
|
|
1666
2157
|
}
|
|
2158
|
+
/** 期望 vs 实际 + 下一步:把 fail-closed 从「正确但沉默」升级为「正确且能自救」。 */
|
|
2159
|
+
function printFailureDetails(details, indent) {
|
|
2160
|
+
if (!Array.isArray(details))
|
|
2161
|
+
return;
|
|
2162
|
+
for (const detail of details) {
|
|
2163
|
+
if (!detail || typeof detail !== 'object')
|
|
2164
|
+
continue;
|
|
2165
|
+
const item = detail;
|
|
2166
|
+
const format = (value) => Array.isArray(value) ? value.join(', ') : typeof value === 'string' ? value : '';
|
|
2167
|
+
const expected = format(item.expected);
|
|
2168
|
+
const actual = format(item.actual);
|
|
2169
|
+
const parts = [
|
|
2170
|
+
expected ? `expected=${expected}` : '', actual ? `actual=${actual}` : '',
|
|
2171
|
+
].filter(Boolean).join(' ');
|
|
2172
|
+
console.log(`${indent}- ${item.code}${parts ? `: ${parts}` : ''}`);
|
|
2173
|
+
if (typeof item.hint === 'string' && item.hint)
|
|
2174
|
+
console.log(`${indent} → ${item.hint}`);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
1667
2177
|
function printMulticaDeployment(out) {
|
|
1668
|
-
console.log(
|
|
2178
|
+
console.log(`${strong('Multica deploy')}: ${noun(out.workspace || '<unknown>')}`);
|
|
1669
2179
|
if (out.planId)
|
|
1670
2180
|
console.log(` plan: ${out.planId}`);
|
|
1671
2181
|
if (out.operationId)
|
|
@@ -1678,17 +2188,42 @@ function printMulticaDeployment(out) {
|
|
|
1678
2188
|
console.log(` status: ${out.status}`);
|
|
1679
2189
|
if (typeof out.providerReady === 'boolean')
|
|
1680
2190
|
console.log(` provider ready: ${out.providerReady}`);
|
|
2191
|
+
// 首次创建 Agent 后把「二次部署怎么绑定」讲在当下(B10):正常路径 ID 已存 state
|
|
2192
|
+
// 自动复用;换机器/state 丢失才需要显式声明。
|
|
2193
|
+
if (out.action === 'apply' && out.agent?.action === 'create' && out.agent?.id) {
|
|
2194
|
+
console.log(` agent created: ${out.agent.id}`);
|
|
2195
|
+
console.log(` ID 已写入 .dingtalk-agent/state/workspaces/${out.workspace}.json,二次部署与 schedule 自动复用`);
|
|
2196
|
+
console.log(' 换机器或 state 丢失时:在 manifest 声明 provider.agentIdFrom=env:<VAR> 并 export 该 ID');
|
|
2197
|
+
}
|
|
2198
|
+
if (out.rebind?.fromRuntimeId) {
|
|
2199
|
+
console.log(` rebind: ${out.rebind.fromRuntimeId} -> ${out.rebind.toRuntimeId}`);
|
|
2200
|
+
}
|
|
2201
|
+
const smoke = out.smoke || out.receipt?.smoke;
|
|
2202
|
+
if (smoke && smoke.status && smoke.status !== 'not-run') {
|
|
2203
|
+
const classified = smoke.classification ? ` (${smoke.classification})` : '';
|
|
2204
|
+
console.log(` smoke: ${smoke.status}${classified} task=${smoke.taskId || '-'}${smoke.taskStatus ? ` (${smoke.taskStatus})` : ''}`);
|
|
2205
|
+
console.log(` loaded: ${smoke.loadedSkills?.length ?? 0}/${smoke.requiredSkills?.length ?? 0} response: ${smoke.responsePassed === true}`);
|
|
2206
|
+
if (Array.isArray(smoke.unrecognizedTools) && smoke.unrecognizedTools.length) {
|
|
2207
|
+
console.log(` unrecognized tools: ${smoke.unrecognizedTools.join(', ')}`);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
1681
2210
|
if (Array.isArray(out.blocking) && out.blocking.length) {
|
|
1682
2211
|
console.log(` blocking: ${out.blocking.join(', ')}`);
|
|
1683
2212
|
}
|
|
1684
2213
|
if (Array.isArray(out.failures) && out.failures.length) {
|
|
1685
2214
|
console.log(` failures: ${out.failures.join(', ')}`);
|
|
1686
2215
|
}
|
|
2216
|
+
printFailureDetails(out.failureDetails || out.receipt?.failureDetails, ' ');
|
|
2217
|
+
if (Array.isArray(smoke?.failureDetails))
|
|
2218
|
+
printFailureDetails(smoke.failureDetails, ' ');
|
|
2219
|
+
if (out.receiptFrozen && out.nextAction) {
|
|
2220
|
+
console.log(muted(` smoke 字段为 apply 时刻的冻结值;刷新: ${out.nextAction}`));
|
|
2221
|
+
}
|
|
1687
2222
|
if (out.triggerWrite === false)
|
|
1688
2223
|
console.log(' triggers: unchanged');
|
|
1689
2224
|
}
|
|
1690
2225
|
function printPromotion(out) {
|
|
1691
|
-
console.log(
|
|
2226
|
+
console.log(`${strong('Promotion')}: ${noun(out.promotionId || out.candidateId || out.candidate?.candidateId || '<none>')}`);
|
|
1692
2227
|
if (out.planId)
|
|
1693
2228
|
console.log(` plan: ${out.planId}`);
|
|
1694
2229
|
if (out.routeId)
|
|
@@ -1739,33 +2274,84 @@ function rejectWorkspaceOptions(values, allowed, command) {
|
|
|
1739
2274
|
fail(`${command} 不接受参数: ${extras.join(', ')}`);
|
|
1740
2275
|
}
|
|
1741
2276
|
function printSetup(out) {
|
|
1742
|
-
console.log(`${out.dryRun ? 'DRY-RUN' : '✅'} dingtalk-agent setup`);
|
|
1743
|
-
console.log(` CLI: ${out.cli.executable}`);
|
|
1744
|
-
console.log(` 短命令: ${out.cli.alias}`);
|
|
1745
|
-
console.log(` Skill: ${out.skill.canonical.path}`);
|
|
2277
|
+
console.log(`${out.dryRun ? mark('muted', 'DRY-RUN') : mark('pass', '✅')} ${strong('dingtalk-agent setup')}`);
|
|
2278
|
+
console.log(` CLI: ${noun(out.cli.executable)}`);
|
|
2279
|
+
console.log(` 短命令: ${noun(out.cli.alias)}`);
|
|
2280
|
+
console.log(` Skill: ${noun(out.skill.canonical.path)}`);
|
|
1746
2281
|
if (!out.shell.pathReady) {
|
|
1747
|
-
console.log(` 当前 shell 尚未加载 PATH;本次终端执行: ${out.shell.activateCommand}`);
|
|
2282
|
+
console.log(` 当前 shell 尚未加载 PATH;本次终端执行: ${noun(out.shell.activateCommand)}`);
|
|
1748
2283
|
if (out.shell.rcFile)
|
|
1749
|
-
console.log(` 新终端会从 ${out.shell.rcFile} 自动加载`);
|
|
2284
|
+
console.log(muted(` 新终端会从 ${out.shell.rcFile} 自动加载`));
|
|
1750
2285
|
}
|
|
1751
2286
|
printDoctor(out.doctor);
|
|
1752
|
-
console.log(`\n开始使用: ${out.nextCommand}`);
|
|
2287
|
+
console.log(`\n开始使用: ${noun(out.nextCommand)}`);
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* 确认之前的抬头:告诉人「将要去哪」,好让 [y/N] 是个有信息的决定。
|
|
2291
|
+
*
|
|
2292
|
+
* 三态必须分开说。只判 outdated 会让 `--version <旧版> --force` 显示成
|
|
2293
|
+
* "已是最新版本",用户确认之后却真的降级——在唯一的不可逆动作前给了反的信息。
|
|
2294
|
+
*/
|
|
2295
|
+
function printUpgradePreamble(checked, options) {
|
|
2296
|
+
const { theme } = stderrUi();
|
|
2297
|
+
const arrow = (from, to, paint) => `${theme.dim(from)} ${theme.bold('→')} ${paint(to)}`;
|
|
2298
|
+
if (checked.relation === 'same' && !options.force && !options.dryRun) {
|
|
2299
|
+
console.error(` ${theme.success('✔')} 已是最新版本 ${theme.bold(checked.currentVersion)}`);
|
|
2300
|
+
console.error(theme.dim(' 仍会重新校验环境;只想查版本可用 dta upgrade --check'));
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
const label = checked.relation === 'upgrade' ? '新版本可用'
|
|
2304
|
+
: checked.relation === 'downgrade' ? '降级' : '重装';
|
|
2305
|
+
const value = checked.relation === 'upgrade'
|
|
2306
|
+
? arrow(checked.currentVersion, checked.targetVersion, theme.success)
|
|
2307
|
+
: checked.relation === 'downgrade'
|
|
2308
|
+
? `${arrow(checked.currentVersion, checked.targetVersion, theme.warn)}${theme.warn('(降级)')}`
|
|
2309
|
+
: `${theme.bold(checked.targetVersion)}${theme.dim('(与当前版本相同,将强制重装)')}`;
|
|
2310
|
+
for (const line of renderPairs([
|
|
2311
|
+
[label, value],
|
|
2312
|
+
['通道', `${checked.channel} ${theme.dim(checked.pinnedSource)}`],
|
|
2313
|
+
]))
|
|
2314
|
+
console.error(line);
|
|
2315
|
+
console.error('');
|
|
2316
|
+
}
|
|
2317
|
+
function printUpgradeCheck(checked) {
|
|
2318
|
+
const { theme } = stdoutUi();
|
|
2319
|
+
const glyph = checked.relation === 'upgrade' ? theme.warn('⬆️')
|
|
2320
|
+
: checked.relation === 'downgrade' ? theme.warn('⬇️') : theme.success('✅');
|
|
2321
|
+
const summary = checked.relation === 'upgrade'
|
|
2322
|
+
? `${theme.dim(checked.currentVersion)} → ${theme.success(checked.targetVersion)}`
|
|
2323
|
+
: checked.relation === 'downgrade'
|
|
2324
|
+
? `${theme.dim(checked.currentVersion)} → ${theme.warn(checked.targetVersion)}(目标更旧)`
|
|
2325
|
+
: `${theme.bold(checked.currentVersion)}(已是最新)`;
|
|
2326
|
+
console.log(`${glyph} dta upgrade --check: ${summary}`);
|
|
2327
|
+
console.log(` target=${theme.noun(checked.pinnedSource)} channel=${checked.channel}`);
|
|
2328
|
+
if (checked.relation === 'upgrade')
|
|
2329
|
+
console.log(` ${theme.dim('运行 dta upgrade 执行升级')}`);
|
|
2330
|
+
if (checked.relation === 'downgrade') {
|
|
2331
|
+
console.log(` ${theme.dim(`降级需显式确认: dta upgrade --version ${checked.targetVersion} --force`)}`);
|
|
2332
|
+
}
|
|
1753
2333
|
}
|
|
1754
2334
|
function printUpgrade(out) {
|
|
1755
|
-
const
|
|
1756
|
-
const
|
|
2335
|
+
const { theme } = stdoutUi();
|
|
2336
|
+
const mark = out.dryRun ? theme.dim('DRY-RUN') : out.ready ? theme.success('✅') : theme.warn('⚠️');
|
|
2337
|
+
const relation = out.changed
|
|
2338
|
+
? `${theme.dim(out.currentVersion)} → ${theme.success(out.targetVersion)}`
|
|
2339
|
+
: `${out.targetVersion}(已是当前版本)`;
|
|
1757
2340
|
console.log(`${mark} dta upgrade: ${relation}`);
|
|
1758
|
-
console.log(` target=${out.target}`);
|
|
1759
|
-
console.log(` CLI=${out.executable}`);
|
|
2341
|
+
console.log(` target=${theme.noun(out.target)}`);
|
|
2342
|
+
console.log(` CLI=${theme.noun(out.executable)}`);
|
|
1760
2343
|
if (out.dryRun) {
|
|
1761
|
-
console.log(` 将执行: ${out.commands.install}`);
|
|
1762
|
-
console.log(` 将复核: ${out.commands.verify}`);
|
|
2344
|
+
console.log(` 将执行: ${theme.noun(out.commands.install)}`);
|
|
2345
|
+
console.log(` 将复核: ${theme.noun(out.commands.verify)}`);
|
|
2346
|
+
console.log(` ${theme.dim('移除 --dry-run 以实际执行升级')}`);
|
|
1763
2347
|
}
|
|
1764
2348
|
else if (out.ready) {
|
|
1765
2349
|
console.log(' setup / DWS / Skill / Claude Code / Codex / OpenCode 全部 ready');
|
|
2350
|
+
// 出口指引:验证命令 + 后悔药,都是可以直接复制的
|
|
2351
|
+
console.log(` ${theme.dim(`验证: dta --version${out.changed ? ` 回退: dta upgrade --version ${out.currentVersion} --force` : ''}`)}`);
|
|
1766
2352
|
}
|
|
1767
2353
|
else {
|
|
1768
|
-
console.log(` CLI 已更新;环境复核未完全 ready,请运行: ${out.commands.verify}`);
|
|
2354
|
+
console.log(` CLI 已更新;环境复核未完全 ready,请运行: ${theme.noun(out.commands.verify)}`);
|
|
1769
2355
|
}
|
|
1770
2356
|
}
|
|
1771
2357
|
//# sourceMappingURL=dingtalk-agent.js.map
|