@xdxer/dingtalk-agent 0.1.5-beta.7 → 0.1.5-beta.9
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 +38 -0
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/dist/bin/dingtalk-agent.js +118 -15
- package/dist/bin/dingtalk-agent.js.map +1 -1
- package/dist/src/agent-audit.js +108 -9
- package/dist/src/agent-audit.js.map +1 -1
- package/dist/src/development-workspace.js +37 -1
- package/dist/src/development-workspace.js.map +1 -1
- package/dist/src/dws.js +9 -0
- package/dist/src/dws.js.map +1 -1
- package/dist/src/multica-deploy.js +347 -105
- package/dist/src/multica-deploy.js.map +1 -1
- package/dist/src/multica-provider.js +151 -27
- 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/promotion.js +2 -1
- package/dist/src/promotion.js.map +1 -1
- package/dist/src/schedule-plan.js +192 -24
- package/dist/src/schedule-plan.js.map +1 -1
- package/dist/src/skill-manager.js +76 -9
- package/dist/src/skill-manager.js.map +1 -1
- package/docs/AGENT-IN-PRODUCTION.md +255 -0
- package/docs/ARCHITECTURE.md +5 -1
- package/docs/PLATFORM-GUARDRAILS.md +188 -0
- package/docs/schemas/multica-deployment-plan.schema.json +3 -1
- package/docs/schemas/multica-deployment-receipt.schema.json +15 -1
- 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-status.schema.json +2 -0
- package/docs/schemas/workspace-scaffold.schema.json +1 -0
- package/lab/project-workspace/fake-multica-provider.mjs +97 -7
- package/lab/project-workspace/multica-deploy.fixture.json +2 -2
- package/lab/project-workspace/multica-readonly.fixture.json +2 -2
- package/lab/project-workspace/project.fixture.json +1 -1
- package/lab/robot-eval/suite.json +1 -1
- package/package.json +5 -2
- package/skills/core/dta-agent-compose/SKILL.md +1 -1
- package/skills/core/dta-agent-compose/references/drive-and-schedules.md +22 -2
- package/skills/core/dta-basic-behavior/SKILL.md +1 -1
- package/skills/core/dta-basic-behavior/references/memory-and-evolution.md +1 -1
- package/skills/core/dta-people-group-memory/SKILL.md +4 -4
- package/skills/core/dta-people-group-memory/references/model.md +1 -1
- package/skills/platforms/multica-dingtalk/PLATFORM.md +2 -2
- package/skills/platforms/multica-dingtalk/dta-deploy-multica/SKILL.md +6 -5
- package/skills/platforms/multica-dingtalk/dta-deploy-multica/references/multica-deployment-contract.md +14 -4
- package/skills/platforms/multica-dingtalk/dta-ops-multica/SKILL.md +33 -7
- package/skills/platforms/multica-dingtalk/dta-ops-multica/scripts/multica_ext.py +118 -12
- package/dist/src/map.js +0 -157
- package/dist/src/map.js.map +0 -1
|
@@ -3,7 +3,7 @@ name: dta-people-group-memory
|
|
|
3
3
|
description: 让办公 Agent 认识「人」和「会话(群/单聊)」并记住交互。需要判断某位同事是谁、在做什么、怎么和他协作、我们之前聊过什么,或需要判断某个群是干什么的、在推什么、谁说了算、这事在哪讨论过、我该不该在这儿说话时使用;用户要求「记住这个人/沉淀一下这次聊天/以后记得我的偏好」时也使用。未绑定存储前完全惰性,不写任何东西。
|
|
4
4
|
compatibility: Storage-agnostic. Ships with a DingTalk-document adapter and a local-Markdown adapter; the model and ingest layers never touch a specific medium. Binding happens on demand under the runtime identity.
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.3.
|
|
6
|
+
version: "0.3.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# 认识人 · 认识会话 · 记住交互
|
|
@@ -23,7 +23,7 @@ metadata:
|
|
|
23
23
|
└ adapters.md 介质特有的坑全部收在适配器里
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
**存储是双介质、按 section 种类分流**(`model.md` §三):叙事类正文(identity/chronicle/digest…)走**钉钉文档**(读整页、人打开看);注册表类(索引/水位/count/tombstone/roster)走 **AI
|
|
26
|
+
**存储是双介质、按 section 种类分流**(`model.md` §三):叙事类正文(identity/chronicle/digest…)走**钉钉文档**(读整页、人打开看);注册表类(索引/水位/count/tombstone/roster)走 **AI 表格**(**按 key / recordId 有界查**,不撞文档字符墙)。同一套模型层操作,registry 换成结构化介质就把一半病理关掉——这是端口解耦的现场兑现。**但 AI 表格的全量枚举仍会静默截断**(见四条不变式③):只有按 key/recordId 的有界查询能当存在性证据,别把它当"随便分页扫都安全"。
|
|
27
27
|
|
|
28
28
|
**解耦的检验标准**:如果这世界上只有本地 Markdown 文件这一种存储,模型层会长成同样的样子吗?会,才算解耦。凡是“因为钉钉文档会截断,所以本体要按月分页”这类推理,都必须改写成“适配器声明读取上限,通用算法据此分片”。
|
|
29
29
|
|
|
@@ -54,8 +54,8 @@ metadata:
|
|
|
54
54
|
## 四条不变式(模型层保证,不下放给适配器)
|
|
55
55
|
|
|
56
56
|
1. **原始层只增不删,派生层可重建。** 纠错用 `supersedes` 并列新增,不改写原件。
|
|
57
|
-
2.
|
|
58
|
-
3.
|
|
57
|
+
2. **写后必回读,且按 recordId 回读那一行,`success` 不算数。** 回读若靠全表扫或按键再查,会被截断骗到别的行或读空——只有拿写入拿到的 recordId 精确回读才作数。
|
|
58
|
+
3. **读不完整 ≠ 不存在。** 不只是"读报错了":AI 表格分页会**静默截断**——成功返回一个偏小的前缀却报 hasMore=false,没有任何完整性凭据。所以全量枚举一律当"未证明完整",绝不据此写"没有"、也绝不据此新建重复实体。要判"某人/某群不存在",用有界的按键查(命中 0 行),不要用全表扫。
|
|
59
59
|
4. **先写事实、后推水位。** 任一失败即本批作废、水位不动——水位虚高会让这批源事件永远不再被消化。
|
|
60
60
|
|
|
61
61
|
## 两个实体,一句话说清区别
|
|
@@ -145,4 +145,4 @@ watermark: { subject: <key>, channel: im|todo|doc, value: <绝对时间> }
|
|
|
145
145
|
4. 交叉归属三铁律。
|
|
146
146
|
5. 先写事实、后推水位。
|
|
147
147
|
6. 会话隐私档位白名单默认拒绝。
|
|
148
|
-
7. **"
|
|
148
|
+
7. **"读不完整 ≠ 不存在"**:不只是读报错——AI 表格分页会**静默截断**(成功返回偏小前缀却报 hasMore=false,无完整性凭据)。读失败、分页未完、授权缺失、以及**任何全量枚举**,一律当"未证明完整",记欠账、跳过本轮,**绝不写成"没有"**,也绝不据此新建重复实体。判"不存在"只能用有界的按键查(命中 0 行)。
|
|
@@ -34,7 +34,7 @@ Multica 是钉钉 FDE fork 的托管 Agent 平台:把一个 dingtalk-agent 数
|
|
|
34
34
|
- **run 幂等按 `(trigger, 计划时刻)`;漏掉的 fire 会塌缩成最近一次,迟到超过 5 分钟直接跳过。** 所以定点 cron 只用来开窗,不能用来保证「某时刻必须发生」——完成判据必须逾期后持续为真。
|
|
35
35
|
- **多条节律不要落在同一分钟。** 外发类的留痕写在动作之后、没有锁,并发唤醒会把同一条消息发两遍给第三方。
|
|
36
36
|
|
|
37
|
-
`autopilot-create` 的 stdout
|
|
37
|
+
`autopilot-create` 的 stdout 是**纯 JSON**(`scheduled: ...` 进度提示走 stderr,`json.load(stdout)` 不会炸)。真正的坑是 **create 非幂等**:重复创建就是重复的节律、重复的外发。建完一律用 `autopilot-list` / `autopilot-get` 独立回读条数、cron、时区、提示词、启用状态和 `next_run_at`,不要相信创建响应。
|
|
38
38
|
|
|
39
39
|
`multica_ext.py` **没有** `autopilot-delete`;误建的先 `autopilot-set-status --status paused` 止血,再按需 `DELETE /api/autopilots/{id}`(需带 `X-Workspace-Id` 头,否则 400)。
|
|
40
40
|
|
|
@@ -48,7 +48,7 @@ Multica 是钉钉 FDE fork 的托管 Agent 平台:把一个 dingtalk-agent 数
|
|
|
48
48
|
|
|
49
49
|
1. **dws 自动路径(首选)**:`dws dev app list` 选已有应用或经用户确认 `dws dev app robot submit` 新建 →(异步,`robot result` 轮询到 SUCCESS)→ `dws dev app credentials get --unified-app-id <id>` 取 AppKey/AppSecret → `dingtalk-bind --agent <id> --client-id <AppKey> --client-secret-stdin`。
|
|
50
50
|
- 机器人 desc 只允许中英文数字和特定标点(冒号/括号会触发 67010 失败)。
|
|
51
|
-
-
|
|
51
|
+
- **不加 `--allow-unbound` 的默认行为是拦截**:任何未把钉钉账号绑定到 Multica 的发送者(包括机器人 owner 本人)@ 机器人只会收到账号绑定引导,不产生任何 Run——验收第一句话就会撞上。想让未绑定账号的用户直接对话(快速验收/对客服务),绑定时加 `--allow-unbound`(connect-to-customers 模式:以安装者身份服务未绑定发送者),或先走 `dingtalk-account-begin --binding-mode message` 完成绑定。
|
|
52
52
|
2. **复用已有安装**:`dingtalk-list` 查看现有机器人安装换绑。
|
|
53
53
|
3. **扫码兜底**:`dingtalk-begin` 产出扫码链接交用户完成(不要用 `--wait` 阻塞会话)。
|
|
54
54
|
|
|
@@ -3,7 +3,7 @@ name: dta-deploy-multica
|
|
|
3
3
|
description: 当开发者要把 dingtalk-agent Agent Project 部署、更新、检查、恢复或 retire 到 Multica Workspace,或把通过指定 Eval gate 的本地版本晋级、把脱敏反馈转成待评审 Eval candidate 时使用。只编排 dta 的稳定 deploy/promote/observe CLI,不直接调用 Multica 写命令,不创建 Trigger,不热改 Agent 本体或 Skill,也不替用户登录或持有凭据。
|
|
4
4
|
compatibility: Requires dingtalk-agent and an authenticated Multica CLI profile for live apply/status readback.
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.5.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# 部署 dingtalk-agent 到 Multica
|
|
@@ -13,11 +13,12 @@ metadata:
|
|
|
13
13
|
## 工作顺序
|
|
14
14
|
|
|
15
15
|
1. 先运行 `dta workspace doctor <name> --json` 和 `dta workspace inspect <name> --execute --yes --json`。登录、profile 默认 Workspace、显式 Workspace、Runtime、Agent ID 链或 Skill 唯一性不一致时停止;不要用 `--yes` 越过。
|
|
16
|
-
2. 运行 `dta deploy --workspace <name> --dry-run --json`,向用户展示 `planId`、本体/Skill hash、forward/rollback 写预算、将读取和写入的资源类型、删除/retire 范围以及不包含 Trigger。
|
|
16
|
+
2. 运行 `dta deploy --workspace <name> --dry-run --json`,向用户展示 `planId`、本体/Skill hash、forward/rollback 写预算、将读取和写入的资源类型、删除/retire 范围以及不包含 Trigger。plan 对中间状态敏感:dry-run 之后、apply 之前不要插入 `workspace inspect` 等操作,否则 planId 作废,重跑 dry-run 后立即 apply。
|
|
17
17
|
3. 只有用户明确确认这份 plan 后,才运行 `dta deploy --workspace <name> --plan-id <id> --yes --json`。旧 planId、当前选择的 Workspace 或显示名都不能代替稳定 ID。
|
|
18
|
-
4. 返回 pending、超时或本地 Receipt 写入失败时,不重复 apply。load-smoke Issue 已创建但没有任何 task 时,CLI 会且只会按稳定 Agent ID 在同一 Issue 留下一条受控 `@` 评论恢复首次入队;已有 queued/running/terminal task 时绝不重复触发。随后运行 `dta deploy --workspace <name> --status --operation-id <id> --execute --yes --json` 做独立回读和 reconcile
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
18
|
+
4. 返回 pending、超时或本地 Receipt 写入失败时,不重复 apply。load-smoke Issue 已创建但没有任何 task 时,CLI 会且只会按稳定 Agent ID 在同一 Issue 留下一条受控 `@` 评论恢复首次入队;已有 queued/running/terminal task 时绝不重复触发。随后运行 `dta deploy --workspace <name> --status --operation-id <id> --execute --yes --json` 做独立回读和 reconcile。注意不带 `--execute` 的 `--status` 返回的是 apply 时刻的**冻结回执**(首次部署 smoke 字段常是空的 pending 快照,输出会标 `receiptFrozen` 并给出刷新命令);smoke 失败若分类为 `skills-not-visible`(冷沙箱尚未物化 Skill),状态收敛为 `verifying` 而非 `failed`,用带 `--execute --yes` 的 status 重试即可。
|
|
19
|
+
5. 换 runtime 只有两条正路:显式迁移用 `dta deploy --rebind-runtime`(先 dry-run,planId 绑定迁移前后 runtime,Receipt 记录 from/to);退役用 `--retire`(不受 `agent.runtime-drift` 阻挡)。不要原地改 env 里的 runtime id 硬顶普通 apply——那会被 fail-closed 挡下。
|
|
20
|
+
6. 只有远端 Agent/Runtime/Skill tree/assignment 回读一致,System Prompt 与本地人类可读 Definition 原文一致,并且 Basic、Role 的随机 load smoke 轨迹通过,Workspace 才能进入 `ready`。
|
|
21
|
+
7. 退役先 dry-run,再用冻结 plan 执行 `--retire`;只 archive 精确 Agent,保留 Skill、Issue、语义存储和 Artifact,不把 retire 当成删除。
|
|
21
22
|
|
|
22
23
|
完整命令、状态机与失败恢复见 [multica-deployment-contract.md](references/multica-deployment-contract.md)。
|
|
23
24
|
|
|
@@ -23,6 +23,10 @@ dta workspace run multica-dev --prompt-file readonly-question.md --execute --yes
|
|
|
23
23
|
# 只 archive 精确 Agent;不删除 Skill、Storage、Issue 或 Trigger
|
|
24
24
|
dta deploy --workspace multica-dev --retire --dry-run --json
|
|
25
25
|
dta deploy --workspace multica-dev --retire --plan-id <planId> --yes --json
|
|
26
|
+
|
|
27
|
+
# 显式把 Agent 迁到 env 当前指向的 runtime(planId 绑定迁移前后 runtime,Receipt 记 from/to)
|
|
28
|
+
dta deploy --workspace multica-dev --dry-run --rebind-runtime --json
|
|
29
|
+
dta deploy --workspace multica-dev --rebind-runtime --plan-id <planId> --yes --json
|
|
26
30
|
```
|
|
27
31
|
|
|
28
32
|
## 状态机
|
|
@@ -33,7 +37,10 @@ planned → applying → verifying → ready
|
|
|
33
37
|
ready → retired
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
- 每次 apply 前重新读取 W3 的 profile/auth/Workspace/Runtime/Agent/Skill ID 链;与 plan 不一致时在任何写入前失败。
|
|
40
|
+
- 每次 apply 前重新读取 W3 的 profile/auth/Workspace/Runtime/Agent/Skill ID 链;与 plan 不一致时在任何写入前失败。scope 类失败带结构化 `failureDetails`(expected/actual/hint);provider CLI 自身失败时其 stderr 原文透传到终端,证据文件仍只存 hash。
|
|
41
|
+
- 目标 runtime 的 provider 必须在受支持词汇表内(当前 `opencode`);hermes 等不受支持 provider 在任何远端写入前 fail closed(`scope.runtime-provider-unsupported`)——那类 runtime 上 workspace skill 一律加载不到,部署"成功"也是死局。
|
|
42
|
+
- Agent 的 runtime 归属漂移(`agent.runtime-drift`)只挡普通 apply:显式迁移用 `--rebind-runtime`,退役用 `--retire`,都不被漂移锁死。
|
|
43
|
+
- dta 调用 multica CLI 前会从子进程环境剔除保留变量 `MULTICA_AGENT_ID`(切 Agent 身份鉴权、误报未登录)、`MULTICA_SERVER_URL` 与 `MULTICA_TOKEN`(把 endpoint/身份从具名 profile 脚下换走——shell 里残留的预发 URL 绝不能重定向生产部署);剔除记录在 inspection 的 `envSanitized`。
|
|
37
44
|
- plan 同时冻结 forward/rollback 最大写预算;最多 32 个受管 Skill、每个最多 128 个 supporting files,CLI 在每个 Provider mutation 前检查预算。
|
|
38
45
|
- Provider 调用超时属于结果未知:先保存 `reconciling` operation,再只读 status;不盲目重放 create/update。
|
|
39
46
|
- load-smoke Issue 创建成功但 `issue runs` 为空时,说明 Issue 已持久化而首次入队未落地;apply 只允许按计划中稳定 Agent ID 在同一 Issue 留下一条受控 `mention://agent/<id>` 评论恢复一次。只要已有 task(包括 queued/running/terminal),就不得触发第二次运行。
|
|
@@ -48,10 +55,13 @@ ready → retired
|
|
|
48
55
|
3. Basic、Role Skill 的完整文件树 hash 一致;
|
|
49
56
|
4. assignment 精确等于部署计划,不包含隐式平台启动 Skill;
|
|
50
57
|
5. smoke task completed;
|
|
51
|
-
6. 轨迹中 Basic、每个 Role
|
|
52
|
-
7.
|
|
53
|
-
8.
|
|
58
|
+
6. 轨迹中 Basic、每个 Role 都有装载证据——按目标 runtime 的 trace 词汇解读:Host `skill` 工具调用(工具本身就是装载机制),或云 runtime `read_file` **结果**中的 `**Skill loaded**` banner。装载「尝试」(`Loading skill 'X'` 的 tool_use,失败时同样会出现)与模型自述文本都不算证据,banner 也只认可信工具(read_file/skill)的 tool_result——terminal/bash 的输出是模型指定的,echo 伪造不算数;
|
|
59
|
+
7. 精确 JSON 回执可回读——优先从 Issue comment 本身取证(平台回读,与工具词汇无关),其次是回复文本 / 受限收口文件;
|
|
60
|
+
8. 轨迹不含 dws(工具名或 shell 命令);
|
|
61
|
+
9. operation 与 Receipt hash 完整,Workspace state 绑定当前 desired/deployment hash。
|
|
54
62
|
|
|
55
63
|
任一门禁失败都保持 `verifying` 或进入 `failed`,不能用本地 OpenCode 通过、旧 smoke、目录存在或模型自述覆盖。
|
|
56
64
|
|
|
65
|
+
**ready 证明什么、不证明什么**:ready 证明每个必需 Skill 有装载证据、精确回执已落盘、无 dws 调用;**不再**证明「轨迹里每个工具都在白名单内」——无害的额外工具/命令不判死(旧的负向白名单在健康云 runtime 上造成过结构性假阴性),词汇表不认识的工具名记录在 `smoke.unrecognizedTools` 供诊断。smoke 失败若分类为 `skills-not-visible`(任务终态但轨迹显示 Skill not found,冷沙箱推送物化竞态),状态收敛为 `verifying`:apply 在预算内做有限次受控 mention 重试,`--status --execute --yes` 也可补一次(该情形如实上报 `remoteWrite`)。重试以「最新 run 的 taskId 变化」为新 run 出现的判据——mention 触发的 run 在平台上异步创建,等不到新 run 只会停手,不会重复 mention;重试评论本身的写入抖动只终止重试并保持 `verifying`,绝不触发对已验证部署的回滚。
|
|
66
|
+
|
|
57
67
|
`workspace run` 在创建 Issue 前重新核对精确 Workspace、Runtime、Agent 与 assignment。报告记录 Issue/task 状态、实际 Skill trace、回答、轮询次数和脱敏调用;同一状态的重复轮询只保留一条证据。超时后仍没有关联 task 时返回 `task.not-created`,并把 Issue 状态写进 `taskDiagnostic`,不能把 Issue 创建成功算成 Agent 已运行。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: dta-ops-multica
|
|
3
3
|
description: External Python wrapper defining the full developer chain for delivering an agent on the Multica (DingTalk-FDE fork) platform — workspace bootstrap, runtime/agent provisioning, skill assembly (push/pull), DingTalk robot & account binding, execution-trace observability, cron scheduling (autopilot), and health checks. Use when provisioning, operating, debugging, or observing a Multica-managed agent from the command line with only an endpoint and a token.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "0.
|
|
5
|
+
version: "0.3.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Multica FDE External Skill
|
|
@@ -60,8 +60,13 @@ python3 $PY agent-create --workspace fde-team --name "FDE 教练" \
|
|
|
60
60
|
merge, never clobber; repo registration needs owner/admin. Slugs:
|
|
61
61
|
`^[a-z0-9]+(-[a-z0-9]+)*$`, reserved list enforced. An agent needs a runtime:
|
|
62
62
|
FC-E2B cloud (`runtime-create-fc`, requires server-side FC-E2B enablement) or
|
|
63
|
-
a local daemon. `runtime-
|
|
64
|
-
|
|
63
|
+
a local daemon. `runtime-create-fc` injects `--provider opencode` by default —
|
|
64
|
+
the SERVER default is hermes, where workspace skills structurally never load
|
|
65
|
+
(agents answer "Skill not found" with zero warning); only pass
|
|
66
|
+
`--provider hermes` if that is explicitly what you want. `runtime-templates`
|
|
67
|
+
lists templates; when `--template-id` is omitted the sole READY template is
|
|
68
|
+
auto-picked (waiting templates are excluded; several ready ones → the error
|
|
69
|
+
lists candidates sorted by `created_at`, pick the newest).
|
|
65
70
|
|
|
66
71
|
### 2 — Define & assemble: where the agent comes from, what skills it has
|
|
67
72
|
|
|
@@ -94,10 +99,19 @@ python3 $PY dingtalk-bind --agent <uuid> --client-id <AppKey> --client-secret-st
|
|
|
94
99
|
python3 $PY dingtalk-begin --agent <uuid> --wait
|
|
95
100
|
python3 $PY dingtalk-list ; python3 $PY dingtalk-revoke --installation <uuid>
|
|
96
101
|
# Personal-account binding family (dingtalk_account):
|
|
97
|
-
python3 $PY dingtalk-account-begin --agent <uuid> # prints QR URL
|
|
98
|
-
python3 $PY dingtalk-account-list
|
|
102
|
+
python3 $PY dingtalk-account-begin --agent <uuid> --binding-mode message # prints QR URL
|
|
103
|
+
python3 $PY dingtalk-account-list
|
|
104
|
+
python3 $PY dingtalk-account-unbind --installation <uuid> --binding-mode message
|
|
99
105
|
```
|
|
100
106
|
|
|
107
|
+
`binding_mode` is server-enforced (400 `invalid_binding_mode` without it) and
|
|
108
|
+
its wire contract is asymmetric: `begin` takes it in the JSON body, `unbind`
|
|
109
|
+
in the query string. `message` maps the sender's identity for conversation
|
|
110
|
+
routing only; `identity` grants the agent a persistent delegated DingTalk
|
|
111
|
+
identity — never choose it silently on the user's behalf. Without
|
|
112
|
+
`--allow-unbound` on the robot install, senders not account-bound to Multica
|
|
113
|
+
(the robot owner included) only receive a binding prompt.
|
|
114
|
+
|
|
101
115
|
Bind and unbind always come in pairs: robot `dingtalk-bind`/`dingtalk-begin`
|
|
102
116
|
↔ `dingtalk-revoke`; account `dingtalk-account-begin` ↔
|
|
103
117
|
`dingtalk-account-unbind`. `dingtalk-account-begin` returns the binding link
|
|
@@ -142,6 +156,10 @@ that desired state against live:
|
|
|
142
156
|
|
|
143
157
|
```bash
|
|
144
158
|
dta schedule plan --workspace <name> --json # desired state, provider-mapped
|
|
159
|
+
# EXIT 2 = do NOT reconcile. The manifest has schedules that collide at the same
|
|
160
|
+
# instant, that can't be proven not to (cross-timezone / cron extension syntax), or
|
|
161
|
+
# that lack a data-derived completion. Deploying anyway creates one autopilot per
|
|
162
|
+
# entry → the same message goes out twice. Fix the manifest, don't override.
|
|
145
163
|
python3 $PY autopilot-list # live; match declared↔live by title
|
|
146
164
|
# create only missing, update drifted, delete extra — autopilot-create is NOT
|
|
147
165
|
# idempotent, so ALWAYS list first; blind-creating duplicates → duplicate外发.
|
|
@@ -175,8 +193,16 @@ python3 $PY agent-check --agent <uuid>
|
|
|
175
193
|
Composite JSON report: agent status/archived, runtime health (online /
|
|
176
194
|
unstable = offline but seen <5 min ago / offline / missing), active tasks +
|
|
177
195
|
last outcome (with `failure_reason`), recent failure count, DingTalk robot +
|
|
178
|
-
account binding state, plus a `checks[]` verdict list
|
|
179
|
-
|
|
196
|
+
account binding state, plus a `checks[]` verdict list. **BREAKING (0.3.0)**:
|
|
197
|
+
the single `healthy` boolean is removed — it reported green while users could
|
|
198
|
+
not reach the agent at all (no robot / account unbound are only warns).
|
|
199
|
+
Consume the split verdict instead: `infrastructureHealthy` (agent/runtime/
|
|
200
|
+
tasks have no fail) and `userReady` (infrastructure healthy AND robot bound
|
|
201
|
+
AND `dws_identity` bound), with `reasons[]` naming every gap and `nextAction`
|
|
202
|
+
carrying the single next command. `userReadyCaveat` records the one upstream
|
|
203
|
+
blind spot: installations don't read back effective `allow_unbound`, so a
|
|
204
|
+
robot bound with that flag serves unbound senders even when
|
|
205
|
+
`userReady=false`.
|
|
180
206
|
|
|
181
207
|
### 7 — Basics: issues, comments, chat, members, tokens
|
|
182
208
|
|
|
@@ -313,15 +313,33 @@ def cmd_runtime_create_fc(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
|
313
313
|
# The server resolves template_id against id, template, or name;
|
|
314
314
|
# id is best-effort and may be absent.
|
|
315
315
|
ref = lambda t: t.get("id") or t.get("template") or t.get("name") # noqa: E731
|
|
316
|
-
|
|
316
|
+
status_of = lambda t: str(t.get("status") or "").lower() # noqa: E731
|
|
317
|
+
# waiting 等未就绪状态选了也建不起来;status 字段缺失时保守放行(Observed 字段)。
|
|
318
|
+
usable = [t for t in templates if not status_of(t) or status_of(t) == "ready"]
|
|
319
|
+
if not usable:
|
|
317
320
|
fail(
|
|
318
|
-
"
|
|
319
|
-
+ ", ".join(f"{ref(t)}
|
|
321
|
+
"no ready FC-E2B templates (all "
|
|
322
|
+
+ ", ".join(f"{ref(t)}={status_of(t) or 'unknown'}" for t in templates)
|
|
323
|
+
+ ")"
|
|
320
324
|
)
|
|
321
|
-
|
|
325
|
+
if len(usable) > 1:
|
|
326
|
+
newest = max(usable, key=lambda t: str(t.get("created_at") or ""))
|
|
327
|
+
fail(
|
|
328
|
+
"multiple ready templates, pass --template-id: "
|
|
329
|
+
+ ", ".join(
|
|
330
|
+
f"{ref(t)} ({t.get('name') or t.get('template')}, "
|
|
331
|
+
f"{status_of(t) or 'unknown'}, created {t.get('created_at') or '?'})"
|
|
332
|
+
for t in sorted(usable, key=lambda t: str(t.get("created_at") or ""), reverse=True)
|
|
333
|
+
)
|
|
334
|
+
+ f"; component_versions 通常完全相同,选最新 ready 即可(推荐 {ref(newest)})"
|
|
335
|
+
)
|
|
336
|
+
template_id = ref(usable[0])
|
|
322
337
|
if not template_id:
|
|
323
338
|
fail("template list entry has no usable identifier")
|
|
324
|
-
note(
|
|
339
|
+
note(
|
|
340
|
+
f"using template {template_id} ({usable[0].get('name') or usable[0].get('template')}, "
|
|
341
|
+
f"{status_of(usable[0]) or 'unknown'})"
|
|
342
|
+
)
|
|
325
343
|
body: Dict[str, Any] = {"template_id": template_id}
|
|
326
344
|
for flag in ("name", "provider", "visibility"):
|
|
327
345
|
value = getattr(args, flag, None)
|
|
@@ -397,7 +415,15 @@ def cmd_dingtalk_bind(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
|
397
415
|
"robot_code": args.robot_code or args.client_id,
|
|
398
416
|
"allow_unbound": args.allow_unbound,
|
|
399
417
|
}
|
|
400
|
-
|
|
418
|
+
res = api(ctx, "POST", f"/api/workspaces/{ws_id}/dingtalk/install/manual", body=body)
|
|
419
|
+
if not args.allow_unbound:
|
|
420
|
+
note(
|
|
421
|
+
"allow_unbound=false: senders whose DingTalk account is NOT bound to Multica "
|
|
422
|
+
"(including the robot owner) only get a binding prompt when they @ the robot. "
|
|
423
|
+
"For acceptance/customer-facing use, either run dingtalk-account-begin first or "
|
|
424
|
+
"re-bind with --allow-unbound (upsert; installation id unchanged)."
|
|
425
|
+
)
|
|
426
|
+
out(res)
|
|
401
427
|
|
|
402
428
|
|
|
403
429
|
def cmd_dingtalk_begin(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
@@ -450,7 +476,9 @@ def cmd_dingtalk_account_begin(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
|
450
476
|
ctx,
|
|
451
477
|
"POST",
|
|
452
478
|
f"/api/workspaces/{ws_id}/dingtalk/account-bindings/begin",
|
|
453
|
-
|
|
479
|
+
# Server enforces binding_mode (400 invalid_binding_mode without it): message-only
|
|
480
|
+
# identity mapping vs delegated identity. Wire contract: begin reads it from the BODY.
|
|
481
|
+
body={"agent_id": args.agent, "binding_mode": args.binding_mode},
|
|
454
482
|
)
|
|
455
483
|
note(f"scan to bind account: {res.get('qr_code_url')}")
|
|
456
484
|
out(res)
|
|
@@ -463,7 +491,14 @@ def cmd_dingtalk_account_list(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
|
463
491
|
|
|
464
492
|
def cmd_dingtalk_account_unbind(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
465
493
|
ws_id = resolve_workspace_id(ctx)
|
|
466
|
-
|
|
494
|
+
# Wire contract asymmetry (verified live): unbind reads binding_mode from the QUERY
|
|
495
|
+
# string; putting it in the DELETE body still returns 400 invalid_binding_mode.
|
|
496
|
+
api(
|
|
497
|
+
ctx,
|
|
498
|
+
"DELETE",
|
|
499
|
+
f"/api/workspaces/{ws_id}/dingtalk/account-bindings/{args.installation}"
|
|
500
|
+
f"?binding_mode={urllib.parse.quote(args.binding_mode)}",
|
|
501
|
+
)
|
|
467
502
|
out({"unbound": True})
|
|
468
503
|
|
|
469
504
|
|
|
@@ -675,6 +710,38 @@ def cmd_agent_check(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
|
675
710
|
json.dumps(account.get("dws_identity"), ensure_ascii=False) if account else "no account binding",
|
|
676
711
|
)
|
|
677
712
|
|
|
713
|
+
# 基础设施健康 ≠ 用户可用:机器人未绑/账号 unbound 时用户发消息只会收到绑定引导、
|
|
714
|
+
# 不产生任何 Run,但没有一项是 "fail"。旧的单布尔 `healthy` 正是把这两层混成一团、
|
|
715
|
+
# 让 operator 看着全绿而用户侧完全不工作的祸根——按验收证据(P-01/M-06)拆开并移除。
|
|
716
|
+
infra_checks = {"agent", "runtime", "last_task", "recent_tasks"}
|
|
717
|
+
infrastructure_healthy = all(
|
|
718
|
+
c["status"] != "fail" for c in checks if c["check"] in infra_checks
|
|
719
|
+
)
|
|
720
|
+
robot_ok = bool(robot)
|
|
721
|
+
account_bound = bool(account and (account.get("dws_identity") or {}).get("status") == "bound")
|
|
722
|
+
user_ready = infrastructure_healthy and robot_ok and account_bound
|
|
723
|
+
reasons = [
|
|
724
|
+
f"{c['check']}: {c['detail']}"
|
|
725
|
+
for c in checks
|
|
726
|
+
if (c["check"] in infra_checks and c["status"] == "fail")
|
|
727
|
+
or (c["check"] in ("dingtalk_robot", "dingtalk_account") and c["status"] != "ok")
|
|
728
|
+
]
|
|
729
|
+
if agent.get("archived_at"):
|
|
730
|
+
next_action = "agent restore(或重新部署)后再检查"
|
|
731
|
+
elif runtime_health in ("missing", "offline"):
|
|
732
|
+
next_action = "runtime-list 核对;必要时 runtime-create-fc --provider opencode 重建并迁移"
|
|
733
|
+
elif not robot_ok:
|
|
734
|
+
next_action = (
|
|
735
|
+
f"python3 multica_ext.py dingtalk-bind --agent {args.agent} "
|
|
736
|
+
"--client-id <AppKey> --client-secret-stdin(或 dingtalk-begin 扫码)"
|
|
737
|
+
)
|
|
738
|
+
elif not account_bound:
|
|
739
|
+
next_action = (
|
|
740
|
+
f"python3 multica_ext.py dingtalk-account-begin --agent {args.agent} "
|
|
741
|
+
"--binding-mode message(或重绑机器人加 --allow-unbound)"
|
|
742
|
+
)
|
|
743
|
+
else:
|
|
744
|
+
next_action = ""
|
|
678
745
|
out({
|
|
679
746
|
"agent": {k: agent.get(k) for k in ("id", "name", "status", "model", "runtime_id", "archived_at")},
|
|
680
747
|
"runtime": {
|
|
@@ -683,7 +750,14 @@ def cmd_agent_check(ctx: Ctx, args: argparse.Namespace) -> None:
|
|
|
683
750
|
},
|
|
684
751
|
"tasks": {"active": len(active), "last_outcome_status": (last_outcome or {}).get("status")},
|
|
685
752
|
"checks": checks,
|
|
686
|
-
"
|
|
753
|
+
"infrastructureHealthy": infrastructure_healthy,
|
|
754
|
+
"userReady": user_ready,
|
|
755
|
+
"reasons": reasons,
|
|
756
|
+
"nextAction": next_action,
|
|
757
|
+
"userReadyCaveat": (
|
|
758
|
+
"安装回读不含 effective allow_unbound(上游缺口 MUL-005):若绑定时开启过 "
|
|
759
|
+
"allow_unbound,未绑定账号的发送者实际也能对话"
|
|
760
|
+
),
|
|
687
761
|
"warnings": sum(1 for c in checks if c["status"] == "warn"),
|
|
688
762
|
})
|
|
689
763
|
|
|
@@ -936,11 +1010,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
936
1010
|
p = cmd(
|
|
937
1011
|
"runtime-create-fc",
|
|
938
1012
|
cmd_runtime_create_fc,
|
|
939
|
-
"Create an FC-E2B cloud runtime
|
|
1013
|
+
"Create an FC-E2B cloud runtime (provider defaults to opencode); "
|
|
1014
|
+
"picks the sole READY template when --template-id is omitted",
|
|
940
1015
|
)
|
|
941
1016
|
p.add_argument("--template-id", dest="template_id")
|
|
942
1017
|
p.add_argument("--name")
|
|
943
|
-
p.add_argument(
|
|
1018
|
+
p.add_argument(
|
|
1019
|
+
"--provider",
|
|
1020
|
+
default="opencode",
|
|
1021
|
+
help="runtime provider; defaults to opencode — the only provider dta's skill-loading "
|
|
1022
|
+
"contract supports. The SERVER default is hermes, where workspace skills never load "
|
|
1023
|
+
"(agents report 'Skill not found' with zero warning); pass --provider hermes only if "
|
|
1024
|
+
"you explicitly want that",
|
|
1025
|
+
)
|
|
944
1026
|
p.add_argument("--visibility", choices=["private", "public"])
|
|
945
1027
|
|
|
946
1028
|
p = cmd("agent-list", cmd_agent_list, "List agents in the workspace")
|
|
@@ -984,7 +1066,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
984
1066
|
p.add_argument("--client-secret", dest="client_secret", help="DingTalk AppSecret (prefer stdin/env)")
|
|
985
1067
|
p.add_argument("--client-secret-stdin", dest="client_secret_stdin", action="store_true")
|
|
986
1068
|
p.add_argument("--client-secret-env", dest="client_secret_env", metavar="VAR")
|
|
987
|
-
p.add_argument(
|
|
1069
|
+
p.add_argument(
|
|
1070
|
+
"--allow-unbound",
|
|
1071
|
+
dest="allow_unbound",
|
|
1072
|
+
action="store_true",
|
|
1073
|
+
help="serve senders whose DingTalk account is not bound to Multica "
|
|
1074
|
+
"(connect-to-customers). WITHOUT this flag such senders — the robot owner "
|
|
1075
|
+
"included — only receive a binding prompt",
|
|
1076
|
+
)
|
|
988
1077
|
|
|
989
1078
|
p = cmd(
|
|
990
1079
|
"dingtalk-begin",
|
|
@@ -1000,6 +1089,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1000
1089
|
|
|
1001
1090
|
p = cmd("dingtalk-account-begin", cmd_dingtalk_account_begin, "Start binding a personal DingTalk account to an agent")
|
|
1002
1091
|
p.add_argument("--agent", required=True, help="agent uuid")
|
|
1092
|
+
p.add_argument(
|
|
1093
|
+
"--binding-mode",
|
|
1094
|
+
dest="binding_mode",
|
|
1095
|
+
choices=("message", "identity"),
|
|
1096
|
+
default="message",
|
|
1097
|
+
help="binding capability: message-only identity mapping (default), or delegated "
|
|
1098
|
+
"DingTalk identity — identity grants a persistent execution identity, never pick "
|
|
1099
|
+
"it silently for the user",
|
|
1100
|
+
)
|
|
1003
1101
|
|
|
1004
1102
|
cmd("dingtalk-account-list", cmd_dingtalk_account_list, "List personal DingTalk account bindings")
|
|
1005
1103
|
|
|
@@ -1009,6 +1107,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1009
1107
|
"Remove a personal DingTalk account binding (row kept as revoked for audit)",
|
|
1010
1108
|
)
|
|
1011
1109
|
p.add_argument("--installation", required=True, help="installation uuid")
|
|
1110
|
+
p.add_argument(
|
|
1111
|
+
"--binding-mode",
|
|
1112
|
+
dest="binding_mode",
|
|
1113
|
+
choices=("message", "identity"),
|
|
1114
|
+
default="message",
|
|
1115
|
+
help="binding capability to remove: message-only identity mapping (default) or "
|
|
1116
|
+
"delegated DingTalk identity",
|
|
1117
|
+
)
|
|
1012
1118
|
|
|
1013
1119
|
p = cmd("agent-tasks", cmd_agent_tasks, "List an agent's task run history")
|
|
1014
1120
|
p.add_argument("--agent", required=True, help="agent uuid")
|
package/dist/src/map.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
// Managed Agents Platform(MAP)注册表与工作区归属解析。
|
|
2
|
-
//
|
|
3
|
-
// 一个 Agent 工作区归属于至多一个托管平台;归属解析顺序固定:
|
|
4
|
-
// DTA_MAP 环境变量 > dingtalk-agent.json#map > .dingtalk-agent/map.json > 按 Provider 声明推断。
|
|
5
|
-
// 平台相关的方法学不写进 CLI——它们随对应平台的 Skill 包分发,只在用户选择该平台时安装。
|
|
6
|
-
// 平台命令(如 deploy/promote/observe)必须先通过归属门禁;coming-soon 平台一律 fail-closed。
|
|
7
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
-
import { dirname, join, parse, resolve } from 'node:path';
|
|
9
|
-
import { findAgentProjectRoot } from './development-workspace.js';
|
|
10
|
-
import { installGlobalSkill } from './skill-manager.js';
|
|
11
|
-
export const MANAGED_AGENT_PLATFORMS = [
|
|
12
|
-
{
|
|
13
|
-
name: 'multica-dingtalk',
|
|
14
|
-
label: 'Multica (DingTalk)',
|
|
15
|
-
status: 'supported',
|
|
16
|
-
description: '钉钉 Multica 托管平台:受控部署、晋级与观测回流。',
|
|
17
|
-
skills: ['dingtalk-agent-deploy', 'dingtalk-agent-boot-multica'],
|
|
18
|
-
commands: ['deploy', 'promote', 'observe'],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: 'deap',
|
|
22
|
-
label: 'DEAP',
|
|
23
|
-
status: 'coming-soon',
|
|
24
|
-
description: '敬请期待。',
|
|
25
|
-
skills: [],
|
|
26
|
-
commands: [],
|
|
27
|
-
},
|
|
28
|
-
];
|
|
29
|
-
export const MAP_FILE = '.dingtalk-agent/map.json';
|
|
30
|
-
const PROJECT_FILE = 'dingtalk-agent.json';
|
|
31
|
-
export function mapDefinition(name) {
|
|
32
|
-
return MANAGED_AGENT_PLATFORMS.find((item) => item.name === name) || null;
|
|
33
|
-
}
|
|
34
|
-
export function resolveMap(start = process.cwd(), env = process.env) {
|
|
35
|
-
const fromEnv = String(env.DTA_MAP || '').trim();
|
|
36
|
-
if (fromEnv)
|
|
37
|
-
return resolution(fromEnv, 'env', '');
|
|
38
|
-
const projectRoot = findAgentProjectRoot(start);
|
|
39
|
-
if (projectRoot) {
|
|
40
|
-
const manifestPath = join(projectRoot, PROJECT_FILE);
|
|
41
|
-
if (existsSync(manifestPath) && !lstatSync(manifestPath).isSymbolicLink()) {
|
|
42
|
-
const manifest = readJsonOrNull(manifestPath);
|
|
43
|
-
const declared = typeof manifest?.map === 'string' ? manifest.map.trim() : '';
|
|
44
|
-
if (declared)
|
|
45
|
-
return resolution(declared, 'project-manifest', manifestPath);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const mapPath = findUp(start, MAP_FILE);
|
|
49
|
-
if (mapPath) {
|
|
50
|
-
const stored = readJsonOrNull(mapPath);
|
|
51
|
-
const declared = typeof stored?.map === 'string' ? stored.map.trim() : '';
|
|
52
|
-
if (declared)
|
|
53
|
-
return resolution(declared, 'map-file', mapPath);
|
|
54
|
-
}
|
|
55
|
-
if (projectRoot) {
|
|
56
|
-
const manifest = readJsonOrNull(join(projectRoot, PROJECT_FILE));
|
|
57
|
-
const providers = Object.values(manifest?.workspaces || {})
|
|
58
|
-
.map((workspace) => workspace?.provider?.kind);
|
|
59
|
-
if (providers.includes('multica')) {
|
|
60
|
-
return resolution('multica-dingtalk', 'inferred', join(projectRoot, PROJECT_FILE));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return resolution(null, 'none', '');
|
|
64
|
-
}
|
|
65
|
-
export function useMap(start, name, options) {
|
|
66
|
-
const definition = mapDefinition(name);
|
|
67
|
-
if (!definition) {
|
|
68
|
-
throw new Error(`未知 managed agents platform: ${name}\n` +
|
|
69
|
-
`可用: ${MANAGED_AGENT_PLATFORMS.map((item) => `${item.name}(${statusLabel(item.status)})`).join('、')}`);
|
|
70
|
-
}
|
|
71
|
-
if (definition.status !== 'supported') {
|
|
72
|
-
throw new Error(`${definition.label} 敬请期待——当前版本尚未开放该平台。`);
|
|
73
|
-
}
|
|
74
|
-
const projectRoot = findAgentProjectRoot(start);
|
|
75
|
-
let written;
|
|
76
|
-
if (projectRoot && existsSync(join(projectRoot, PROJECT_FILE))) {
|
|
77
|
-
const manifestPath = join(projectRoot, PROJECT_FILE);
|
|
78
|
-
if (lstatSync(manifestPath).isSymbolicLink()) {
|
|
79
|
-
throw new Error(`${PROJECT_FILE} 不能是符号链接`);
|
|
80
|
-
}
|
|
81
|
-
const manifest = readJsonOrNull(manifestPath);
|
|
82
|
-
if (!manifest || typeof manifest !== 'object') {
|
|
83
|
-
throw new Error(`${PROJECT_FILE} 不是合法 JSON,无法写入 map 归属`);
|
|
84
|
-
}
|
|
85
|
-
manifest.map = definition.name;
|
|
86
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
87
|
-
written = manifestPath;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
const target = join(resolve(start), MAP_FILE);
|
|
91
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
92
|
-
writeFileSync(target, JSON.stringify({ $schema: 'dingtalk-agent/map@1', map: definition.name }, null, 2) + '\n');
|
|
93
|
-
written = target;
|
|
94
|
-
}
|
|
95
|
-
const skillInstalls = [];
|
|
96
|
-
if (options.installSkills !== false) {
|
|
97
|
-
for (const skill of definition.skills) {
|
|
98
|
-
skillInstalls.push(installGlobalSkill(options.packageRoot, {
|
|
99
|
-
name: skill, home: options.home, env: options.env,
|
|
100
|
-
}));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return { ...resolveMap(start, options.env || process.env), written, skillInstalls };
|
|
104
|
-
}
|
|
105
|
-
/** deploy/promote/observe 等平台命令的归属门禁:未归属、未知或 coming-soon 一律 fail-closed。 */
|
|
106
|
-
export function requireSupportedMap(command, start = process.cwd(), env = process.env) {
|
|
107
|
-
const resolved = resolveMap(start, env);
|
|
108
|
-
if (!resolved.map) {
|
|
109
|
-
throw new Error(`当前工作区未声明 managed agents platform,无法执行 \`${command}\`。\n` +
|
|
110
|
-
'先运行 `dta map use multica-dingtalk`,或设置 DTA_MAP 环境变量。');
|
|
111
|
-
}
|
|
112
|
-
if (!resolved.definition) {
|
|
113
|
-
throw new Error(`工作区声明了未知 platform: ${resolved.map}(来源 ${resolved.source})。\n` +
|
|
114
|
-
`可用: ${MANAGED_AGENT_PLATFORMS.map((item) => item.name).join('、')}`);
|
|
115
|
-
}
|
|
116
|
-
if (resolved.definition.status !== 'supported') {
|
|
117
|
-
throw new Error(`${resolved.definition.label} 敬请期待——\`${command}\` 尚未在该平台开放。`);
|
|
118
|
-
}
|
|
119
|
-
if (!resolved.definition.commands.includes(command)) {
|
|
120
|
-
throw new Error(`${resolved.definition.label} 不承接命令 \`${command}\`。`);
|
|
121
|
-
}
|
|
122
|
-
return resolved;
|
|
123
|
-
}
|
|
124
|
-
export function statusLabel(status) {
|
|
125
|
-
return status === 'supported' ? '已支持' : '敬请期待';
|
|
126
|
-
}
|
|
127
|
-
function resolution(name, source, file) {
|
|
128
|
-
return {
|
|
129
|
-
$schema: 'dingtalk-agent/map-resolution@1',
|
|
130
|
-
map: name,
|
|
131
|
-
source,
|
|
132
|
-
file,
|
|
133
|
-
definition: name ? mapDefinition(name) : null,
|
|
134
|
-
platforms: MANAGED_AGENT_PLATFORMS,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
function readJsonOrNull(path) {
|
|
138
|
-
try {
|
|
139
|
-
return JSON.parse(readFileSync(path, 'utf8'));
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
function findUp(start, relativePath) {
|
|
146
|
-
let dir = resolve(start);
|
|
147
|
-
while (true) {
|
|
148
|
-
const candidate = join(dir, relativePath);
|
|
149
|
-
if (existsSync(candidate) && !lstatSync(candidate).isSymbolicLink())
|
|
150
|
-
return candidate;
|
|
151
|
-
const parent = dirname(dir);
|
|
152
|
-
if (parent === dir || dir === parse(dir).root)
|
|
153
|
-
return null;
|
|
154
|
-
dir = parent;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
//# sourceMappingURL=map.js.map
|
package/dist/src/map.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/map.ts"],"names":[],"mappings":"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","sourcesContent":["// Managed Agents Platform(MAP)注册表与工作区归属解析。\n//\n// 一个 Agent 工作区归属于至多一个托管平台;归属解析顺序固定:\n// DTA_MAP 环境变量 > dingtalk-agent.json#map > .dingtalk-agent/map.json > 按 Provider 声明推断。\n// 平台相关的方法学不写进 CLI——它们随对应平台的 Skill 包分发,只在用户选择该平台时安装。\n// 平台命令(如 deploy/promote/observe)必须先通过归属门禁;coming-soon 平台一律 fail-closed。\n\nimport { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, parse, resolve } from 'node:path'\n\nimport { findAgentProjectRoot } from './development-workspace.js'\nimport { installGlobalSkill, type GlobalSkillStatus } from './skill-manager.js'\n\nexport type MapStatus = 'supported' | 'coming-soon'\n\nexport interface MapDefinition {\n name: string\n label: string\n status: MapStatus\n description: string\n skills: string[]\n commands: string[]\n}\n\nexport const MANAGED_AGENT_PLATFORMS: MapDefinition[] = [\n {\n name: 'multica-dingtalk',\n label: 'Multica (DingTalk)',\n status: 'supported',\n description: '钉钉 Multica 托管平台:受控部署、晋级与观测回流。',\n skills: ['dingtalk-agent-deploy', 'dingtalk-agent-boot-multica'],\n commands: ['deploy', 'promote', 'observe'],\n },\n {\n name: 'deap',\n label: 'DEAP',\n status: 'coming-soon',\n description: '敬请期待。',\n skills: [],\n commands: [],\n },\n]\n\nexport const MAP_FILE = '.dingtalk-agent/map.json'\nconst PROJECT_FILE = 'dingtalk-agent.json'\n\nexport type MapSource = 'env' | 'project-manifest' | 'map-file' | 'inferred' | 'none'\n\nexport interface MapResolution {\n $schema: 'dingtalk-agent/map-resolution@1'\n map: string | null\n source: MapSource\n file: string\n definition: MapDefinition | null\n platforms: MapDefinition[]\n}\n\nexport interface MapUseResult extends MapResolution {\n written: string\n skillInstalls: GlobalSkillStatus[]\n}\n\nexport function mapDefinition(name: string): MapDefinition | null {\n return MANAGED_AGENT_PLATFORMS.find((item) => item.name === name) || null\n}\n\nexport function resolveMap(start = process.cwd(), env: NodeJS.ProcessEnv = process.env): MapResolution {\n const fromEnv = String(env.DTA_MAP || '').trim()\n if (fromEnv) return resolution(fromEnv, 'env', '')\n\n const projectRoot = findAgentProjectRoot(start)\n if (projectRoot) {\n const manifestPath = join(projectRoot, PROJECT_FILE)\n if (existsSync(manifestPath) && !lstatSync(manifestPath).isSymbolicLink()) {\n const manifest = readJsonOrNull(manifestPath)\n const declared = typeof manifest?.map === 'string' ? manifest.map.trim() : ''\n if (declared) return resolution(declared, 'project-manifest', manifestPath)\n }\n }\n\n const mapPath = findUp(start, MAP_FILE)\n if (mapPath) {\n const stored = readJsonOrNull(mapPath)\n const declared = typeof stored?.map === 'string' ? stored.map.trim() : ''\n if (declared) return resolution(declared, 'map-file', mapPath)\n }\n\n if (projectRoot) {\n const manifest = readJsonOrNull(join(projectRoot, PROJECT_FILE))\n const providers = Object.values(manifest?.workspaces || {})\n .map((workspace: any) => workspace?.provider?.kind)\n if (providers.includes('multica')) {\n return resolution('multica-dingtalk', 'inferred', join(projectRoot, PROJECT_FILE))\n }\n }\n\n return resolution(null, 'none', '')\n}\n\nexport interface MapUseOptions {\n packageRoot: string\n home?: string\n env?: NodeJS.ProcessEnv\n installSkills?: boolean\n}\n\nexport function useMap(start: string, name: string, options: MapUseOptions): MapUseResult {\n const definition = mapDefinition(name)\n if (!definition) {\n throw new Error(\n `未知 managed agents platform: ${name}\\n` +\n `可用: ${MANAGED_AGENT_PLATFORMS.map((item) => `${item.name}(${statusLabel(item.status)})`).join('、')}`,\n )\n }\n if (definition.status !== 'supported') {\n throw new Error(`${definition.label} 敬请期待——当前版本尚未开放该平台。`)\n }\n\n const projectRoot = findAgentProjectRoot(start)\n let written: string\n if (projectRoot && existsSync(join(projectRoot, PROJECT_FILE))) {\n const manifestPath = join(projectRoot, PROJECT_FILE)\n if (lstatSync(manifestPath).isSymbolicLink()) {\n throw new Error(`${PROJECT_FILE} 不能是符号链接`)\n }\n const manifest = readJsonOrNull(manifestPath)\n if (!manifest || typeof manifest !== 'object') {\n throw new Error(`${PROJECT_FILE} 不是合法 JSON,无法写入 map 归属`)\n }\n manifest.map = definition.name\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n')\n written = manifestPath\n } else {\n const target = join(resolve(start), MAP_FILE)\n mkdirSync(dirname(target), { recursive: true })\n writeFileSync(target, JSON.stringify(\n { $schema: 'dingtalk-agent/map@1', map: definition.name }, null, 2,\n ) + '\\n')\n written = target\n }\n\n const skillInstalls: GlobalSkillStatus[] = []\n if (options.installSkills !== false) {\n for (const skill of definition.skills) {\n skillInstalls.push(installGlobalSkill(options.packageRoot, {\n name: skill, home: options.home, env: options.env,\n }))\n }\n }\n\n return { ...resolveMap(start, options.env || process.env), written, skillInstalls }\n}\n\n/** deploy/promote/observe 等平台命令的归属门禁:未归属、未知或 coming-soon 一律 fail-closed。 */\nexport function requireSupportedMap(\n command: string, start = process.cwd(), env: NodeJS.ProcessEnv = process.env,\n): MapResolution {\n const resolved = resolveMap(start, env)\n if (!resolved.map) {\n throw new Error(\n `当前工作区未声明 managed agents platform,无法执行 \\`${command}\\`。\\n` +\n '先运行 `dta map use multica-dingtalk`,或设置 DTA_MAP 环境变量。',\n )\n }\n if (!resolved.definition) {\n throw new Error(\n `工作区声明了未知 platform: ${resolved.map}(来源 ${resolved.source})。\\n` +\n `可用: ${MANAGED_AGENT_PLATFORMS.map((item) => item.name).join('、')}`,\n )\n }\n if (resolved.definition.status !== 'supported') {\n throw new Error(`${resolved.definition.label} 敬请期待——\\`${command}\\` 尚未在该平台开放。`)\n }\n if (!resolved.definition.commands.includes(command)) {\n throw new Error(`${resolved.definition.label} 不承接命令 \\`${command}\\`。`)\n }\n return resolved\n}\n\nexport function statusLabel(status: MapStatus): string {\n return status === 'supported' ? '已支持' : '敬请期待'\n}\n\nfunction resolution(name: string | null, source: MapSource, file: string): MapResolution {\n return {\n $schema: 'dingtalk-agent/map-resolution@1',\n map: name,\n source,\n file,\n definition: name ? mapDefinition(name) : null,\n platforms: MANAGED_AGENT_PLATFORMS,\n }\n}\n\nfunction readJsonOrNull(path: string): any {\n try { return JSON.parse(readFileSync(path, 'utf8')) } catch { return null }\n}\n\nfunction findUp(start: string, relativePath: string): string | null {\n let dir = resolve(start)\n while (true) {\n const candidate = join(dir, relativePath)\n if (existsSync(candidate) && !lstatSync(candidate).isSymbolicLink()) return candidate\n const parent = dirname(dir)\n if (parent === dir || dir === parse(dir).root) return null\n dir = parent\n }\n}\n"]}
|