@dsh-workbench/core 0.3.10 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +19 -7
  2. package/bin/dsh-workbench +2 -2
  3. package/client.js +1648 -973
  4. package/cordis.patch.yml +60 -35
  5. package/dist/client/client.body.js +1648 -973
  6. package/dist/client/index.js +1333 -658
  7. package/dist/host/index.js +2896 -2334
  8. package/dist/types/app/client.d.ts +2 -1
  9. package/dist/types/app/layouts/env-gate/boot-gate.d.ts +1 -1
  10. package/dist/types/app/layouts/market/use-module-versions.d.ts +13 -2
  11. package/dist/types/app/layouts/rightbar/MarketView.d.ts +6 -3
  12. package/dist/types/app/layouts/rightbar/market-act.d.ts +6 -3
  13. package/dist/types/app/layouts/rightbar/market-settle-hold.d.ts +2 -0
  14. package/dist/types/app/layouts/rightbar/market-settle.d.ts +20 -0
  15. package/dist/types/app/layouts/rightbar/market-update-verdict.d.ts +41 -0
  16. package/dist/types/app/layouts/rightbar/market-view-text.d.ts +4 -1
  17. package/dist/types/app/layouts/rightbar/store.d.ts +14 -0
  18. package/dist/types/app/layouts/sidebar/LongTaskDot.d.ts +12 -0
  19. package/dist/types/app/layouts/sidebar/LongTaskHover.d.ts +12 -0
  20. package/dist/types/app/layouts/sidebar/edition-boot.d.ts +24 -0
  21. package/dist/types/app/layouts/sidebar/edition.d.ts +22 -0
  22. package/dist/types/app/layouts/workbench-settings/ConfigSyncItem.d.ts +5 -0
  23. package/dist/types/app/layouts/workbench-settings/SettingsItem.d.ts +17 -0
  24. package/dist/types/app/layouts/workbench-settings/SidebarEditionItem.d.ts +11 -0
  25. package/dist/types/app/layouts/workbench-settings/WorkbenchSettingsSection.d.ts +11 -0
  26. package/dist/types/modules/panels/model/approval.d.ts +12 -4
  27. package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -1
  28. package/dist/types/modules/panels/model/diy-context.d.ts +31 -14
  29. package/dist/types/modules/panels/model/diy-page.d.ts +3 -2
  30. package/dist/types/modules/panels/model/diy-record.d.ts +3 -3
  31. package/dist/types/modules/panels/model/diy-report.d.ts +6 -7
  32. package/dist/types/modules/panels/model/market-dependency-update.d.ts +21 -0
  33. package/dist/types/modules/panels/model/market-dispatch.d.ts +1 -0
  34. package/dist/types/modules/panels/model/market-types.d.ts +1 -0
  35. package/dist/types/modules/panels/model/market-update-bundle.d.ts +25 -0
  36. package/dist/types/modules/panels/model/market-verify.d.ts +31 -0
  37. package/dist/types/modules/panels/unit.d.ts +3 -4
  38. package/dist/types/shared/api/bundle-install.d.ts +13 -0
  39. package/dist/types/shared/api/host/approval-gate.d.ts +39 -0
  40. package/dist/types/shared/api/host/bundle-routes.d.ts +2 -1
  41. package/dist/types/shared/api/host/diy-artifacts.d.ts +14 -0
  42. package/dist/types/shared/api/host/diy-modules.d.ts +41 -0
  43. package/dist/types/shared/api/host/host-routes.d.ts +13 -0
  44. package/dist/types/shared/api/host/permission-mode.d.ts +9 -0
  45. package/dist/types/shared/api/host/runtime-seed.d.ts +2 -0
  46. package/dist/types/shared/api/host/self-update.d.ts +9 -2
  47. package/dist/types/shared/api/host/sidebar-edition.d.ts +46 -0
  48. package/dist/types/shared/api/host/workspace-root.d.ts +13 -0
  49. package/dist/types/shared/api/sidebar-edition.d.ts +14 -0
  50. package/dist/types/shared/api/unit-board.d.ts +20 -0
  51. package/dist/types/shared/api/write-outcome.d.ts +15 -0
  52. package/dist/types/shared/components/IconModules.d.ts +8 -0
  53. package/dist/types/shared/components/blocker-action.d.ts +16 -0
  54. package/dist/types/shared/hooks/sidebar-edition.d.ts +7 -0
  55. package/dist/types/shared/hooks/use-capabilities.d.ts +5 -0
  56. package/dist/types/shared/hooks/use-capability-gate.d.ts +1 -2
  57. package/dist/types/shared/hooks/use-host-write.d.ts +1 -1
  58. package/dist/types/shared/hooks/use-unit-board.d.ts +1 -1
  59. package/docs/HANDOFF.md +3 -3
  60. package/docs/OPERATIONS.md +16 -5
  61. package/package.json +3 -3
  62. package/preset/diy.patch.yml +2 -2
  63. package/profile/isolation.patch.yml +11 -0
  64. package/skills/workbench-module-diy/BUILD.md +62 -131
  65. package/skills/workbench-module-diy/COPY.md +89 -0
  66. package/skills/workbench-module-diy/DATASOURCE.md +84 -112
  67. package/skills/workbench-module-diy/DESIGN.md +75 -109
  68. package/skills/workbench-module-diy/FAMILY.md +70 -54
  69. package/skills/workbench-module-diy/KIT.md +77 -133
  70. package/skills/workbench-module-diy/LINKAGE.md +66 -105
  71. package/skills/workbench-module-diy/PANEL.md +79 -171
  72. package/skills/workbench-module-diy/REVIEW.md +67 -81
  73. package/skills/workbench-module-diy/SKILL.md +57 -96
  74. package/skills/workbench-module-diy/STEPS.md +66 -69
  75. package/skills/workbench-module-diy/TOOLING.md +34 -51
  76. package/skills/workbench-module-diy/TOOLS.md +110 -103
  77. package/skills/workbench-module-diy/WIRING.md +51 -83
  78. package/skills/workbench-module-diy/WRITES.md +89 -142
  79. package/dist/types/app/layouts/config-sync/ConfigSyncSection.d.ts +0 -5
  80. package/dist/types/shared/api/host/diy-workshop.d.ts +0 -23
  81. package/dist/types/shared/api/host/workshop-root.d.ts +0 -16
  82. /package/dist/types/shared/{api/host/diy-workshop.test.d.ts → components/blocker-action.test.d.ts} +0 -0
@@ -1,144 +1,110 @@
1
- # 设计口径:先说清他要什么,再谈怎么做
1
+ # 设计:定需求、出三张表、落 design.json
2
2
 
3
- 这一步与落点无关 —— 无论模块最后落进工作台外壳里,还是落进一个独立模块包,
4
- **问法、产出是同一套;验收归谁按域分**(见 §4)。工程域与 DIY 域共用这一份。
3
+ 工程域与 DIY 域共用。验收归谁见 §4。
5
4
 
6
- ## 0 先判域
5
+ ## 0 判域
7
6
 
8
- 做不成的不是"难",是"**不该在这里做**"。判完要给他**一句人话 + 下一步**,不是丢给他一个分类。
7
+ 判据:**要不要新凭据**,不是要不要接新平台。
9
8
 
10
- | 需求长这样 | 判 | 下一步 |
9
+ | 需求 | 域 | 动作 |
11
10
  |---|---|---|
12
- | 一张只读卡;数据从 HTTP GET / 一条命令 / 本地文件来 | **DIY 域** | 直接造([BUILD.md](BUILD.md)) |
13
- | 卡片要能自己配(钉一个环境、选一个仓库) | **DIY 域** | 配置清单([PANEL.md](PANEL.md) §5) |
14
- | 要 agent 替他做一两件**只读**的事 | **DIY 域** | 能力清单([TOOLS.md](TOOLS.md)) |
15
- | 要 agent 动真东西(提交、发布、改配置) | **DIY 域**,但必须写明点头那一步 | 预览 + 确认两拍([TOOLS.md](TOOLS.md) §5–§7) |
16
- | 他已经在浏览器里登录过那套系统,只是**没人告诉他数据在哪** | **DIY 域** | 用 opencli 复用他的登录去探([DATASOURCE.md](DATASOURCE.md)) |
17
- | 要**新凭据**(新账号 / 密钥 / token) | **工程域** | 停,说清要谁去配什么 —— 别在 DIY 里做 |
18
- | 一条流:多步、要等很久、中途要人、失败要能接着来 | **工程域** | 同上 |
19
- | 要新建服务 / 新进程 / 新依赖 | **工程域** | 同上 |
20
- | 要改工作台外壳,或改一份**已经发布**的模块 | **工程域** | 同上 |
21
-
22
- 判域产出的是**他能听懂的一句话**("这件事得先有人给你配一个 XX 的账号,我按工程流程走"),
23
- 以及"接下来会发生什么、还要多久"。不是"你这个需求超纲了"。
24
-
25
- ## 1 两条分工(贯穿全程)
26
-
27
- | 谁 | 管什么 |
28
- |---|---|
29
- | **用户** | 他看得见、能决定的事:要看到什么、要能自己配什么、要 agent 替他做什么、哪些要他点头、话怎么说、取舍 |
30
- | **agent** | 一切实现与验收(验收按域分,见 §4):怎么落、叫什么、放在哪、用什么、怎么在真卡片上逐条验 —— **这些不问用户** |
31
-
32
- 四条对话纪律:
11
+ | 只读卡;数据来自 HTTP GET / 命令 / 本地文件 | DIY | 造([BUILD.md](BUILD.md)) |
12
+ | 卡片要能配置(环境、仓库) | DIY | 配置清单([PANEL.md](PANEL.md) §5) |
13
+ | agent 做一两件只读的事 | DIY | 能力清单([TOOLS.md](TOOLS.md)) |
14
+ | agent 写平台(提交、发布、改配置) | DIY | 预览 + 确认两拍([TOOLS.md](TOOLS.md) §5–§7) |
15
+ | 用户已在浏览器登录过该系统,只是不知道数据在哪 | DIY | 复用登录探([DATASOURCE.md](DATASOURCE.md)) |
16
+ | 要**新凭据**(账号 / 密钥 / token) | 工程 | 停;说清谁配什么 |
17
+ | 多步长流程(要等、中途要人、失败要续) | 工程 | 同上 |
18
+ | 新服务 / 新进程 / 新依赖 | 工程 | 同上 |
19
+ | 改工作台外壳,或改已发布模块 | 工程 | 同上 |
33
20
 
34
- - 每个问题都**带推荐答案**,他一个字就能过("就这样");他不确定就按推荐走,标出来,别卡住。
35
- - 任何**必须他参与**的决定,都翻译成"**他会看到什么**"再问(不问"用 A 还是 B"这类内部选择)。
36
- - 每走完一步,给他看一个**他看得懂的东西**:一张表、一张画面、或一个能点的界面。**不贴代码、不讲实现**。
37
- - 开工先列一份待办(问 → 三张表 → 做 → 验收),边做边更新;这是一件要跑很多轮的事。
21
+ 输出:一句结论 + 下一步 + 预计发生什么。
38
22
 
39
- ## 2 三个问题,三张表
23
+ ## 1 分工
40
24
 
41
- | 步 | 问他什么 | 产出 | 表的行 × 列 |
42
- |---|---|---|---|
43
- | 1 面板 | 他要看到什么?要能自己配什么? | 展示清单 + 配置清单 + 状态矩阵 + 图例 | 每种状态(含每种缺失)×「他看到什么 / 他能做什么 / agent 能接着做什么」 |
44
- | 2 agent | 他要 agent 替他做什么?哪几件他必须先点头? | 能力清单 + 全场景 + 点头场景 | 每件事 × 每种情况:agent 得到什么 / 用户看到什么 |
45
- | 3 联动 | 他凭什么相信面板是新的? | 联动场景 | 每种「谁在动」×「面板什么时候变 / 变成什么样 / 用户看到什么」 |
25
+ | 谁 | 决定 |
26
+ |---|---|
27
+ | 用户 | 看到什么、能配什么、agent 替做什么、哪些要点头、措辞、取舍 |
28
+ | agent | 实现与验收:怎么落、命名、位置、选型、怎么验。**不问用户** |
46
29
 
47
- **三张表既是要问清的需求,也是最后的验收单** —— 表里每一格,都要能在真卡片上见过或点过
48
- (DIY 域由 agent 自己走,工程域请他看一次,见 §4)。
49
- 表里不许有空格,也不许有「其他」那一行。
30
+ 对话规则:
50
31
 
51
- 它的落点是模块里的 `design.json`(命令与形状见本文 §5)—— **只留在对话里等于没定**。
32
+ - 每个问题给推荐答案,用户可一字通过;不确定就按推荐走并标注。
33
+ - 必答的决定翻译成"你会看到什么"再问,不问 A/B 选型。
34
+ - 每步给用户一个可看的东西:表、ASCII、可点界面。不贴代码、不讲实现。
35
+ - 开工列待办:问 → 三张表 → 做 → 验收。
52
36
 
53
- 三步各自要问什么、要定什么、完成判据是什么:[STEPS.md](STEPS.md);
54
- 每一步的细节:[PANEL.md](PANEL.md) · [TOOLS.md](TOOLS.md) · [LINKAGE.md](LINKAGE.md)。
37
+ ## 2 三张表
55
38
 
56
- ## 3 跟他沟通,尽量用 ASCII 画出来
39
+ | 步 | 问 | 产出 | 行 × 列 |
40
+ |---|---|---|---|
41
+ | 1 面板 | 看到什么?能配什么? | 展示清单 + 配置清单 + 状态矩阵 + 图例 | 每种状态(含每种缺失)×「看到什么 / 能做什么 / agent 能接着做什么」 |
42
+ | 2 agent | agent 替做什么?哪些要点头? | 能力清单 + 全场景 + 点头场景 | 每件事 × 每种情况:agent 得到什么 / 用户看到什么 |
43
+ | 3 联动 | 凭什么相信面板是新的? | 联动场景 | 每种"谁在动" ×「何时变 / 哪一格变 / 看到什么」 |
57
44
 
58
- **不要用句子描述界面,画给他看。** 一句话可以指向十种界面;一张 ASCII 就是那一种 ——
59
- 他一眼就能说"这个对"或"这不对"。
45
+ 三张表同时是验收单:每格都要在真卡片上见过或点过。不留空格,不留"其他"行。
46
+ 落 `design.json`(§5)。问法见 [STEPS.md](STEPS.md);细节见 [PANEL.md](PANEL.md) · [TOOLS.md](TOOLS.md) · [LINKAGE.md](LINKAGE.md)。
60
47
 
61
- - **界面**:卡片、每一种状态、设置面、审批弹窗,全部画 ASCII,图里用界面上真实的字。
62
- - **工具**:贴真实的调用(参数 JSON)与真实的返回正文(照抄,不转述)。
63
- - **联动**:把"变之前 / 变之后"也画出来(两张并排),别写"会自动刷新"。
64
- - 要比较几种做法,就把几张图并排,别用形容词比。
65
- - 矩阵左列用**图号**引用:面板那些图在步骤 1,联动那两张(变之前 / 变之后)在步骤 3。
66
- - **不需要截图**:沟通靠 ASCII;实现完了他自己随时能打开看真的。
48
+ ## 3 用 ASCII 沟通
67
49
 
68
- **每张表都要配图例,而图例就是把屏幕画出来。** 每种状态给一张小画面
69
- (ASCII 小样:图标、那句话、按钮各在哪;空态有没有按钮;阻断是整张卡换掉还是只在上面加一条;
70
- 骨架是几行灰条;弹窗里哪一行是谁给的),表里用**图号**引用(A / B / C…)。
71
- **给状态起个名字、配个图标,都不算图例** —— 用户看不懂 `⛔` 是什么,
72
- 但一眼看得懂"整张卡被换成了这一屏"。
50
+ 画,不写句子。一张 ASCII 对应一种界面。
73
51
 
74
- 定稿前扫一遍全文,凡是**描述"看到 / 听到 / 发生"的句子**,都问一句:**这句能不能画成 ASCII?** 能就画。
75
- 画不成的只有这几类,写词(并说清意思):纯动作("点一下去平台")、agent 的下一步、
76
- 时间性的词("不变 / 自己变 / 立刻")、纯映射表(能力名 ↔ 用户会怎么说)。
52
+ - 界面:卡片、每种状态、设置面、审批弹窗。图里用界面真实文案。
53
+ - 工具:贴真实调用(参数 JSON)与真实返回正文,照抄。
54
+ - 联动:画"变之前 / 变之后"两张并排。
55
+ - 矩阵左列写图号(A / B / C…);比较方案就并排图。
56
+ - 不截图;实现后用户自己看。
77
57
 
78
- ### 审批屏:写工具族必答,而且那两格是固定的
58
+ **图例 = 屏幕**:每种状态一张小 ASCII(图标、那句话、按钮位置;空态有无按钮;阻断是整卡替换还是加一条;
59
+ 骨架几行;弹窗每行谁给的)。状态名和图标不算图例。
60
+ 凡是"看到 / 听到 / 发生"的句子,能画就画。画不成的只有:纯动作、agent 下一步、时间词(不变 / 自己变 / 立刻)、
61
+ 纯映射表 —— 写成词并给定义。
79
62
 
80
- 有"要用户点头"的写工具,就把**审批屏**也画进图里。它的形状不是自由的(仓内 ADR-0065,这份指引不链仓内路径):
63
+ ### 审批屏(有写工具就画)
81
64
 
82
65
  ```
83
66
  ┌─ card(dsh 的壳)─────────────────────────────────────────┐
84
- │ ● 等待审批 │ ← dsh 画的
67
+ │ ● 等待审批 │ ← dsh 画
85
68
  ├───────────────────────────────────────────────────────────┤
86
- │ <标题:一句,≤40 字,…,允许吗?> │ ← 你写的 question / fallback
87
- │ <详情:ApprovalPlan 的两列表 + warn> │ ← 你写的 approval.render
69
+ │ <标题:一句,≤40 字,…,允许吗?> │ ← question / fallback
70
+ │ <详情:ApprovalPlan 两列表 + warn> │ ← approval.render
88
71
  ├───────────────────────────────────────────────────────────┤
89
72
  │ [ 拒绝 ] [ 允许一次 ] │
90
73
  └───────────────────────────────────────────────────────────┘
91
74
  ```
92
75
 
93
- 四条口径(同一份:[KIT.md](KIT.md) §3):
94
-
95
- 1. **标题只有一句**:`…,允许吗?` 收尾并点名"工作台"(工作台自己的动作可用 `申请…` 陈述句式);
96
- 装配后 ≤108 字(含数据)。
97
- 2. **事实放详情**,用外壳的 `ApprovalPlan`(`approval: { tools, render }`):等宽、能换行、能滚,
98
- 长 URL / diff / 多字段都住这儿。
99
- 3. **别把事实塞进标题**:宿主那句 `approvalDetail`(明细)只留给**没有详情表**的族,≤20 字一行 ——
100
- 你既然画了详情表,它就是空的。
101
- 4. **附注(`approval_note`)不是你的**:只有 opencli 插件命令那族才必填;模块定义的工具默认没有那一格。
76
+ 1. 标题一句,`…,允许吗?` 收尾并点名"工作台"(工作台自身动作用 `申请…`);装配后 ≤108 字。
77
+ 2. 事实进详情:外壳的 `ApprovalPlan`(`approval: { tools, render }`)。
78
+ 3. 事实不进标题:`approvalDetail` 只给没有详情表的族,≤20 字一行;有详情表时它是空的。
79
+ 4. `approval_note` 只有 opencli 插件命令族必填;模块工具默认没有。
80
+ 5. 文案的读者与两层(主句 / 原文)见 [COPY.md](COPY.md) §2–§3。
102
81
 
103
- ## 4 做完的判据
82
+ ## 4 完成判据
104
83
 
105
- - 全程**用户只说过需求相关的话**:没有一次被问"用什么、放在哪、叫什么、怎么测"。
106
- - 三张表没有空格(`dsh-wb-module design check` 绿);表里每一格,都在真卡片上见过或点过,
107
- 验收清单逐条打上了勾(`design accept`)。
108
- - **验收归谁按域分**:**DIY 域(工坊会话)的验收是 agent 自己走的** —— 调试族
109
- (`workbench_diy_debug_start` + 浏览器那 14 把枚举工具 + `logs_*`,全表见 [FAMILY.md](FAMILY.md))
110
- 就是取证面;用户不必在场、也不必逐条点头,走完把"装好了、面板上哪一格"告诉他即可。
111
- **工程域没有那一族**,仍按「打开 :3081 → 右栏第 N 张卡 → 点 X → 应该看到 Y」请他看一次。
112
- - 要用户点头的**写能力(审批)那一拍仍是他的**:他真用一次时点,agent 代不了 ——
113
- 清单里写清"这条要你真用一次",别替他打勾。
114
- - 门禁是绿的 —— 那是 agent 自己的门槛,不用他关心。
115
- - 下次他再说一句"改一下",不用从头问:设计留下的记录还在。
84
+ - 用户只说过需求;没被问实现。
85
+ - 三张表无空格(`design check` 绿);每格在真卡片上验过,`design accept` 打勾。
86
+ - 验收:DIY 域 agent 自验(调试族取证,[FAMILY.md](FAMILY.md));工程域请用户看一次。
87
+ - 需用户点头的写能力留给用户真用一次,清单标「要你真用一次」。
88
+ - 门禁绿;`design.json` 保留,后续改动不必从头问。
116
89
 
117
- ## 5 落点:`design.json`(设计底稿:人话一处,机器字段在清单)
90
+ ## 5 design.json
118
91
 
119
- 三张表 + 状态矩阵 + 数据源记录 + 验收清单住在模块根目录的 `design.json`,README 里那一段**由它生成**
120
- (夹在 `<!-- dsh-wb:design:start/end -->` 之间,别手改 —— 下一次 `check` 会判红):
92
+ 三张表 + 状态矩阵 + 数据源 + 验收存模块根的 `design.json`;README 对应段由它生成
93
+ (`<!-- dsh-wb:design:start/end -->`,手改判红)。
121
94
 
122
95
  ```bash
123
- pnpm exec dsh-wb-module design check # 空着的格?README 落后了?都在这儿报(红 = 还有没定的事)
124
- pnpm exec dsh-wb-module design render # 改完 design.json,把 README 那一段重新生成
125
- pnpm exec dsh-wb-module design accept # agent 自己在真卡片上逐条走通之后,把验收清单打勾
96
+ pnpm exec dsh-wb-module design check # 空格 / README 落后
97
+ pnpm exec dsh-wb-module design render # 重生成 README 段
98
+ pnpm exec dsh-wb-module design accept # 逐条走通后打勾
126
99
  ```
127
100
 
128
- 形状:`unit` / `name` / `oneLine` + `panel[]`(这一格 / 结论 / 去哪)+ `states[]`(**五态一个都不能少**,
129
- key 固定 `idle|loading|ready|empty|unreadable`)+ `config[]` + `capabilities[]` + `linkage[]` +
130
- `datasource{}`(只有一条读)或 `reads[]`(多条读,一条一行;字段一样)+ `writes[]`(**写通道**:一个动作可以打几条写,一条一行;只有一条写也可写 `write{}`;**没有写通道的模块不写就行**)+ `acceptance[]`。
131
- `config` / `capabilities` **允许空数组**("这份模块故意没有能力"是一种决定);其余空着就是没定的事。
132
- (写通道的字段与取证口径见 [WRITES.md](WRITES.md);README 里那一节叫「写通道探明记录」,位置在「数据源探明记录」与「验收」之间。)
133
- `link` 这类**可选格**留空渲染成 `—`,不算空格。
134
-
135
- `design.json` 是**设计底稿**:只装人话。"这条能力在哪些工作区出现"**哪里都别写** ——
136
- 那是**用户**的事:用户口径是「模块开发者不能定义 scope,scope 永远是用户来决定的」,
137
- 他在 `workbench_panels_show` / `workbench_panels_hide` 里决定(见 [TOOLS.md](TOOLS.md) §12)。
138
- `contracts` 只拿名字把底稿与清单对起来(键集合对账)。
139
-
140
- 五态那五行是矩阵的行,**不能省**;但某一态对这份模块确实不适用时,写一句为什么就行 ——
141
- `{ "key": "idle", "na": "读的是账号,不随项目变:没选项目时这张卡压根不渲染" }`,
142
- 那一行不填 `sees` / `canDo` / `agentCan`,表里渲染成「(这份模块不适用)…」。
143
- `capabilities` 是空数组时,`states[].agentCan` 也不用逐格填(渲染成 `—`):这份模块故意没有
144
- agent 能力,那几格本来就没有内容可写,硬填只会逼出一句同义反复。
101
+ 字段:`unit` / `name` / `oneLine` + `panel[]`(这一格 / 结论 / 去哪)+ `states[]`(五态必填,key 固定
102
+ `idle|loading|ready|empty|unreadable`)+ `config[]` + `capabilities[]` + `linkage[]` +
103
+ `datasource{}`(一条读)或 `reads[]`(多条,一条一行)+ `writes[]`(写通道;单条可写 `write{}`;没有就不写)+
104
+ `acceptance[]`。`config` / `capabilities` 可为空数组;其余空 = 未定。写通道字段见 [WRITES.md](WRITES.md)。
105
+
106
+ 单态不适用时写 `na`:`{ "key": "idle", "na": "读的是账号,不随项目变" }`,不填 `sees` / `canDo` / `agentCan`。
107
+ `capabilities` 为空时 `states[].agentCan` 可省。`link` 等可选格留空渲染 `—`。
108
+
109
+ `design.json` 只装人话。模块**不声明**"在哪些工作区出现"—— 用户用 `workbench_panels_show` / `_hide` 决定
110
+ ([TOOLS.md](TOOLS.md) §12)。`contracts` 只对键名。
@@ -1,56 +1,72 @@
1
- # 工坊会话里那一族:33 把(agent 手上真实有什么)
2
-
3
- [TOOLS.md](TOOLS.md) 讲**你写的模块**的能力怎么写;这一份讲**工坊会话里 agent 手上真实有什么**。
4
- 浏览器那一半照 opencli 的子命令枚举,参数只有 `args`(原样转发);为什么这么定见仓内 ADR-0062(这份指引不链仓内路径)。
5
-
6
- ## 1 前置(别的都排它后面)
7
-
8
- - 只在**工坊会话**里出现,且**那块板画在屏上**;看 / 动 / 网络 / 日志那一族还要**先立调试约定** `workbench_diy_debug_start`(请用户点头一次):**没有约定一律当场拒、零命令**;不看屏 `_debug_pause`、收工 `_debug_stop`(顺手收网络与日志钩子)。
9
- - 会话钉死 `workbench`、每条带 `--window background`;`_list`/`_status`/`_panel`/`_components`/`_recon`/`_spec`/`_install`/`_show` 不碰他那一屏,不用开;`_find`/`_eval` 这些带 `read=` 时也**不碰**(走只读线,见 §2 末)。**写那一族(录制交接,见 §5)不走这条路**:它自己开一条 `workbench-rec` 会话,`_record_prepare` 要点头一次,`_record_read` / `_record_detail` / `_record_stop` 不问。
10
-
11
- ## 2 14 把浏览器工具(工具名 → argv;前缀都是 `workbench_diy_`)
12
-
13
- `_state`→`state` · `_html`→`get html` · `_find`→`find` · `_click`→`click` · `_hover`→`hover` · `_type`→`type` · `_fill`→`fill` · `_keys`→`keys` · `_scroll`→`scroll` · `_wait`→`wait` · `_eval`→`eval` · `_screenshot`→`screenshot` · `_network`→`network` · `_dialog`→`dialog`
14
-
15
- 干什么:名字直说;不直观的几个 —— `_state` URL + 标题 + 可点元素(ref 来源)、`_html` HTML(`--as json` 树)、`_find` 回 `{matches_n, entries[]}`、`_network` 收发包(`--follow`;body 拿不到)、`_screenshot` 写文件(`clip` 只裁一块)。
16
-
17
- **参数**(`scope` / `in` 默认都是 `page`):
18
-
19
- - 公共:`unit?`(哪张卡;可省 —— 只剩一个模块就用它。**要卡作用域就得给**:写 `scope: 'card'` / `in: 'card'` 或用 `$card` 却不给 ⇒ **当场拒、零命令**)· `args?: string[]`(原样接在 `opencli browser workbench <verb…>` 后)· `timeoutMs?`(默认 30000)· `read?: string`(只读线档,见本节末)。
20
- - `scope: 'card'` 只给吃 CSS 的**七把**:`_find`(`--css`)· `_html`(`--selector`,没给它我们补)· `_click`/`_hover`/`_type`/`_fill`(**位置参数 0**)· `_wait`(**只在 `wait selector`**,位置参数 1)—— `card` = 给那个目标挂 `#wb-card-<unit> .wb-card `。
21
- - **只读线档**:任何一把带上 `read: <站点地址>` 就跑在**另一条会话** `workbench-read` 上 —— **不要调试约定、不要板在屏上、不认卡作用域**(`unit` / `scope: 'card'` / `in: 'card'` / `$card` 逐个当场拒、零命令),也**不要求会话工作区里有模块包**(它一行都没碰工作区);结果与屏那一档**同一组字段**(`ok`/`exitCode`/`timedOut`/`truncated`/`stdout`/`stderr` 原样),另带 `session` 与 `opened`。`opened` = 我替你开过一次站点:跑之前先 `get url` 比域名,不在那个站点上就 `open <read>` 一次再跑。`_eval` 上沿用读线约定:脚本回 `{ ok: false, code: "not-logged-in" }` 时输出后补一句「去 <read> 登一次」(只补说明,结果字段一个不改)。**这一档取代了老的 `_probe`**(2026-10-20;来龙去脉在本仓 `docs/adr/0062` §6 与 `.scratch/diy-read-line/spec.md` —— 那两份不随包出货,只给改外壳的人):老 `_probe` 那层结构化 `entries` 没有替代 —— `_find` 的 stdout 自己解析。
22
- - `in: 'card'` 只 `_eval`(注入卡根 `root`,**脚本自己 `return`**);`clip`(CSS px)只 `_screenshot`(拍完本地裁);`$card` **任何 `args` 里**都替换成 `#wb-card-<unit> .wb-card`。
23
-
24
- **拿回**:`{ ok, exitCode, timedOut, truncated, stdout, stderr }` + `workshop`/`unit`/`verb`/`argv`(`read=` 那一档改成 `session`/`opened`:只读线不碰工作区,所以没有 `workshop`/`unit`);`_screenshot` 另给 `file`/`cropped`/`cropReason`。`ok` 只管进程,事实全在 `stdout`(报错也是一份 `{"error":{…}}`,`ok` 可能仍 `true`);不抠 JSON / 不翻中文 / 不做 ok 归一,**要参数先问 `--help`**。**"原样"有折损**:`args` 里空串 / 纯空白 / 非字符串丢掉、留下的词前后 trim、`stdout`/`stderr` 也 trim ⇒ **`_fill` 清空不了输入框**、**`_type` 敲不了空格**,没有别的口子就如实说做不到。
25
-
26
- ## 3 老工具 → 新命令
27
-
28
- | 老 | 新 |
1
+ # DIY 工具族:参数与返回
2
+
3
+ 本文件是 agent 在 DIY 会话里实际拿到的工具契约。[TOOLS.md](TOOLS.md) 讲模块自身能力。
4
+
5
+ ## 1 前置
6
+
7
+ - 仅在选了 DIY 模式的会话中出现(按模式,不按工作区),且面板已画在屏上。
8
+ - 看 / 动 / 网络 / 日志族需先 `workbench_diy_debug_start`(审批一次);无约定一律拒,零命令。
9
+ 不看屏 `_debug_pause`;收工 `_debug_stop`(一并收网络与日志钩子)。
10
+ - 固定会话 `workbench`,每条带 `--window background`。`_status` / `_panel` / `_components` / `_recon` / `_spec` /
11
+ `_install` / `_show` 不碰屏,无需约定;`_find` / `_eval` 带 `read=` 走只读线,也不碰。
12
+ - 录制交接(§4)独立:会话 `workbench-rec`;`_record_prepare` 审批一次,其余不问。
13
+
14
+ ## 2 浏览器 14 把
15
+
16
+ 工具名 → opencli 子命令(前缀 `workbench_diy_`):
17
+
18
+ `_state`→`state` · `_html`→`get html` · `_find`→`find` · `_click`→`click` · `_hover`→`hover` · `_type`→`type` ·
19
+ `_fill`→`fill` · `_keys`→`keys` · `_scroll`→`scroll` · `_wait`→`wait` · `_eval`→`eval` · `_screenshot`→`screenshot` ·
20
+ `_network`→`network` · `_dialog`→`dialog`
21
+
22
+ 返回要点:`_state` = URL + 标题 + 可点元素(ref 来源);`_html` = HTML(`--as json` 树);`_find` =
23
+ `{matches_n, entries[]}`;`_network` = 收发包(`--follow`;body 不可取);`_screenshot` 写文件(`clip` 裁剪)。
24
+
25
+ 参数(`scope` / `in` 默认 `page`):
26
+
27
+ - 公共:`unit?`(卡片;可省。使用 `scope: 'card'` / `in: 'card'` / `$card` 时必给,否则拒)·
28
+ `args?: string[]`(原样接在 `opencli browser workbench <verb…>` 后)· `timeoutMs?`(默认 30000)·
29
+ `read?: string`(只读线)。
30
+ - `scope: 'card'` 仅七把:`_find`(`--css`)、`_html`(`--selector`)、`_click` / `_hover` / `_type` / `_fill`
31
+ (位置参数 0)、`_wait`(仅 `wait selector`,位置参数 1)。`card` = 目标挂 `#wb-card-<unit> .wb-card `。
32
+ - 只读线:带 `read: <地址>` 时跑在 `workbench-read` 会话,不要约定、不需板在屏、不认卡作用域
33
+ (`unit` / `scope: 'card'` / `in: 'card'` / `$card` 均拒),也不要求工作区有模块。
34
+ 返回字段与屏线相同,另带 `session` / `opened`(`opened` = 曾代开站点:先比 `get url`,不在则 `open <read>` 再跑)。
35
+ `_eval` 脚本返回 `{ ok: false, code: "not-logged-in" }` 时追加一句"去 <read> 登录"。
36
+ - `in: 'card'` 仅 `_eval`(注入卡根 `root`,脚本自行 `return`);`clip`(CSS px)仅 `_screenshot`;
37
+ `$card` 在任意 `args` 中替换为 `#wb-card-<unit> .wb-card`。
38
+
39
+ 返回:`{ ok, exitCode, timedOut, truncated, stdout, stderr }` + `workshop` / `unit` / `verb` / `argv`
40
+ (只读线为 `session` / `opened`);`_screenshot` 另带 `file` / `cropped` / `cropReason`。
41
+ `ok` 仅表示进程退出;事实在 `stdout`(错误体为 `{"error":{…}}`,`ok` 可能为 true)。不做 JSON 解析、
42
+ 不翻译、不归一。参数不明先 `args: ['<verb>', '--help']`。
43
+ "原样"有折损:空串 / 纯空白 / 非字符串参数被丢弃,词前后 trim,stdout / stderr trim ⇒ `_fill` 无法清空、
44
+ `_type` 无法输入空格。
45
+
46
+ 补充用法:`_select` 不存在,下拉两拍(点触发器 → `scope: 'page'` + `--role menuitem` 按名点);
47
+ 刷新用 `eval 'location.reload()'`;视口用 `screenshot --width/--height`。
48
+
49
+ ## 3 边界
50
+
51
+ - `screenshot` 无 selector:卡图先 `eval` 量盒再 `clip`;不给路径写
52
+ `~/.dsh/workbench/artifacts/<工作区>/<unit|page>-<时间戳>.png`,否则回 base64;裁剪块不在视口时退回整屏,
53
+ 原因在 `cropReason`。
54
+ - `state` 无 `scope`(卡内用 `find --css '$card …'`);无右键;ref 仅一次快照内有效;`_dialog` 不校验
55
+ `accept|dismiss`;`_network --detail` 无 body → 用 `_eval` 的 `fetch`。
56
+ - 录制会话捕获带 body:跟随 `network --follow` 只用于臂上;每 3 秒一次非跟随 `network --all --raw` 排空缓冲,
57
+ 取请求体与响应体。按 URL 去静态资源与 `data:` 后落盘。它覆盖整场,比页面钩子全(钩子只认该页 `fetch` / XHR)。
58
+ 不要换成 opencli 默认过滤(按响应 ct 白名单,会丢"按结束即回包未到"的写)。
59
+ - 其他:`_status` / `_install` / `_show`、`_components`(清单 / 类型面;`name=Button` 取声明)、
60
+ `_panel`、`_recon`、`_spec`(opencli 命令形状,插件适用)、`_logs_*`、`_debug_*`、`_record_*`(§4)。
61
+
62
+ ## 4 录制交接
63
+
64
+ | 工具 | 契约 |
29
65
  |---|---|
30
- | `_look` | `state` + `get html` + `eval` + `screenshot`(卡图靠 `clip`,上游没有 selector) |
31
- | `_reload` / `_viewport` | `eval 'location.reload()'` / `screenshot --width/--height` |
32
- | `_select` | **没了**(上游只认原生 `<select>`);下拉两拍:卡内点触发器 → `scope: 'page'` + `--role menuitem` 按名字点 |
33
- | `_net_*` | `network`(`--follow` 臂上、收工 `_debug_stop` 顺手收;**取 body 走 `_eval` 的 `fetch`**) |
34
- | `_click` `_hover` `_type` `_fill` `_keys` `_scroll` / `_logs_*` | 同名,参数改成原样转发(`console` 是空桩,控制台只能我们做) |
35
-
36
- ## 4 如实边界
66
+ | `_record_prepare` | 参数 `unit` / `url` / `doing`,可选 `expect:{method,urlContains}`。开窗置前(审批一次)、装浮条、臂上捕获。无 `url` 拒;跨域拒(模块 `design.json` 声明过读源时)。同时仅一场:已有场次未收工则拒且不弹审批。返回含 `wrote: false` |
67
+ | `_record_read` | 返回清单 `rows`(`index` / `key` / `at` / `method` / `url` / `status` / `ct` / `size` / `source` / `hasRequest` / `hasResponse` / `likelyWrite` / `marked`)、`hookCaught`、`capturedLines` / `capturedInSegment` / `capturedOutside`、`dropped` / `noBody` / `nav` / `captureRound`。不删行、不回体;钩子与捕获合并,同名只配一条钩子行(缺字段由捕获补);同 method+url 两次调用都保留。`expect` 仅标 ⭐ 并置顶。可选 `waitMs`(默认 0,上限 15 分钟;超时回 `timedOut` 并发 notice) |
68
+ | `_record_detail` | 参数 `key`(原样,可含 `#2`)或 `index`(仅场次存续时);同 key 跨轮重名,带 `at` 定位。返回请求体、响应体、请求头(可能含凭据,不写入 `design.json`)。可选 `unit`(收工后读 `shots/rec-<unit>.jsonl.last`);可选 `maxBody`(默认 60000 字符,0 不限)。取不到时给 DevTools 复制 curl 兜底 |
69
+ | `_record_stop` | 停捕获、收标签、清状态;先读文件,原文另存 `.last`。返回可写入 `design.json` 的 `probeCommand`(仅第一条带 body;无则空串并说明)。先 read / detail,再 stop |
37
70
 
38
- - `screenshot` **没有上游 selector**:卡图先 `eval` 量盒子再填 `clip`;不给路径我们补到工坊同级的 `shots/<unit|page>-<时间戳>.png`(否则回 base64);要裁的块不在视口里就退回整屏图,原因在 `cropReason`。
39
- - `state` **没有 `scope`**(卡内定位用 `find --css '$card …'`);**没有右键 / contextmenu**;ref 只在**一次快照**里有效,别跨调用存;`_dialog` 的 `accept|dismiss` 不校验;`_network --detail` **取不到 body**(缓存落在会话可写根之外)—— 走 `_eval` 里的 `fetch`。
40
- - 录制交接那条**会话捕获**(`_record_prepare` 起的:先短暂 `network --follow` 只做"臂上",然后每 3 秒一次**非跟随** `browser <session> network --all --raw`,**落盘前先按 URL 去掉静态资源与 `data:`**(含 node 就过滤、没有就原样落),一轮一行追加到工坊同级的 `shots/rec-<unit>.jsonl`)**带 body**:非跟随那条会把 live 缓冲排空,连 `request.body`(入参)与 `body`(出参)一起打出来;跟随那条只打形状(只有方法 / URL / 状态 / ct / size / 时间),所以只拿它臂上、装完就杀。它盖**整场**、在浏览器那一层,比页面钩子全(钩子只认那一页自己的 `fetch`/`XHR` —— 应用提前把 `window.fetch` 存走、或在 iframe / Worker / WebSocket 里发,它就看不见)。**不许改成"用 opencli 默认过滤"**:那层是**按响应 ct 白名单**判的(只留 json/xml/text-plain/javascript),"按下写就立刻按结束"那条**回包还没到、ct 为空**的写会被当场丢掉 —— 实测 127 条里它只留 33 条、那条写没了;按 URL 去静态则是 127 → 30 条、那条写还在。
41
- - 另外 19 把:台账 `_list`/`_status`/`_install`/`_show`、组件与 kit 清单 `_components`(不填给清单与类型面根,`name=Button` 回它的类型文件与声明)、宿主运行态 `_panel`/`_status`、数据源 `_recon`、`_spec`(问一条 opencli 命令的形状,**插件也适用**)、`_logs_*`、约定本身 `_debug_*`、录制交接 `_record_*`(§5)。
42
-
43
- ## 5 录制交接(四把):抓那一次"在外站页面上点出来的写",清单 → 挑一条 → 看原文
44
-
45
- | 工具 | 干什么 |
46
- |---|---|
47
- | `_record_prepare` | 开一扇窗**顶到最前**(**要用户点头一次**)、装浮条、臂上捕获。参数 `unit` / `url` / `doing`,可选 `expect:{method,urlContains}`;**不给 `url` 当场拒**,**跨域也当场拒**(那个模块的 `design.json` 说过读源时,只许同域录;还没说过就不拦)。返回带 `wrote: false` |
48
- | `_record_read` | 取那一段的**清单**:`rows`(每行 `index` / `key` / `at` / `method` / `url` / `status` / `ct` / `size` / `source` / `hasRequest` / `hasResponse` / `likelyWrite` / `marked`)· `hookCaught`(钩子这一段**原始**条数)· `capturedLines` / `capturedInSegment` / `capturedOutside` · `dropped` · `noBody` · `nav` · `captureRound`(会话捕获**最近一轮**成没成:`ok` / `fail` / 空 = 还没回)。**清单一条都不删、也不回体**:页面钩子(`[t0,t1]` 段)与会话捕获(整场按段切)合并,**同名只配掉一条钩子行**(缺的 key / 入参 / 出参由捕获那条补上);捕获里两条同 method+url 是**两次真调用,都留着**。**`expect` 不再是过滤器**:它只给命中的行标 ⭐,⭐ 排在前面(不然只列最后十几条时它可能不在正文里);不设 expect 时 ⭐ = 非 GET 或带 request body。**正文每一行把它要的两样直接摆出来**:`· [0] ⭐ POST 站点/路径(捕获 · 入参 有 · 出参 40 B · at 1792583917392)` —— 开头那串就是 `key` 原文(opencli 给的,可能带 `#2`),`at` 是毫秒时间戳;**没有 `key` 的钩子行**写成 `method url(钩子 · … · 无 key)`,那种行只在还开着时按 `index` 取得到。要哪一条的原文就带它的 `key`(或 `index`)去 `_record_detail`。没按开始 / 还在录都如实说,不假装"没有请求"。可选 `waitMs`(默认 0 = 立刻回,帽 15 分钟):等不到就回 `timedOut` 并挂一条 notice 叫醒你 —— **一场就是一个周期(开始 → 结束)**,结束之后要再录就**重新 `_record_prepare` 一场**(同场没有"再按一次开始"这回事)。**他按「结束录制」不用他再跟你说一声**:录制期间宿主每 3 秒自己看一眼那一页(浮条在目标页里、推不到宿主;宿主能叫醒你的通道只有 `deliverDebugNotice`),一看到 `finished` 就推状态并发一条 notice 叫你来 `read` |
49
- | `_record_detail` | 取**某一条**的原文:入参(`request.body`)与出参(`response body`),外加请求头(可能含凭据,别落进 `design.json`)。参数 `key`(清单里那一条,原样给、含 `#2` 这种后缀)或 `index`(清单里的序号,只在那一场还开着时可用);**同一个 key 跨轮会重名**(opencli 的 key 计数器每批重置),所以再带上清单里那行的 `at` 才钉得住是哪一次(不给就取最早那条,并在正文里说重名几次)。可选 `unit` —— 那一场**收工之后**要给 unit,我去读 `shots/rec-<unit>.jsonl.last`(`_record_stop` 会把原文留成 `.last`,下一场 prepare 开录时清掉它)。取不到时如实说"两边都没有",并给兜底:DevTools 复制 curl。可选 `maxBody`(默认 60000 字符,0 = 不限) |
50
- | `_record_stop` | 停捕获、收掉那个标签、清状态(**收工前先读一次捕获文件**,与 read 同一套切段 / 合并;**原文另存成 `.last`**,所以收工之后还能 `_record_detail`;没挪成就不说"原文留在…")。回一段能直接落 `design.json` 的原文(`probeCommand`)—— **只认第一条带 body 的**;一条都没有就回空串,并如实说"只有方法 / URL,body 拿不到 —— 补到 body 再落"(没 body 的写命令落下去是一条**会打错的**命令)。**先 read(或先 detail 完)再 stop** |
51
-
52
- 左栏底部那一格给他的键(等出手 `[取消]` / 录制中 `[停]`)只**给 agent 发一条消息**,不直接改页面那一半(`[停]` 有意不动状态);**录完那一档没有键** —— agent 自己来取、自己收工。
53
- 一场过期(TTL 15 分钟)或台账被换走,**不会把采集循环留在地上**:看门那一拍看到台账没了就停捕获 + 收标签,
54
- 循环本身也被宿主 pid 兜着(宿主重起后孤儿循环自己退);`prepare` 开新一场前也先对同一落点收一次。
55
- 细则、三颗键与如实边界(`fetch`/XHR 之外抓不到、50 条 / 16 KB 上限、一次一场 15 分钟)
56
- 都在 [WRITES.md](WRITES.md) 步骤 3;纪律同那一份的「三条写纪律」。
71
+ 左栏键(`[取消]` / `[停]`)只发消息,不改页面;录完无键。场次 TTL 15 分钟;过期或台账变更会停捕获、收标签。
72
+ 流程与写纪律见 [WRITES.md](WRITES.md) 步骤 3。