@xdxer/dingtalk-agent 0.1.4 → 0.1.5-beta.10
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 +232 -0
- package/README.en.md +115 -89
- package/README.md +111 -86
- package/dist/bin/dingtalk-agent.js +742 -152
- 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-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 +4 -4
- 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/memory/noop-receipt.js +306 -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/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 +366 -0
- package/docs/INSTALLATION.md +8 -8
- package/docs/PLATFORM-GUARDRAILS.md +188 -0
- package/docs/PRIOR-ART.md +126 -0
- package/docs/SELF-TEST.md +182 -0
- 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 +45 -0
- package/examples/agents/fde-coach/AGENTS.md +2 -25
- 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 -25
- 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 +18 -11
- package/skills/README.md +10 -8
- package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/SKILL.md +49 -24
- 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 +28 -3
- 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 +35 -9
- 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/dist/src/map.js +0 -157
- package/dist/src/map.js.map +0 -1
- package/docs/SECOND-AGENT-ACCEPTANCE.md +0 -62
- package/docs/architecture/agent-memory-topology.png +0 -0
- package/docs/architecture/agent-memory-topology.svg +0 -132
- package/docs/architecture/dingtalk-agent-blueprint.png +0 -0
- package/docs/architecture/durable-async-agent-runtime.png +0 -0
- package/docs/architecture/general-agent-kernel-topology.png +0 -0
- package/docs/architecture/provider-bound-development-workspace.png +0 -0
- package/docs/architecture/task-completion-gate.png +0 -0
- 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/deap/README.md +0 -3
- 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
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// dingtalk-agent —— 钉钉数字员工的标准范式(TypeScript source)。
|
|
3
3
|
//
|
|
4
|
-
// CLI = 拦 标准动作:可程序化、可强制、可验证。撞上去没得商量。
|
|
5
4
|
// Skill = 劝 判断:只有 LLM 能做的(该不该答、话题归属、怎么写点评)。
|
|
6
|
-
//
|
|
5
|
+
// CLI = 拦 标准动作:可程序化、可强制、可验证。撞上去没得商量。
|
|
6
|
+
// DWS = 做 真正的钉钉产品能力;本文件只在需要冻结事务边界时包一层。
|
|
7
7
|
//
|
|
8
|
-
//
|
|
8
|
+
// 本体(AGENTS.md + Role Skills)负责"它是谁、它知道什么",git clone 就带走;
|
|
9
|
+
// 它是被上面三者加载和校验的对象,不是第四个执行者。
|
|
9
10
|
//
|
|
11
|
+
// **分界线:凡是能程序化的,收进 CLI;凡是需要判断的,留在 Skill。**
|
|
10
12
|
// 因为:文档会被漏读、误读、自我说服;**撞到闸门上就没得商量。**
|
|
13
|
+
//
|
|
14
|
+
// 完整架构见 docs/ARCHITECTURE.md。
|
|
11
15
|
import { parseArgs } from 'node:util';
|
|
12
16
|
import { join, relative, resolve } from 'node:path';
|
|
13
17
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
@@ -27,16 +31,20 @@ import { runRobotEvaluation } from '../src/robot-evals.js';
|
|
|
27
31
|
import { runOpenCodeEvaluation } from '../src/opencode-evals.js';
|
|
28
32
|
import { runStorageEvaluation } from '../src/storage-evals.js';
|
|
29
33
|
import { runPersonalEventEvaluation } from '../src/personal-event-evals.js';
|
|
30
|
-
import { doctor } from '../src/doctor.js';
|
|
34
|
+
import { CHECK_LABELS, doctor } from '../src/doctor.js';
|
|
31
35
|
import { doctorDevelopmentWorkspace, inspectAgentProject, showDevelopmentWorkspace, } from '../src/development-workspace.js';
|
|
36
|
+
import { buildScheduleShow, buildSchedulePlan, scheduleBlockers } from '../src/schedule-plan.js';
|
|
32
37
|
import { applyOpenCodeWorkspace, evalOpenCodeWorkspace, planOpenCodeWorkspace, runOpenCodeWorkspace, useOpenCodeWorkspace, } from '../src/opencode-provider.js';
|
|
33
|
-
import { inspectMulticaWorkspace, listMulticaWorkspaceResources, planMulticaWorkspace, statusMulticaWorkspace, } from '../src/multica-provider.js';
|
|
34
|
-
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';
|
|
35
40
|
import { applyObservation, applyPromotion, listPromotions, planObservation, planPromotion, statusPromotion, } from '../src/promotion.js';
|
|
36
41
|
import { setup } from '../src/setup.js';
|
|
37
|
-
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';
|
|
38
45
|
import { checkpointTask, showTaskCheckpoint } from '../src/memory/task-checkpoints.js';
|
|
39
46
|
import { upsertOperationalMemory } from '../src/memory/operational.js';
|
|
47
|
+
import { listMemoryNoops, recordMemoryNoop, traceMemoryNoop } from '../src/memory/noop-receipt.js';
|
|
40
48
|
import { proposeMemoryCandidate, publishMemoryCandidate, reviewMemoryCandidate, showMemoryCandidate, } from '../src/memory/candidates.js';
|
|
41
49
|
import { installBundledSkills, installGlobalSkill, skillStatus, skillSuiteStatus, uninstallBundledSkills, uninstallGlobalSkill, upgradeBundledSkills, upgradeGlobalSkill, } from '../src/skill-manager.js';
|
|
42
50
|
import { agentPlatformReadiness, MANAGED_AGENT_PLATFORMS, multicaTargets, platformDocPath, platformSkillForRole, requireSupportedAgentPlatform, resolveAgentPlatform, resolveEndpoint, statusLabel as platformStatusLabel, useAgentPlatform, } from '../src/agent-platform.js';
|
|
@@ -60,7 +68,7 @@ dta —— 给 Agent 安装钉钉员工的基础行为和工作环境
|
|
|
60
68
|
dta setup 首次配置 CLI、DWS 和内置 Skill(基础行为 + Agent 装配 + 交付后评测)
|
|
61
69
|
dta bootstrap --json 为当前 Agent 会话加载工作区
|
|
62
70
|
dta agent enhance / audit 安全增强已有仓库,再审计定义、存储与 Skill 真加载
|
|
63
|
-
dta info /
|
|
71
|
+
dta info / workspace doctor / schedule show <name> 只读诊断 Project、Workspace 与声明的常驻节律
|
|
64
72
|
dta agent-platform use <platform> → dta deploy / dta promote 声明平台归属并装平台技能包,再受控部署与晋级
|
|
65
73
|
|
|
66
74
|
维护环境:
|
|
@@ -82,6 +90,7 @@ dingtalk-agent 可信事件运行时(可选宿主能力)
|
|
|
82
90
|
task checkpoint --input checkpoint.json --expect-revision N
|
|
83
91
|
memory operational upsert --provider provider.json --input record.json [--live --yes]
|
|
84
92
|
memory candidate propose|show|review|publish
|
|
93
|
+
memory noop record|list|trace
|
|
85
94
|
act ack|reply|ask|silence
|
|
86
95
|
|
|
87
96
|
这些命令负责目标冻结、Session/Run、Wait、幂等和回执;普通 Agent Session 不必使用。
|
|
@@ -151,7 +160,7 @@ const DEPLOY_HELP = `
|
|
|
151
160
|
dingtalk-agent Multica 受控部署
|
|
152
161
|
|
|
153
162
|
deploy --workspace <name> --dry-run [--json]
|
|
154
|
-
deploy --workspace <name> --plan-id <id> --yes [--no-wait] [--json]
|
|
163
|
+
deploy --workspace <name> --plan-id <id> --yes [--wait | --no-wait] [--json]
|
|
155
164
|
deploy --workspace <name> --status [--operation-id <id>] [--json]
|
|
156
165
|
deploy --workspace <name> --status --operation-id <id> --execute --yes [--json]
|
|
157
166
|
deploy --workspace <name> --list [--json]
|
|
@@ -159,6 +168,9 @@ dingtalk-agent Multica 受控部署
|
|
|
159
168
|
deploy --workspace <name> --retire --plan-id <id> --yes [--json]
|
|
160
169
|
|
|
161
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 分钟)。
|
|
162
174
|
status 默认只验本地脱敏 operation/Receipt;--execute --yes 才做远端独立回读。
|
|
163
175
|
deploy 只管理 Agent、完整 Skill tree、assignment 和 load smoke,不创建机器人、Webhook、Autopilot 或定时 Trigger。
|
|
164
176
|
`;
|
|
@@ -175,13 +187,100 @@ dingtalk-agent 已验版本晋级与反馈候选
|
|
|
175
187
|
Promotion 只接受指定 suite 的 load/hard/surface gate 与精确 W2 evidence hash,并委托 W4 deploy。
|
|
176
188
|
Observe 只写 gitignored、proposed、不可直接发布的 Eval candidate;不修改 AGENTS.md、Prompt、Skill 或 Trigger。
|
|
177
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
|
+
}
|
|
178
246
|
function fail(msg, code = 1, hint = '') {
|
|
179
|
-
|
|
247
|
+
// 整行染色,绝不做 danger('错误') + ': ' + msg 这种切分——
|
|
248
|
+
// 契约 eval 断言的是 stderr.includes('错误:'),切开就再也匹配不上。
|
|
249
|
+
const { theme } = stderrUi();
|
|
250
|
+
console.error(theme.danger(`错误: ${msg}`));
|
|
180
251
|
if (hint)
|
|
181
|
-
console.error(`提示: ${hint}`);
|
|
252
|
+
console.error(theme.dim(`提示: ${hint}`));
|
|
182
253
|
process.exit(code);
|
|
183
254
|
}
|
|
184
|
-
|
|
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)];
|
|
185
284
|
const cmd = argv[0];
|
|
186
285
|
const sub = argv[1];
|
|
187
286
|
try {
|
|
@@ -194,38 +293,38 @@ catch (e) {
|
|
|
194
293
|
}
|
|
195
294
|
async function main() {
|
|
196
295
|
if (cmd === 'help' && sub === 'runtime') {
|
|
197
|
-
console.log(RUNTIME_HELP);
|
|
296
|
+
console.log(renderHelp(RUNTIME_HELP));
|
|
198
297
|
return;
|
|
199
298
|
}
|
|
200
299
|
if (cmd === 'help' && sub === 'adapters') {
|
|
201
|
-
console.log(ADAPTER_HELP);
|
|
300
|
+
console.log(renderHelp(ADAPTER_HELP));
|
|
202
301
|
return;
|
|
203
302
|
}
|
|
204
303
|
if (cmd === 'help' && sub === 'lab') {
|
|
205
|
-
console.log(LAB_HELP);
|
|
304
|
+
console.log(renderHelp(LAB_HELP));
|
|
206
305
|
return;
|
|
207
306
|
}
|
|
208
307
|
if (cmd === 'help' && sub === 'agent') {
|
|
209
|
-
console.log(AGENT_HELP);
|
|
308
|
+
console.log(renderHelp(AGENT_HELP));
|
|
210
309
|
return;
|
|
211
310
|
}
|
|
212
311
|
if (cmd === 'help' && sub === 'workspace') {
|
|
213
|
-
console.log(WORKSPACE_HELP);
|
|
312
|
+
console.log(renderHelp(WORKSPACE_HELP));
|
|
214
313
|
return;
|
|
215
314
|
}
|
|
216
315
|
if (cmd === 'help' && sub === 'deploy') {
|
|
217
|
-
console.log(DEPLOY_HELP);
|
|
316
|
+
console.log(renderHelp(DEPLOY_HELP));
|
|
218
317
|
return;
|
|
219
318
|
}
|
|
220
319
|
if (cmd === 'help' && ['promote', 'observe'].includes(sub || '')) {
|
|
221
|
-
console.log(PROMOTION_HELP);
|
|
320
|
+
console.log(renderHelp(PROMOTION_HELP));
|
|
222
321
|
return;
|
|
223
322
|
}
|
|
224
323
|
if (cmd === 'help' && sub) {
|
|
225
324
|
fail(`不认识的帮助主题: ${sub}`, 1, '运行 dta --help 回到产品入口;事件宿主可用 dta help runtime。');
|
|
226
325
|
}
|
|
227
326
|
if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
228
|
-
console.log(HELP);
|
|
327
|
+
console.log(renderHelp(HELP));
|
|
229
328
|
return;
|
|
230
329
|
}
|
|
231
330
|
if (cmd === '--version' || cmd === '-V' || cmd === 'version') {
|
|
@@ -241,10 +340,11 @@ async function main() {
|
|
|
241
340
|
workspace: { type: 'string' }, json: { type: 'boolean' },
|
|
242
341
|
'dry-run': { type: 'boolean' }, yes: { type: 'boolean' },
|
|
243
342
|
'plan-id': { type: 'string' }, 'no-wait': { type: 'boolean' },
|
|
343
|
+
wait: { type: 'boolean' },
|
|
244
344
|
status: { type: 'boolean' }, list: { type: 'boolean' },
|
|
245
345
|
execute: { type: 'boolean' }, 'operation-id': { type: 'string' },
|
|
246
346
|
timeout: { type: 'string' }, 'smoke-timeout': { type: 'string' },
|
|
247
|
-
retire: { type: 'boolean' },
|
|
347
|
+
retire: { type: 'boolean' }, 'rebind-runtime': { type: 'boolean' },
|
|
248
348
|
},
|
|
249
349
|
});
|
|
250
350
|
if (positionals.length)
|
|
@@ -261,16 +361,21 @@ async function main() {
|
|
|
261
361
|
const timeoutMs = parseOptionalTimeout(o.timeout, '--timeout');
|
|
262
362
|
const smokeTimeoutMs = parseOptionalTimeout(o['smoke-timeout'], '--smoke-timeout');
|
|
263
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 互斥');
|
|
264
368
|
if (o.list) {
|
|
265
|
-
if (o.execute || o['operation-id'] || o.retire || o['plan-id'] || o['no-wait'] ||
|
|
266
|
-
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']) {
|
|
267
371
|
fail('deploy --list 只接受 --workspace 和 --json');
|
|
268
372
|
}
|
|
269
373
|
out = listMulticaDeployments(PROJECT_START, o.workspace, PACKAGE_JSON.version);
|
|
270
374
|
}
|
|
271
375
|
else if (o.status) {
|
|
272
|
-
if (o.retire || o['plan-id'] || o['no-wait'] || o['dry-run'] || o['smoke-timeout']
|
|
273
|
-
|
|
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');
|
|
274
379
|
}
|
|
275
380
|
if (Boolean(o.execute) !== Boolean(o.yes)) {
|
|
276
381
|
fail('deploy --status 远端 reconcile 必须同时传 --execute --yes');
|
|
@@ -281,24 +386,30 @@ async function main() {
|
|
|
281
386
|
});
|
|
282
387
|
}
|
|
283
388
|
else if (o['dry-run']) {
|
|
284
|
-
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 ||
|
|
285
390
|
o.timeout || o['smoke-timeout']) {
|
|
286
|
-
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');
|
|
287
392
|
}
|
|
288
393
|
out = planMulticaDeployment(PROJECT_START, o.workspace, {
|
|
289
394
|
action: o.retire ? 'retire' : 'apply', cliVersion: PACKAGE_JSON.version,
|
|
395
|
+
rebindRuntime: o['rebind-runtime'],
|
|
290
396
|
});
|
|
291
397
|
}
|
|
292
398
|
else {
|
|
293
399
|
if (o.execute || o['operation-id'] || o.status || o.list || o['dry-run']) {
|
|
294
400
|
fail('deploy apply 不接受 execute/operation-id/status/list/dry-run');
|
|
295
401
|
}
|
|
296
|
-
if (o.retire && (o['no-wait'] || o['smoke-timeout'])) {
|
|
297
|
-
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');
|
|
298
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;
|
|
299
409
|
out = applyMulticaDeployment(PROJECT_START, o.workspace, {
|
|
300
410
|
yes: o.yes, planId: o['plan-id'], noWait: o['no-wait'],
|
|
301
|
-
timeoutMs, smokeTimeoutMs, cliVersion: PACKAGE_JSON.version,
|
|
411
|
+
timeoutMs, smokeTimeoutMs: effectiveSmokeTimeoutMs, cliVersion: PACKAGE_JSON.version,
|
|
412
|
+
rebindRuntime: o['rebind-runtime'],
|
|
302
413
|
}, o.retire ? 'retire' : 'apply');
|
|
303
414
|
}
|
|
304
415
|
if (o.json)
|
|
@@ -409,6 +520,106 @@ async function main() {
|
|
|
409
520
|
printProjectInfo(out);
|
|
410
521
|
return;
|
|
411
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
|
+
}
|
|
412
623
|
if (cmd === 'workspace') {
|
|
413
624
|
const { values: o, positionals } = parseArgs({
|
|
414
625
|
args: argv.slice(2), allowPositionals: true,
|
|
@@ -419,6 +630,8 @@ async function main() {
|
|
|
419
630
|
cases: { type: 'string' }, lanes: { type: 'string' }, runs: { type: 'string' },
|
|
420
631
|
out: { type: 'string' }, timeout: { type: 'string' },
|
|
421
632
|
'previous-skill': { type: 'string' },
|
|
633
|
+
status: { type: 'boolean' }, 'run-id': { type: 'string' },
|
|
634
|
+
provider: { type: 'string' }, stage: { type: 'string' }, profile: { type: 'string' },
|
|
422
635
|
},
|
|
423
636
|
});
|
|
424
637
|
if (sub === 'list') {
|
|
@@ -540,16 +753,46 @@ async function main() {
|
|
|
540
753
|
return;
|
|
541
754
|
}
|
|
542
755
|
if (sub === 'create') {
|
|
543
|
-
rejectWorkspaceOptions(o, ['json', 'yes', 'dry-run'], 'workspace create');
|
|
544
756
|
if (positionals.length !== 1)
|
|
545
757
|
fail('workspace create 需要且只接受一个 Workspace name');
|
|
546
758
|
if (o.yes && o['dry-run'])
|
|
547
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');
|
|
548
791
|
const out = o.yes
|
|
549
|
-
? applyOpenCodeWorkspace(PROJECT_START,
|
|
792
|
+
? applyOpenCodeWorkspace(PROJECT_START, wsName, {
|
|
550
793
|
yes: true, cliVersion: PACKAGE_JSON.version,
|
|
551
794
|
})
|
|
552
|
-
: planOpenCodeWorkspace(PROJECT_START,
|
|
795
|
+
: planOpenCodeWorkspace(PROJECT_START, wsName, PACKAGE_JSON.version);
|
|
553
796
|
if (o.json)
|
|
554
797
|
console.log(JSON.stringify(out, null, 2));
|
|
555
798
|
else
|
|
@@ -564,30 +807,65 @@ async function main() {
|
|
|
564
807
|
if (o.json)
|
|
565
808
|
console.log(JSON.stringify(out, null, 2));
|
|
566
809
|
else
|
|
567
|
-
console.log(
|
|
810
|
+
console.log(`${mark('pass', '✅')} current workspace: ${muted(out.previous || '-')} → ${noun(out.current)}`);
|
|
568
811
|
return;
|
|
569
812
|
}
|
|
570
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
|
+
}
|
|
571
839
|
rejectWorkspaceOptions(o, ['json', 'yes', 'execute', 'prompt-file', 'out', 'timeout'], 'workspace run');
|
|
572
840
|
if (positionals.length !== 1 || !o['prompt-file']) {
|
|
573
841
|
fail('workspace run 需要一个 Workspace name 和 --prompt-file <file>');
|
|
574
842
|
}
|
|
575
|
-
const timeoutMs = o.timeout === undefined ? undefined : Number(o.timeout);
|
|
576
843
|
const prompt = readProjectText(PROJECT_START, o['prompt-file'], 'workspace run prompt');
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
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
|
+
});
|
|
580
853
|
if (o.json)
|
|
581
854
|
console.log(JSON.stringify(out, null, 2));
|
|
582
855
|
else if (out.$schema === 'dingtalk-agent/opencode-workspace-run-plan@1') {
|
|
583
|
-
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}`);
|
|
584
860
|
}
|
|
585
861
|
else {
|
|
586
|
-
console.log(`${out.passed ? '✅' : '❌'} workspace run ${out.runId}`);
|
|
587
|
-
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)
|
|
588
864
|
console.log(out.execution.answer);
|
|
865
|
+
if ('answer' in out && out.answer)
|
|
866
|
+
console.log(out.answer);
|
|
589
867
|
if (out.evidencePath)
|
|
590
|
-
console.log(`evidence=${out.evidencePath}`);
|
|
868
|
+
console.log(`evidence=${noun(out.evidencePath)}`);
|
|
591
869
|
}
|
|
592
870
|
if (o.execute && !out.passed)
|
|
593
871
|
process.exitCode = 2;
|
|
@@ -656,23 +934,72 @@ async function main() {
|
|
|
656
934
|
return;
|
|
657
935
|
}
|
|
658
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
|
+
}
|
|
659
942
|
const { values: o } = parseArgs({
|
|
660
943
|
args: argv.slice(1),
|
|
661
944
|
options: {
|
|
662
945
|
channel: { type: 'string' }, version: { type: 'string' }, prefix: { type: 'string' },
|
|
663
946
|
'dry-run': { type: 'boolean' }, force: { type: 'boolean' }, json: { type: 'boolean' },
|
|
947
|
+
check: { type: 'boolean' }, yes: { type: 'boolean' },
|
|
664
948
|
},
|
|
665
949
|
});
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
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,
|
|
669
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,
|
|
670
997
|
});
|
|
671
|
-
if (
|
|
672
|
-
console.log(JSON.stringify(
|
|
998
|
+
if (json)
|
|
999
|
+
console.log(JSON.stringify(result, null, 2));
|
|
673
1000
|
else
|
|
674
|
-
printUpgrade(
|
|
675
|
-
if (!
|
|
1001
|
+
printUpgrade(result);
|
|
1002
|
+
if (!result.dryRun && !result.ready)
|
|
676
1003
|
process.exitCode = 2;
|
|
677
1004
|
return;
|
|
678
1005
|
}
|
|
@@ -715,9 +1042,16 @@ async function main() {
|
|
|
715
1042
|
}
|
|
716
1043
|
if (o.json)
|
|
717
1044
|
console.log(JSON.stringify(out, null, 2));
|
|
718
|
-
else if ('skills' in out)
|
|
1045
|
+
else if ('skills' in out) {
|
|
719
1046
|
for (const item of out.skills)
|
|
720
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
|
+
}
|
|
721
1055
|
else
|
|
722
1056
|
printSkillStatus(out, sub, dryRun);
|
|
723
1057
|
return;
|
|
@@ -772,7 +1106,7 @@ async function main() {
|
|
|
772
1106
|
if (o.json)
|
|
773
1107
|
console.log(JSON.stringify({ ...out, endpoint, readiness, targets, doc }, null, 2));
|
|
774
1108
|
else {
|
|
775
|
-
console.log(
|
|
1109
|
+
console.log(`${mark('pass', '✅')} 工作区已归属 ${strong(out.definition?.label)}(写入 ${noun(out.written)})`);
|
|
776
1110
|
for (const install of out.skillInstalls)
|
|
777
1111
|
printSkillStatus(install, 'install', false);
|
|
778
1112
|
if (!out.skillInstalls.length) {
|
|
@@ -812,14 +1146,14 @@ async function main() {
|
|
|
812
1146
|
if (o.json)
|
|
813
1147
|
console.log(JSON.stringify(out, null, 2));
|
|
814
1148
|
else {
|
|
815
|
-
console.log(
|
|
1149
|
+
console.log(`${mark('pass', '✅')} ${strong(out.mode === 'workspace' ? '已装载 Workspace' : '直接会话模式')}`);
|
|
816
1150
|
console.log(` scope=${out.scopeId} initialized=${out.initialized}`);
|
|
817
1151
|
console.log(` agent=${out.definition.id} definition=${out.definition.status}`);
|
|
818
1152
|
if (!out.mounts.length)
|
|
819
|
-
console.log(' 没有发现持久上下文;这不是错误,也不需要自动 init。');
|
|
1153
|
+
console.log(muted(' 没有发现持久上下文;这不是错误,也不需要自动 init。'));
|
|
820
1154
|
for (const mount of out.mounts)
|
|
821
|
-
console.log(` ${mount.slot}: ${mount.path}`);
|
|
822
|
-
console.log(` ${out.note}`);
|
|
1155
|
+
console.log(` ${mount.slot}: ${noun(mount.path)}`);
|
|
1156
|
+
console.log(` ${muted(out.note)}`);
|
|
823
1157
|
}
|
|
824
1158
|
return;
|
|
825
1159
|
}
|
|
@@ -873,6 +1207,7 @@ async function main() {
|
|
|
873
1207
|
'state-dir': { type: 'string' }, profile: { type: 'string' },
|
|
874
1208
|
'expected-user-id': { type: 'string' }, 'require-skill': { type: 'string' },
|
|
875
1209
|
'verify-load': { type: 'boolean' }, yes: { type: 'boolean' },
|
|
1210
|
+
'strict-single-run': { type: 'boolean' },
|
|
876
1211
|
model: { type: 'string' }, out: { type: 'string' },
|
|
877
1212
|
'load-report': { type: 'string' }, 'remote-report': { type: 'string' },
|
|
878
1213
|
json: { type: 'boolean' },
|
|
@@ -889,6 +1224,7 @@ async function main() {
|
|
|
889
1224
|
bindings: loaded ? { path: loaded.path, hash: loaded.hash } : undefined,
|
|
890
1225
|
requiredSkills: o['require-skill']?.split(','),
|
|
891
1226
|
verifyLoad: o['verify-load'], yes: o.yes, model: o.model, out: o.out,
|
|
1227
|
+
strictSingleRun: o['strict-single-run'],
|
|
892
1228
|
loadReport: o['load-report'], remoteReport: o['remote-report'],
|
|
893
1229
|
});
|
|
894
1230
|
if (o.json)
|
|
@@ -1205,9 +1541,9 @@ async function main() {
|
|
|
1205
1541
|
if (o.json)
|
|
1206
1542
|
console.log(JSON.stringify(out, null, 2));
|
|
1207
1543
|
else if (!out.found)
|
|
1208
|
-
console.log('当前 Session 尚无 Task checkpoint。');
|
|
1544
|
+
console.log(muted('当前 Session 尚无 Task checkpoint。'));
|
|
1209
1545
|
else {
|
|
1210
|
-
console.log(
|
|
1546
|
+
console.log(`${mark('pass', '✅')} Task ${noun(out.checkpoint.taskId)} · ${out.checkpoint.status}`);
|
|
1211
1547
|
console.log(` revision=${out.checkpoint.revision} hash=${out.hash}`);
|
|
1212
1548
|
console.log(` next=${out.checkpoint.nextAction || '-'}`);
|
|
1213
1549
|
}
|
|
@@ -1232,7 +1568,7 @@ async function main() {
|
|
|
1232
1568
|
if (o.json || !o.live)
|
|
1233
1569
|
console.log(JSON.stringify(out, null, 2));
|
|
1234
1570
|
else {
|
|
1235
|
-
console.log(`${out.verified ? '✅' : '⚠️'} Operational Memory · ${out.mode}`);
|
|
1571
|
+
console.log(`${mark(out.verified ? 'pass' : 'warn', out.verified ? '✅' : '⚠️')} ${strong('Operational Memory')} · ${out.mode}`);
|
|
1236
1572
|
console.log(` record=${out.recordId || '-'} verification=${out.verification}`);
|
|
1237
1573
|
}
|
|
1238
1574
|
if (out.mode === 'uncertain')
|
|
@@ -1286,18 +1622,78 @@ async function main() {
|
|
|
1286
1622
|
if (o.json)
|
|
1287
1623
|
console.log(JSON.stringify(out, null, 2));
|
|
1288
1624
|
else if (action === 'publish') {
|
|
1289
|
-
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)}`);
|
|
1290
1626
|
console.log(` mode=${out.mode} targetHash=${out.currentTargetHash}`);
|
|
1291
1627
|
}
|
|
1292
1628
|
else {
|
|
1293
|
-
console.log(
|
|
1294
|
-
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)}`);
|
|
1295
1631
|
}
|
|
1296
1632
|
if (action === 'publish' && out.mode === 'uncertain')
|
|
1297
1633
|
process.exitCode = 2;
|
|
1298
1634
|
return;
|
|
1299
1635
|
}
|
|
1300
|
-
|
|
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');
|
|
1301
1697
|
}
|
|
1302
1698
|
// ── act:元语化动作;目标/身份/预算/状态转移都由 Run 固定 ──
|
|
1303
1699
|
if (cmd === 'act') {
|
|
@@ -1322,10 +1718,10 @@ async function main() {
|
|
|
1322
1718
|
if (o.json || o['dry-run'])
|
|
1323
1719
|
console.log(JSON.stringify(out, null, 2));
|
|
1324
1720
|
else {
|
|
1325
|
-
console.log(`${out.verified ? '✅' : '⚠️'} ${out.kind} · ${out.outcome} · ${out.toState}`);
|
|
1326
|
-
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)}`);
|
|
1327
1723
|
if (!out.verified)
|
|
1328
|
-
console.log(` ${out.verification}`);
|
|
1724
|
+
console.log(` ${muted(out.verification)}`);
|
|
1329
1725
|
}
|
|
1330
1726
|
if (out.outcome === 'uncertain')
|
|
1331
1727
|
process.exitCode = 2;
|
|
@@ -1397,11 +1793,41 @@ function editDistance(a, b) {
|
|
|
1397
1793
|
}
|
|
1398
1794
|
return row[b.length];
|
|
1399
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
|
+
}
|
|
1400
1824
|
function printDispatch(out, json = false) {
|
|
1401
1825
|
if (json)
|
|
1402
1826
|
return console.log(JSON.stringify(out, null, 2));
|
|
1403
|
-
const
|
|
1404
|
-
|
|
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)}`);
|
|
1405
1831
|
console.log(` Field=${out.fieldId} Session=${out.sessionId} Event=${out.eventKind}`);
|
|
1406
1832
|
console.log(` cwd=${out.cwd}`);
|
|
1407
1833
|
console.log(` entrypoint=${out.entrypoint} queue=${out.queue.key} (max=${out.queue.maxConcurrency})`);
|
|
@@ -1411,7 +1837,7 @@ function printDispatch(out, json = false) {
|
|
|
1411
1837
|
}
|
|
1412
1838
|
function printLab(out) {
|
|
1413
1839
|
const passed = out.ready ?? out.passed ?? out.assertions?.passed;
|
|
1414
|
-
console.log(`${passed === false ? '❌' : '✅'} ${out.$schema || 'dingtalk-agent/lab'}`);
|
|
1840
|
+
console.log(`${mark(levelOf(passed !== false), passed === false ? '❌' : '✅')} ${out.$schema || 'dingtalk-agent/lab'}`);
|
|
1415
1841
|
if (out.mode)
|
|
1416
1842
|
console.log(` mode=${out.mode}`);
|
|
1417
1843
|
if (out.marker)
|
|
@@ -1420,17 +1846,33 @@ function printLab(out) {
|
|
|
1420
1846
|
console.log(` evidence=${out.evidencePath}`);
|
|
1421
1847
|
if (Array.isArray(out.checks)) {
|
|
1422
1848
|
for (const check of out.checks) {
|
|
1423
|
-
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}`);
|
|
1424
1850
|
}
|
|
1425
1851
|
}
|
|
1426
1852
|
}
|
|
1427
1853
|
function printAgentAudit(out) {
|
|
1428
|
-
console.log(
|
|
1854
|
+
console.log(`${strong('dingtalk-agent agent audit')} · ${mark(out.ready ? 'pass' : 'warn', out.ready ? 'READY' : 'PARTIAL')}`);
|
|
1429
1855
|
console.log(` definition=${out.definition.hash} storage=${out.storageMode}`);
|
|
1430
1856
|
console.log(` dingtalkSideEffect=${out.dingtalkSideEffect} sideEffect=${out.sideEffect}`);
|
|
1431
1857
|
for (const check of out.checks) {
|
|
1432
|
-
const
|
|
1433
|
-
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
|
+
}
|
|
1434
1876
|
}
|
|
1435
1877
|
if (out.repairs.length) {
|
|
1436
1878
|
console.log('\n修复建议:');
|
|
@@ -1443,7 +1885,7 @@ function printAgentAudit(out) {
|
|
|
1443
1885
|
}
|
|
1444
1886
|
function printAgentEnhance(out) {
|
|
1445
1887
|
if (out.$schema === 'dingtalk-agent/agent-enhancement-plan@1') {
|
|
1446
|
-
console.log(
|
|
1888
|
+
console.log(`${strong('dingtalk-agent agent enhance')} · ${mark(out.ready ? 'pass' : 'fail', out.ready ? 'READY PLAN' : 'BLOCKED')}`);
|
|
1447
1889
|
console.log(` planId=${out.planId} project=${out.projectName}`);
|
|
1448
1890
|
console.log(` sideEffect=${out.sideEffect} dingtalkSideEffect=${out.dingtalkSideEffect}`);
|
|
1449
1891
|
for (const operation of out.operations) {
|
|
@@ -1453,12 +1895,12 @@ function printAgentEnhance(out) {
|
|
|
1453
1895
|
console.log(` REVIEW ${out.semanticReview.files.join(', ')}: ${out.semanticReview.why}`);
|
|
1454
1896
|
}
|
|
1455
1897
|
for (const blocker of out.blockers)
|
|
1456
|
-
console.log(` BLOCK ${blocker}`);
|
|
1898
|
+
console.log(` ${mark('fail', 'BLOCK')} ${blocker}`);
|
|
1457
1899
|
if (out.ready)
|
|
1458
1900
|
console.log(`\nApply:\n ${out.apply.command}`);
|
|
1459
1901
|
return;
|
|
1460
1902
|
}
|
|
1461
|
-
console.log('dingtalk-agent agent enhance · APPLIED');
|
|
1903
|
+
console.log(`${strong('dingtalk-agent agent enhance')} · ${mark('pass', 'APPLIED')}`);
|
|
1462
1904
|
console.log(` operation=${out.operationId} planId=${out.planId}`);
|
|
1463
1905
|
console.log(` changes=${out.changes.length} idempotent=${out.idempotent}`);
|
|
1464
1906
|
if (out.backupRoot)
|
|
@@ -1466,8 +1908,8 @@ function printAgentEnhance(out) {
|
|
|
1466
1908
|
console.log(` audit=${out.audit.status} missing=${out.audit.missing.join(', ') || '-'}`);
|
|
1467
1909
|
}
|
|
1468
1910
|
function printAgentPlatform(platform) {
|
|
1469
|
-
console.log(`${platform.status === 'supported' ? '✅' : '⏳'} ${platform.name} · ${platform.label} · ${platformStatusLabel(platform.status)}`);
|
|
1470
|
-
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)}`);
|
|
1471
1913
|
for (const skill of platform.skills)
|
|
1472
1914
|
console.log(` 技能[${skill.role}] ${skill.name} —— ${skill.purpose}`);
|
|
1473
1915
|
if (platform.commands.length)
|
|
@@ -1475,12 +1917,12 @@ function printAgentPlatform(platform) {
|
|
|
1475
1917
|
}
|
|
1476
1918
|
function printAgentPlatformResolution(out) {
|
|
1477
1919
|
if (!out.platform) {
|
|
1478
|
-
console.log('⚠️ 当前工作区未声明 managed agent platform');
|
|
1920
|
+
console.log(mark('warn', '⚠️ 当前工作区未声明 managed agent platform'));
|
|
1479
1921
|
console.log(' 运行 `dta agent-platform use multica-dingtalk`,或设置 DTA_AGENT_PLATFORM 环境变量。');
|
|
1480
1922
|
return;
|
|
1481
1923
|
}
|
|
1482
1924
|
const known = out.definition;
|
|
1483
|
-
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)}` : '(未知)'}`);
|
|
1484
1926
|
console.log(` 来源: ${out.source}${out.file ? ` · ${out.file}` : ''}`);
|
|
1485
1927
|
for (const skill of known?.skills || [])
|
|
1486
1928
|
console.log(` 技能[${skill.role}] ${skill.name} —— ${skill.purpose}`);
|
|
@@ -1488,8 +1930,9 @@ function printAgentPlatformResolution(out) {
|
|
|
1488
1930
|
// 披露:dta 层平台命令关联到平台内确切的 Skill(诊断信息走 stderr,不污染 --json stdout)。
|
|
1489
1931
|
function disclosePlatformSkill(resolution, role) {
|
|
1490
1932
|
const skillName = resolution.definition ? platformSkillForRole(resolution.definition.name, role) : null;
|
|
1491
|
-
if (skillName)
|
|
1492
|
-
console.error(`提示: 平台 ${resolution.definition.name} 的该命令方法学由 Skill ${skillName} 承载`);
|
|
1933
|
+
if (skillName) {
|
|
1934
|
+
console.error(stderrUi().theme.dim(`提示: 平台 ${resolution.definition.name} 的该命令方法学由 Skill ${skillName} 承载`));
|
|
1935
|
+
}
|
|
1493
1936
|
}
|
|
1494
1937
|
function printEndpoint(endpoint) {
|
|
1495
1938
|
if (!endpoint)
|
|
@@ -1503,9 +1946,9 @@ function printEndpoint(endpoint) {
|
|
|
1503
1946
|
console.log(` Endpoint: 未解析——设 ${endpoint.envVar} 或登录 multica 后生效`);
|
|
1504
1947
|
return;
|
|
1505
1948
|
}
|
|
1506
|
-
const
|
|
1949
|
+
const glyph = mark(endpoint.confirmed ? 'pass' : 'warn', endpoint.confirmed ? '✅' : '⚠️');
|
|
1507
1950
|
const note = endpoint.confirmed ? '' : '(建议/未确认,deploy 前需显式确认是预发还是生产)';
|
|
1508
|
-
console.log(` ${
|
|
1951
|
+
console.log(` ${glyph} Endpoint: ${noun(endpoint.endpoint)} · 来源: ${endpoint.source}${muted(note)}`);
|
|
1509
1952
|
console.log(` (切换:设 ${endpoint.envVar}=<url>、写 dingtalk-agent.json#multicaEndpoint、或登录对应 profile;无需改代码)`);
|
|
1510
1953
|
}
|
|
1511
1954
|
function printPlatformDoc(platformName) {
|
|
@@ -1513,27 +1956,27 @@ function printPlatformDoc(platformName) {
|
|
|
1513
1956
|
return;
|
|
1514
1957
|
const path = platformDocPath(PACKAGE_ROOT, platformName);
|
|
1515
1958
|
if (path)
|
|
1516
|
-
console.log(` 平台说明: ${path}\n
|
|
1959
|
+
console.log(` 平台说明: ${noun(path)}\n${muted(' (切换到该平台后请阅读,了解各技能用途与交付链)')}`);
|
|
1517
1960
|
}
|
|
1518
1961
|
function printMulticaTargets(targets) {
|
|
1519
1962
|
if (!targets.length)
|
|
1520
1963
|
return;
|
|
1521
|
-
console.log(' 本机 Multica 登录目标(发布前必须与用户确认 endpoint 与 workspace):');
|
|
1964
|
+
console.log(strong(' 本机 Multica 登录目标(发布前必须与用户确认 endpoint 与 workspace):'));
|
|
1522
1965
|
for (const target of targets) {
|
|
1523
1966
|
console.log(` - profile=${target.profile} · ${target.serverUrl}${target.workspaceId ? ` · workspace=${target.workspaceId}` : ''}`);
|
|
1524
1967
|
}
|
|
1525
1968
|
}
|
|
1526
1969
|
function printAgentPlatformReadiness(checks) {
|
|
1527
1970
|
for (const check of checks) {
|
|
1528
|
-
console.log(` ${check.ok ? '✅' : '⚠️'} ${check.summary}`);
|
|
1971
|
+
console.log(` ${mark(check.ok ? 'pass' : 'warn', check.ok ? '✅' : '⚠️')} ${check.summary}`);
|
|
1529
1972
|
if (check.hint)
|
|
1530
|
-
console.log(` ${check.hint}`);
|
|
1973
|
+
console.log(` ${muted(check.hint)}`);
|
|
1531
1974
|
}
|
|
1532
1975
|
}
|
|
1533
1976
|
function printSkillStatus(out, action, dryRun) {
|
|
1534
|
-
const prefix = dryRun ? 'DRY-RUN' : '✅';
|
|
1977
|
+
const prefix = dryRun ? mark('muted', 'DRY-RUN') : mark('pass', '✅');
|
|
1535
1978
|
const verb = action === 'status' ? '全局 Skill 状态' : `skill ${action}`;
|
|
1536
|
-
console.log(`${prefix} ${verb}: ${out.name}`);
|
|
1979
|
+
console.log(`${prefix} ${verb}: ${strong(out.name)}`);
|
|
1537
1980
|
console.log(` manager=${out.manager} bundled=${out.bundled.skillVersion || '-'}`);
|
|
1538
1981
|
console.log(` canonical=${out.canonical.path}`);
|
|
1539
1982
|
console.log(` installed=${out.canonical.exists} ` +
|
|
@@ -1544,71 +1987,110 @@ function printSkillStatus(out, action, dryRun) {
|
|
|
1544
1987
|
` → ${out.operation.after.installedVersion}(${out.operation.after.state})`);
|
|
1545
1988
|
}
|
|
1546
1989
|
if (out.operation)
|
|
1547
|
-
console.log(` delegated: ${out.operation.command}`);
|
|
1990
|
+
console.log(` delegated: ${noun(out.operation.command)}`);
|
|
1548
1991
|
for (const client of out.clients) {
|
|
1549
|
-
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)})`);
|
|
1550
1993
|
}
|
|
1551
1994
|
if (action === 'install' && !dryRun) {
|
|
1552
|
-
console.log(' 新 Agent 会话将自动发现;当前会话未发现时请重启 Claude Code/Codex。');
|
|
1995
|
+
console.log(muted(' 新 Agent 会话将自动发现;当前会话未发现时请重启 Claude Code/Codex。'));
|
|
1553
1996
|
}
|
|
1554
1997
|
}
|
|
1998
|
+
/**
|
|
1999
|
+
* doctor 的版面。改造前的问题不是"不够花哨",是三条实打实的:
|
|
2000
|
+
* 1. 同一事实说两遍——⚠️ 摘要和它的 detail 重复平台 Skill 漂移;三个客户端
|
|
2001
|
+
* 的 Skill 可见性在 check 区和「本机 Coding Agent」区各说一次。
|
|
2002
|
+
* 2. 一行塞太多——detail 用 ` | ` 把四条 canonical 路径和平台 Skill 状态拼在
|
|
2003
|
+
* 一起,200+ 字符,终端里必然折行。
|
|
2004
|
+
* 3. 分隔符混用 `·` `;` `|` 三种,没有一列是对齐的。
|
|
2005
|
+
* 所以这里按「一行一件事、明细缩进、列对齐」重排,并把客户端合并成一节。
|
|
2006
|
+
*/
|
|
1555
2007
|
function printDoctor(out) {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
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
|
+
});
|
|
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))}`);
|
|
1582
2062
|
}
|
|
1583
2063
|
}
|
|
1584
|
-
/**
|
|
1585
|
-
function
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
2064
|
+
/** 反引号在终端里不是强调,是噪声;着色已经承担了同一职责。 */
|
|
2065
|
+
function plain(text) {
|
|
2066
|
+
return text.replace(/`/g, '');
|
|
2067
|
+
}
|
|
2068
|
+
/** 家目录缩成 ~/,省掉对读者零信息量的前缀。 */
|
|
2069
|
+
function tilde(path) {
|
|
2070
|
+
const home = process.env.HOME || '';
|
|
2071
|
+
return home && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
1590
2072
|
}
|
|
1591
2073
|
function printProjectInfo(out) {
|
|
1592
|
-
console.log(
|
|
2074
|
+
console.log(`${strong('dingtalk-agent Project')} · ${noun(out.project.name)}`);
|
|
1593
2075
|
console.log(` root=${out.root}`);
|
|
1594
2076
|
console.log(` source=${out.source} manifest=${out.manifest || '(synthetic legacy)'}`);
|
|
1595
2077
|
console.log(` hash=${out.manifestHash} dta=${out.project.dtaVersion}`);
|
|
1596
2078
|
console.log(` current=${out.currentWorkspace || '(not selected)'}`);
|
|
1597
2079
|
for (const workspace of out.workspaces) {
|
|
1598
|
-
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}`);
|
|
1599
2081
|
}
|
|
1600
2082
|
}
|
|
1601
2083
|
function printWorkspaceList(out) {
|
|
1602
|
-
console.log(
|
|
2084
|
+
console.log(`${strong('dingtalk-agent workspaces')} · ${noun(out.project.name)}`);
|
|
1603
2085
|
if (!out.workspaces.length)
|
|
1604
|
-
console.log(' 尚未声明 Development Workspace。');
|
|
2086
|
+
console.log(muted(' 尚未声明 Development Workspace。'));
|
|
1605
2087
|
for (const workspace of out.workspaces) {
|
|
1606
|
-
console.log(` ${workspace.current ? '*' : '-'} ${workspace.name} provider=${workspace.provider.kind} ` +
|
|
2088
|
+
console.log(` ${workspace.current ? strong('*') : muted('-')} ${workspace.name} provider=${workspace.provider.kind} ` +
|
|
1607
2089
|
`stage=${workspace.stage} status=${workspace.status}`);
|
|
1608
2090
|
}
|
|
1609
2091
|
}
|
|
1610
2092
|
function printDevelopmentWorkspace(out) {
|
|
1611
|
-
console.log(
|
|
2093
|
+
console.log(`${strong('dingtalk-agent workspace')} · ${noun(out.name)}`);
|
|
1612
2094
|
console.log(` provider=${out.provider.kind} stage=${out.stage} status=${out.status}`);
|
|
1613
2095
|
console.log(` desired=${out.desiredHash} observed=${out.observedHash || '-'}`);
|
|
1614
2096
|
console.log(` current=${out.current} legacy=${out.legacy}`);
|
|
@@ -1618,19 +2100,22 @@ function printDevelopmentWorkspace(out) {
|
|
|
1618
2100
|
console.log(` source=${source}`);
|
|
1619
2101
|
}
|
|
1620
2102
|
function printDevelopmentWorkspaceDoctor(out) {
|
|
1621
|
-
console.log(
|
|
2103
|
+
console.log(`${strong('dingtalk-agent workspace doctor')} · ${noun(out.workspace)} · ${mark(out.ready ? 'pass' : 'warn', out.ready ? 'READY' : 'PARTIAL')}`);
|
|
1622
2104
|
console.log(` provider=${out.provider} status=${out.status}`);
|
|
1623
2105
|
console.log(` sideEffect=${out.sideEffect} dingtalkSideEffect=${out.dingtalkSideEffect}`);
|
|
1624
2106
|
for (const check of out.checks) {
|
|
1625
|
-
const
|
|
1626
|
-
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}`);
|
|
1627
2109
|
if (check.fix && check.level !== 'pass')
|
|
1628
|
-
console.log(` fix: ${check.fix}`);
|
|
2110
|
+
console.log(` fix: ${noun(check.fix)}`);
|
|
1629
2111
|
}
|
|
1630
2112
|
}
|
|
1631
2113
|
function printOpenCodeWorkspaceOperation(out) {
|
|
1632
2114
|
const applied = out.$schema === 'dingtalk-agent/opencode-workspace-apply@1';
|
|
1633
|
-
|
|
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)}`);
|
|
1634
2119
|
console.log(` action=${out.action} model=${out.model}`);
|
|
1635
2120
|
console.log(` desired=${out.desiredHash} observed=${out.observedHash || '-'}`);
|
|
1636
2121
|
console.log(` managed=${out.managedPath}`);
|
|
@@ -1640,7 +2125,7 @@ function printOpenCodeWorkspaceOperation(out) {
|
|
|
1640
2125
|
function printMulticaWorkspaceOperation(out) {
|
|
1641
2126
|
const passed = out.passed ?? out.readyForApply ?? out.blocking?.length === 0;
|
|
1642
2127
|
const mode = out.remoteRead ? 'REMOTE-READ' : 'DRY-RUN';
|
|
1643
|
-
console.log(`${passed ? '✅' : '⚠️'} ${mode} Multica Workspace ${out.workspace}`);
|
|
2128
|
+
console.log(`${mark(passed ? 'pass' : 'warn', passed ? '✅' : '⚠️')} ${mode} Multica Workspace ${noun(out.workspace)}`);
|
|
1644
2129
|
console.log(` schema=${out.$schema} profile=${out.profile || '-'}`);
|
|
1645
2130
|
if (out.planId)
|
|
1646
2131
|
console.log(` plan=${out.planId}`);
|
|
@@ -1656,12 +2141,41 @@ function printMulticaWorkspaceOperation(out) {
|
|
|
1656
2141
|
console.log(` blocking=${out.blocking.join(', ')}`);
|
|
1657
2142
|
if (out.failures?.length)
|
|
1658
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
|
+
}
|
|
1659
2154
|
if (out.evidencePath)
|
|
1660
2155
|
console.log(` evidence=${out.evidencePath}`);
|
|
1661
2156
|
console.log(` remoteWrite=${out.remoteWrite} dingtalkSideEffect=${out.dingtalkSideEffect}`);
|
|
1662
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
|
+
}
|
|
1663
2177
|
function printMulticaDeployment(out) {
|
|
1664
|
-
console.log(
|
|
2178
|
+
console.log(`${strong('Multica deploy')}: ${noun(out.workspace || '<unknown>')}`);
|
|
1665
2179
|
if (out.planId)
|
|
1666
2180
|
console.log(` plan: ${out.planId}`);
|
|
1667
2181
|
if (out.operationId)
|
|
@@ -1674,17 +2188,42 @@ function printMulticaDeployment(out) {
|
|
|
1674
2188
|
console.log(` status: ${out.status}`);
|
|
1675
2189
|
if (typeof out.providerReady === 'boolean')
|
|
1676
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
|
+
}
|
|
1677
2210
|
if (Array.isArray(out.blocking) && out.blocking.length) {
|
|
1678
2211
|
console.log(` blocking: ${out.blocking.join(', ')}`);
|
|
1679
2212
|
}
|
|
1680
2213
|
if (Array.isArray(out.failures) && out.failures.length) {
|
|
1681
2214
|
console.log(` failures: ${out.failures.join(', ')}`);
|
|
1682
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
|
+
}
|
|
1683
2222
|
if (out.triggerWrite === false)
|
|
1684
2223
|
console.log(' triggers: unchanged');
|
|
1685
2224
|
}
|
|
1686
2225
|
function printPromotion(out) {
|
|
1687
|
-
console.log(
|
|
2226
|
+
console.log(`${strong('Promotion')}: ${noun(out.promotionId || out.candidateId || out.candidate?.candidateId || '<none>')}`);
|
|
1688
2227
|
if (out.planId)
|
|
1689
2228
|
console.log(` plan: ${out.planId}`);
|
|
1690
2229
|
if (out.routeId)
|
|
@@ -1735,33 +2274,84 @@ function rejectWorkspaceOptions(values, allowed, command) {
|
|
|
1735
2274
|
fail(`${command} 不接受参数: ${extras.join(', ')}`);
|
|
1736
2275
|
}
|
|
1737
2276
|
function printSetup(out) {
|
|
1738
|
-
console.log(`${out.dryRun ? 'DRY-RUN' : '✅'} dingtalk-agent setup`);
|
|
1739
|
-
console.log(` CLI: ${out.cli.executable}`);
|
|
1740
|
-
console.log(` 短命令: ${out.cli.alias}`);
|
|
1741
|
-
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)}`);
|
|
1742
2281
|
if (!out.shell.pathReady) {
|
|
1743
|
-
console.log(` 当前 shell 尚未加载 PATH;本次终端执行: ${out.shell.activateCommand}`);
|
|
2282
|
+
console.log(` 当前 shell 尚未加载 PATH;本次终端执行: ${noun(out.shell.activateCommand)}`);
|
|
1744
2283
|
if (out.shell.rcFile)
|
|
1745
|
-
console.log(` 新终端会从 ${out.shell.rcFile} 自动加载`);
|
|
2284
|
+
console.log(muted(` 新终端会从 ${out.shell.rcFile} 自动加载`));
|
|
1746
2285
|
}
|
|
1747
2286
|
printDoctor(out.doctor);
|
|
1748
|
-
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
|
+
}
|
|
1749
2333
|
}
|
|
1750
2334
|
function printUpgrade(out) {
|
|
1751
|
-
const
|
|
1752
|
-
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}(已是当前版本)`;
|
|
1753
2340
|
console.log(`${mark} dta upgrade: ${relation}`);
|
|
1754
|
-
console.log(` target=${out.target}`);
|
|
1755
|
-
console.log(` CLI=${out.executable}`);
|
|
2341
|
+
console.log(` target=${theme.noun(out.target)}`);
|
|
2342
|
+
console.log(` CLI=${theme.noun(out.executable)}`);
|
|
1756
2343
|
if (out.dryRun) {
|
|
1757
|
-
console.log(` 将执行: ${out.commands.install}`);
|
|
1758
|
-
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 以实际执行升级')}`);
|
|
1759
2347
|
}
|
|
1760
2348
|
else if (out.ready) {
|
|
1761
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` : ''}`)}`);
|
|
1762
2352
|
}
|
|
1763
2353
|
else {
|
|
1764
|
-
console.log(` CLI 已更新;环境复核未完全 ready,请运行: ${out.commands.verify}`);
|
|
2354
|
+
console.log(` CLI 已更新;环境复核未完全 ready,请运行: ${theme.noun(out.commands.verify)}`);
|
|
1765
2355
|
}
|
|
1766
2356
|
}
|
|
1767
2357
|
//# sourceMappingURL=dingtalk-agent.js.map
|