@dsh-workbench/core 0.3.10 → 1.0.0-beta.0

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 (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -105
  3. package/bin/dsh-workbench +132 -31
  4. package/bin/dsh-workbench.cmd +5 -0
  5. package/bin/opencli-isolated +3 -2
  6. package/bin/opencli-isolated.mjs +44 -0
  7. package/bin/opencli-node-mode.cjs +1 -0
  8. package/bin/seed-workshop-windows.mjs +307 -0
  9. package/client.js +8192 -4904
  10. package/cordis.patch.yml +62 -37
  11. package/dist/client/client.body.js +8192 -4904
  12. package/dist/client/index.js +7186 -3898
  13. package/dist/host/index.js +7277 -6956
  14. package/dist/types/app/client.d.ts +3 -1
  15. package/dist/types/app/host.d.ts +2 -1
  16. package/dist/types/app/layouts/approval-detail.d.ts +3 -0
  17. package/dist/types/app/layouts/env-gate/boot-gate.d.ts +1 -12
  18. package/dist/types/app/layouts/env-gate/gate-text.d.ts +2 -1
  19. package/dist/types/app/layouts/market/DiscoverPanel.d.ts +8 -0
  20. package/dist/types/app/layouts/market/add-module-text.d.ts +1 -1
  21. package/dist/types/app/layouts/market/discover-categories.d.ts +8 -0
  22. package/dist/types/app/layouts/market/discover-confirm.d.ts +14 -0
  23. package/dist/types/app/layouts/market/discover-detail.d.ts +12 -0
  24. package/dist/types/app/layouts/market/discover-dialogs.d.ts +7 -0
  25. package/dist/types/app/layouts/market/discover-doc.d.ts +18 -0
  26. package/dist/types/app/layouts/market/discover-init-dialog.d.ts +10 -0
  27. package/dist/types/app/layouts/market/discover-rows.d.ts +24 -0
  28. package/dist/types/app/layouts/market/discover-sources-dialog.d.ts +16 -0
  29. package/dist/types/app/layouts/market/discover-states.d.ts +22 -0
  30. package/dist/types/app/layouts/market/discover-toolbar.d.ts +23 -0
  31. package/dist/types/app/layouts/market/discover-versions.d.ts +9 -0
  32. package/dist/types/app/layouts/market/discover-view.d.ts +38 -0
  33. package/dist/types/app/layouts/market/market-header.d.ts +13 -0
  34. package/dist/types/app/layouts/market/panel.d.ts +1 -1
  35. package/dist/types/app/layouts/market/use-add-module.d.ts +1 -1
  36. package/dist/types/app/layouts/market/use-discover.d.ts +72 -0
  37. package/dist/types/app/layouts/market/use-module-versions.d.ts +6 -2
  38. package/dist/types/app/layouts/rightbar/MarketView.d.ts +6 -3
  39. package/dist/types/app/layouts/rightbar/Rightbar.d.ts +1 -1
  40. package/dist/types/app/layouts/rightbar/board-shape.d.ts +1 -14
  41. package/dist/types/app/layouts/rightbar/market-act.d.ts +6 -3
  42. package/dist/types/app/layouts/rightbar/market-busy.d.ts +0 -22
  43. package/dist/types/app/layouts/rightbar/market-change.d.ts +1 -1
  44. package/dist/types/app/layouts/rightbar/market-hint.d.ts +1 -7
  45. package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +0 -7
  46. package/dist/types/app/layouts/rightbar/market-row-icon.d.ts +1 -1
  47. package/dist/types/app/layouts/rightbar/market-row-types.d.ts +2 -2
  48. package/dist/types/app/layouts/rightbar/market-rows.d.ts +1 -1
  49. package/dist/types/app/layouts/rightbar/market-section-icon.d.ts +1 -1
  50. package/dist/types/app/layouts/rightbar/market-settle-hold.d.ts +2 -0
  51. package/dist/types/app/layouts/rightbar/market-settle.d.ts +17 -0
  52. package/dist/types/app/layouts/rightbar/market-shared-rows.d.ts +1 -1
  53. package/dist/types/app/layouts/rightbar/market-update-verdict.d.ts +31 -0
  54. package/dist/types/app/layouts/rightbar/market-view-text.d.ts +8 -1
  55. package/dist/types/app/layouts/rightbar/market-workbench-row.d.ts +1 -1
  56. package/dist/types/app/layouts/rightbar/store.d.ts +2 -0
  57. package/dist/types/app/layouts/sidebar/LongTaskDot.d.ts +6 -0
  58. package/dist/types/app/layouts/sidebar/LongTaskHover.d.ts +6 -0
  59. package/dist/types/app/layouts/sidebar/edition-boot.d.ts +6 -0
  60. package/dist/types/app/layouts/sidebar/edition.d.ts +5 -0
  61. package/dist/types/app/layouts/sidebar/use-attention.d.ts +0 -4
  62. package/dist/types/app/layouts/sidebar/workshop-preset.d.ts +2 -0
  63. package/dist/types/app/layouts/workbench-settings/AdapterSetItem.d.ts +5 -0
  64. package/dist/types/app/layouts/workbench-settings/ConfigSyncItem.d.ts +5 -0
  65. package/dist/types/app/layouts/workbench-settings/ConfigSyncRows.d.ts +11 -0
  66. package/dist/types/app/layouts/workbench-settings/NewWorkbenchItem.d.ts +5 -0
  67. package/dist/types/app/layouts/workbench-settings/SettingsItem.d.ts +8 -0
  68. package/dist/types/app/layouts/workbench-settings/SidebarEditionItem.d.ts +5 -0
  69. package/dist/types/app/layouts/workbench-settings/WorkbenchSettingsSection.d.ts +5 -0
  70. package/dist/types/app/units/manifests.d.ts +1 -1
  71. package/dist/types/{app/units → kernel/client}/registry.d.ts +1 -1
  72. package/dist/types/kernel/contract/bundle-install.d.ts +2 -0
  73. package/dist/types/kernel/contract/config-sync.d.ts +2 -0
  74. package/dist/types/kernel/contract/diy.d.ts +1 -0
  75. package/dist/types/kernel/contract/dsh-plugins.d.ts +2 -0
  76. package/dist/types/kernel/contract/index.d.ts +4 -0
  77. package/dist/types/kernel/contract/long-tasks.d.ts +2 -0
  78. package/dist/types/kernel/contract/market-index.d.ts +2 -0
  79. package/dist/types/kernel/contract/platform.d.ts +1 -0
  80. package/dist/types/kernel/contract/preflight-wake.d.ts +2 -0
  81. package/dist/types/kernel/contract/profile-create.d.ts +2 -0
  82. package/dist/types/kernel/contract/sidebar-edition.d.ts +2 -0
  83. package/dist/types/kernel/contract/unit-manifests.d.ts +2 -0
  84. package/dist/types/{shared/api → kernel}/host/mount-units.d.ts +2 -1
  85. package/dist/types/modules/panels/client.d.ts +1 -1
  86. package/dist/types/modules/panels/model/approval.d.ts +4 -8
  87. package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -1
  88. package/dist/types/modules/panels/model/diy-changelog.d.ts +2 -0
  89. package/dist/types/modules/panels/model/diy-context.d.ts +20 -18
  90. package/dist/types/modules/panels/model/diy-page.d.ts +4 -2
  91. package/dist/types/modules/panels/model/diy-record-open.d.ts +0 -15
  92. package/dist/types/modules/panels/model/diy-record-text.d.ts +0 -2
  93. package/dist/types/modules/panels/model/diy-record.d.ts +1 -31
  94. package/dist/types/modules/panels/model/diy-report.d.ts +6 -7
  95. package/dist/types/modules/panels/model/diy-tools.d.ts +1 -0
  96. package/dist/types/modules/panels/model/long-task-tools.d.ts +0 -11
  97. package/dist/types/modules/panels/model/market-dependency-update.d.ts +15 -0
  98. package/dist/types/modules/panels/model/market-dependency.d.ts +15 -10
  99. package/dist/types/modules/panels/model/market-discover-routes.d.ts +3 -0
  100. package/dist/types/modules/panels/model/market-discover.d.ts +33 -0
  101. package/dist/types/modules/panels/model/market-dispatch.d.ts +4 -3
  102. package/dist/types/modules/panels/model/market-env.d.ts +2 -3
  103. package/dist/types/modules/panels/model/market-registry.d.ts +3 -3
  104. package/dist/types/modules/panels/model/market-restart.d.ts +1 -1
  105. package/dist/types/modules/panels/model/market-route.d.ts +5 -3
  106. package/dist/types/modules/panels/model/market-sources-act.d.ts +9 -0
  107. package/dist/types/modules/panels/model/market-types.d.ts +7 -13
  108. package/dist/types/modules/panels/model/market-update-bundle.d.ts +19 -0
  109. package/dist/types/modules/panels/model/market-verify.d.ts +15 -0
  110. package/dist/types/modules/panels/model/market-workbench-update.d.ts +3 -2
  111. package/dist/types/modules/panels/model/market.d.ts +6 -5
  112. package/dist/types/modules/panels/model/plugin-actions.d.ts +2 -1
  113. package/dist/types/modules/panels/model/plugin-section.d.ts +3 -1
  114. package/dist/types/modules/panels/model/publish-preview-routes.d.ts +2 -0
  115. package/dist/types/modules/panels/unit.d.ts +8 -5
  116. package/dist/types/modules/sessions/model/attention.d.ts +0 -11
  117. package/dist/types/modules/sessions/model/session-node.d.ts +0 -10
  118. package/dist/types/shared/api/diy.d.ts +6 -4
  119. package/dist/types/shared/api/host/approval-call.d.ts +0 -9
  120. package/dist/types/shared/api/host/approval-gate.d.ts +26 -0
  121. package/dist/types/shared/api/host/bundle-routes.d.ts +2 -1
  122. package/dist/types/shared/api/host/diy-artifacts.d.ts +3 -0
  123. package/dist/types/shared/api/host/diy-changelog.d.ts +13 -0
  124. package/dist/types/shared/api/host/diy-components.d.ts +2 -3
  125. package/dist/types/shared/api/host/diy-modules.d.ts +25 -0
  126. package/dist/types/shared/api/host/diy-publish-preview.d.ts +4 -0
  127. package/dist/types/shared/api/host/diy-recon-fs.d.ts +2 -2
  128. package/dist/types/shared/api/host/diy-tokens.d.ts +8 -0
  129. package/dist/types/shared/api/host/host-routes.d.ts +10 -0
  130. package/dist/types/shared/api/host/long-task-notice.d.ts +0 -20
  131. package/dist/types/shared/api/host/long-tasks.d.ts +1 -8
  132. package/dist/types/shared/api/host/market-doc.d.ts +21 -0
  133. package/dist/types/shared/api/host/market-index-fetch.d.ts +37 -0
  134. package/dist/types/shared/api/host/market-source-init.d.ts +22 -0
  135. package/dist/types/shared/api/host/market-sources-store.d.ts +15 -0
  136. package/dist/types/shared/api/host/permission-mode.d.ts +4 -0
  137. package/dist/types/shared/api/host/post-channels.d.ts +1 -1
  138. package/dist/types/shared/api/host/sidebar-edition.d.ts +14 -0
  139. package/dist/types/shared/api/host/unit-roster-facts.d.ts +1 -1
  140. package/dist/types/shared/api/host/workspace-root.d.ts +6 -0
  141. package/dist/types/shared/api/platform/client.d.ts +10 -0
  142. package/dist/types/shared/api/unit-board.d.ts +4 -0
  143. package/dist/types/shared/api/write-outcome.d.ts +3 -0
  144. package/dist/types/shared/components/IconModules.d.ts +2 -0
  145. package/dist/types/shared/components/PanelFoot.d.ts +0 -9
  146. package/dist/types/shared/components/blocker-action.d.ts +3 -0
  147. package/dist/types/shared/dev-surface/index.d.ts +5 -0
  148. package/dist/types/shared/dev-surface/types.d.ts +79 -0
  149. package/dist/types/shared/hooks/bundle-queries.d.ts +1 -13
  150. package/dist/types/shared/hooks/fact-transport.d.ts +4 -0
  151. package/dist/types/shared/hooks/fact-transports.d.ts +0 -4
  152. package/dist/types/shared/hooks/market-queries.d.ts +11 -0
  153. package/dist/types/shared/hooks/panel-state-report.d.ts +0 -12
  154. package/dist/types/shared/hooks/sidebar-edition.d.ts +2 -0
  155. package/dist/types/shared/hooks/use-blocker-actions.d.ts +0 -8
  156. package/dist/types/shared/hooks/use-capabilities.d.ts +1 -55
  157. package/dist/types/shared/hooks/use-capability-gate.d.ts +1 -2
  158. package/dist/types/shared/hooks/use-diy-debug.d.ts +0 -13
  159. package/dist/types/shared/hooks/use-diy-record.d.ts +0 -13
  160. package/dist/types/shared/hooks/use-host-write.d.ts +1 -1
  161. package/dist/types/shared/hooks/use-shell-long-tasks.d.ts +1 -7
  162. package/dist/types/shared/hooks/use-unit-board.d.ts +1 -1
  163. package/icon.svg +17 -28
  164. package/package.json +33 -13
  165. package/preset/diy.patch.yml +3 -3
  166. package/profile/isolation.patch.yml +18 -7
  167. package/profile/sandbox.patch.yml +2 -2
  168. package/scripts/clone-profile.mjs +268 -0
  169. package/scripts/create-workbench.mjs +166 -0
  170. package/scripts/seed-profile.sh +22 -10
  171. package/scripts/seed-workshop.sh +17 -19
  172. package/skills/workbench-module-diy/BUILD.md +57 -133
  173. package/skills/workbench-module-diy/COPY.md +89 -0
  174. package/skills/workbench-module-diy/DATASOURCE.md +173 -116
  175. package/skills/workbench-module-diy/DESIGN.md +76 -109
  176. package/skills/workbench-module-diy/FAMILY.md +48 -52
  177. package/skills/workbench-module-diy/KIT.md +78 -133
  178. package/skills/workbench-module-diy/LINKAGE.md +66 -105
  179. package/skills/workbench-module-diy/PANEL.md +81 -171
  180. package/skills/workbench-module-diy/REVIEW.md +67 -81
  181. package/skills/workbench-module-diy/SKILL.md +66 -96
  182. package/skills/workbench-module-diy/STEPS.md +66 -69
  183. package/skills/workbench-module-diy/TOOLS.md +134 -103
  184. package/skills/workbench-module-diy/WIRING.md +64 -84
  185. package/skills/workbench-module-diy/WRITES.md +115 -142
  186. package/dist/types/app/layouts/config-sync/ConfigSyncSection.d.ts +0 -5
  187. package/dist/types/shared/api/bundle-install.d.ts +0 -123
  188. package/dist/types/shared/api/config-sync.d.ts +0 -35
  189. package/dist/types/shared/api/dsh-plugins.d.ts +0 -11
  190. package/dist/types/shared/api/host/bundle-manager.d.ts +0 -11
  191. package/dist/types/shared/api/host/config-sync.d.ts +0 -46
  192. package/dist/types/shared/api/host/diy-preset-health.d.ts +0 -35
  193. package/dist/types/shared/api/host/diy-workshop.d.ts +0 -23
  194. package/dist/types/shared/api/host/dsh-plugins.d.ts +0 -5
  195. package/dist/types/shared/api/host/installed-module.d.ts +0 -54
  196. package/dist/types/shared/api/host/installed-module.test.d.ts +0 -1
  197. package/dist/types/shared/api/host/opencli-command.d.ts +0 -10
  198. package/dist/types/shared/api/host/opencli-command.test.d.ts +0 -1
  199. package/dist/types/shared/api/host/opencli.test.d.ts +0 -1
  200. package/dist/types/shared/api/host/package-content.d.ts +0 -6
  201. package/dist/types/shared/api/host/package-content.test.d.ts +0 -1
  202. package/dist/types/shared/api/host/package-kind.d.ts +0 -21
  203. package/dist/types/shared/api/host/package-kind.test.d.ts +0 -1
  204. package/dist/types/shared/api/host/package-route.d.ts +0 -23
  205. package/dist/types/shared/api/host/package-route.test.d.ts +0 -1
  206. package/dist/types/shared/api/host/plugin-catalog.d.ts +0 -65
  207. package/dist/types/shared/api/host/plugin-login-hint.d.ts +0 -2
  208. package/dist/types/shared/api/host/plugin-tools.d.ts +0 -14
  209. package/dist/types/shared/api/host/plugin-units.d.ts +0 -48
  210. package/dist/types/shared/api/host/preflight-route.d.ts +0 -15
  211. package/dist/types/shared/api/host/preflight.d.ts +0 -75
  212. package/dist/types/shared/api/host/probe-cache.d.ts +0 -6
  213. package/dist/types/shared/api/host/probe-libs.d.ts +0 -11
  214. package/dist/types/shared/api/host/probe-units.d.ts +0 -17
  215. package/dist/types/shared/api/host/probe-verdict.d.ts +0 -7
  216. package/dist/types/shared/api/host/profile-registries.d.ts +0 -21
  217. package/dist/types/shared/api/host/registry-declaration.d.ts +0 -23
  218. package/dist/types/shared/api/host/release.d.ts +0 -24
  219. package/dist/types/shared/api/host/runtime-seed.d.ts +0 -38
  220. package/dist/types/shared/api/host/self-update.d.ts +0 -45
  221. package/dist/types/shared/api/host/version-range.d.ts +0 -2
  222. package/dist/types/shared/api/host/workshop-root.d.ts +0 -16
  223. package/dist/types/shared/api/long-tasks.d.ts +0 -19
  224. package/dist/types/shared/api/unit-manifests.d.ts +0 -6
  225. package/docs/HANDOFF.md +0 -333
  226. package/docs/OPERATIONS.md +0 -229
  227. package/skills/workbench-module-diy/TOOLING.md +0 -55
  228. /package/dist/types/{app/layouts/rightbar/registry.d.ts → kernel/client/board-registry.d.ts} +0 -0
  229. /package/dist/types/{app/layouts/rightbar/registry.test.d.ts → kernel/contract/contracts.test.d.ts} +0 -0
  230. /package/dist/types/{shared/api/host/bundle-manager.test.d.ts → kernel/contract/index.test.d.ts} +0 -0
  231. /package/dist/types/{shared/api → kernel}/host/unit-readiness.d.ts +0 -0
  232. /package/dist/types/{shared/api → kernel}/host/unit-registry.d.ts +0 -0
  233. /package/dist/types/shared/{api/host/diy-workshop.test.d.ts → components/blocker-action.test.d.ts} +0 -0
  234. /package/dist/types/shared/{api/host/dsh-plugins.test.d.ts → dev-surface/install.d.ts} +0 -0
@@ -1,130 +1,187 @@
1
- # 数据从哪来:干净机器上先用 opencli 探,再把它固化成一条读
1
+ # 数据源:探一条读,再固化
2
2
 
3
- 用户往往只说"我要看发布情况""我想看缺陷看板" —— 他不知道那套系统叫什么、接口在哪、要什么参数。
4
- 干净机器上你也没有现成的 skill 可抄。所以这一步是**探查**,不是查手册。
3
+ 用户只说"我要看 X",不提供接口。干净机器上没有现成 skill。步骤是探查,不是查手册。
5
4
 
6
- ## 先判:这是 DIY 域还是工程域
5
+ ## 判域
7
6
 
8
- | 情况 | 判 |
7
+ | 情况 | 域 |
9
8
  |---|---|
10
- | 他已经在浏览器里登录过那套系统 | **DIY 域** —— 用 opencli 复用他的登录去探(下面四步) |
11
- | 要**新凭据**(新账号 / 密钥 / token) | **工程域** —— 停下来,说清要谁去配什么 |
12
-
13
- 判的是「**要不要新凭据**」,不是「要不要接新平台」。复用已有登录的探查,正是把一大类需求留在 DIY 域的原因。
14
- 其余几类(本地文件 / 已有命令 / 不要凭据的 GET → 直接造;新服务 / 新依赖 / 改外壳 → 工程域):[DESIGN.md](DESIGN.md) §0 那张全表。
15
-
16
- ## 四步
17
-
18
- 1. **问出他在看什么(用他的词)** —— "你平时是在哪个网页 / 哪个软件里看这个的?" 记下他说的入口。
19
- **不要**问"你们用 argocd 还是 jenkins" —— 他多半不知道,也答不上来。
20
- 2. **数据从哪来 —— 四层,从上往下试**(**用户不知道 opencli 是什么,这一整步是你的活**:
21
- 别问他"你用哪个插件 / 有没有 API",他说的是"我想看 X",把 X 翻成关键词是你的活)。
22
-
23
- ① **先看 opencli 自带的**:`workbench_diy_recon`(`keyword=<站点名 / 系统名 / 域名>`,
24
- 也可以直接给 `url` 让它取域名)。它回这台机器上**已经能用**的能力:站点、命令、`strategy`
25
- (PUBLIC / COOKIE / INTERCEPT / UI)以及**要不要用户的 Chrome 登录**,还有能直接照抄的那一行。
26
- **有就直接复用:不用装、不用点头。** 官方那份发现指引:`opencli skills read opencli-usage`。
27
- - **命中了要看全**:一个词常常翻出好几组(打 `arxiv` 会同时带出 `hf`、`semanticscholar`),
28
- 只看第一组就会漏掉能互补的命令(引用数、相关推荐那类);
29
- - **挑的时候优先 `public`**:不用他的 Chrome、不用登录、不用点头,卡也最容易验;
30
- `cookie` / `ui` / `intercept` 的先把"他有没有登录"这件事摆到明面上。
31
- - **一张卡背后可以有多条读**(甚至跨站点):design.json 用 `reads: [ … ]` 一条一行写全,
32
- 每条各自的"参数 / 凭据 / 没登录长什么样 / 超时长什么样"都别省 —— 卡片上要按条给失败态。
33
- ② **再看这台机器另装的插件**:recon 会一并列出来(另一面是 `opencli plugin list`)。用
34
- `opencli <插件> <命令>`,参数用 `--help` 看 —— 也可以 `workbench_diy_spec`(`command='<site> <name>'`)
35
- 一次问全:它把 `--help -f json` **原样**回(插件与内置都能问)。
36
- ③ **官方有、这台机器没有** → 才考虑装:社区插件 `opencli plugin install github:user/repo`、
37
- 外部 CLI `opencli external install <名字>`,或在模块清单里声明 `manifest.env.plugins`
38
- (`installSource`:npm 包 / git / 本地)。**装是这台机器上的持久变更、而且会跑别人的代码 → 要用户
39
- 点头**,优先内网或官方源。
40
- ④ **都没有** → 才自己开发,而且两条官方流程都能用:
41
- - 只读一次 → 下面那条 `probe`;
42
- - 想长期用 → 给这个站点写 adapter:`opencli skills read opencli-adapter-author`(侦察 → 字段解码 →
43
- 写 → verify);以后 adapter 失效:`opencli skills read opencli-autofix`。
44
-
45
- **④ 才轮到探** —— 用工具:`workbench_diy_eval`(`read=<那套系统的地址>, args=["<一段回 JSON 的脚本>"]`;
46
- 页面里没有接口就给 `css=<选择器>`,走的是同一条固定 DOM 读,`{entries:[{text}]}` 直接回给你)。
47
- 它用**用户浏览器里现有的登录**去读,并把"读不到"归一成阻断屏认的那几个码 —— 两拍(不在站点上就自己
48
- 打开 `url` 再读一遍)、刚重启时的重试、平台码到阻断码的映射**都在工具里**,不用你记。
49
- 目标是找到**承载数据的那一处**:列表页本身,或它背后的那一个 XHR。记下:URL、需要的参数、凭据从哪来。
50
- 要手工看别的东西(列请求、点页面)时才直接用 `opencli`。
51
- - **不知道数据在哪**时先跑一条侦察脚本(`script`):`performance.getEntriesByType('resource')`
52
- 里筛 `/api/`、`.json` —— 有一串就是**接口读**(顺它去 fetch);一条都没有、页面上却有一整块
53
- 表格 / 列表,那就是 **DOM 读**(换 `css` 读它)。这个判断一次就够,别反复试。
54
- - 手工用 `opencli` 时记住:**它随工作台预装**(隔离运行时在 `$DSH_HOME/workbench/opencli/`,
55
- 启动体检自己装,**不碰**用户本机的 `~/.opencli`);没装好时它会自己说"opencli 的隔离运行时
56
- 还没装好" —— 把体检那屏的原话转给用户,别硬编 URL 猜(那正是"看起来能用、其实读不到"的来源)。
57
- - 脚本的三条约定(工具就按这个判):不在站点上回 `{ ok: false, code: "not-on-site" }`;登录过期回
58
- `{ ok: false, code: "not-logged-in" }`(工具归一成 `auth-required`);成功回 `{ ok: true, ...数据 }`。
59
- - **`browser network --detail` 在会话里拿不到 body**:捕获缓存写在会话可写根之外
60
- (`EPERM … opencli/home/.opencli/cache/browser-network`),只能列请求 key,`--detail` 永远
61
- `cache_missing`。换这条:`opencli browser <session> eval "<一段 fetch 脚本>"` —— 在页面里用
62
- 他的登录去读,返回 JSON。它既能当探数的手段,也常常**直接就是那条固化读**。
63
- - **新 session 的 `eval` 会静默开一个 `about:blank` 标签页**(不是报错)—— 它落在 opencli 自己的后台容器窗里,不抢焦点、也不进用户当前窗。所以"读之前先判在不在
64
- 那个站点"必须写在脚本第一行:`if (location.origin !== "https://…") return { ok: false, code: "not-on-site" }`。
65
- 固化读的完整形状是**两拍**:不在站点上 → `open` 那个页 → 等一会儿(约 1 秒)再 eval 一遍。
66
- 每一拍都由 shared 的 `backgroundLine` 拼(前缀 `env OPENCLI_WINDOW=background`)—— 别绕开它直接拼
67
- `opencli browser …`:漏一拍就是把用户的 Chrome 顶到面前一次。
68
- - **读自己一条会话,屏是另一条 —— 这两条不许同名**。`browserRead` / `browserFind` 的默认会话是
69
- `BROWSER_READ_SESSION`(`workbench-read`);`BROWSER_SCREEN_SESSION`(`workbench`)是
70
- **工坊看/动族给"用户当前那一页"上租约**用的那条。两条同名的后果是"一读就把用户那一页导航走"
71
- (`open` 那一拍落在租约那个标签上)—— 2026-10-19「套餐余额」卡片一挂载就跳 `/wallet` 就是这个。
72
- 想给自己一条专线(不跟别的模块抢同一个 tab)就传自己的名字:`session: "<unit>"`。
73
- - **页面里没有接口时(DOM 读)不要用 `eval` 去 `document.querySelectorAll`**:产物边界判据会拦
74
- (宿主产物里不许出现 `document.` / `window.` 字样),而且那样解析只能窝在字符串里、测不了。
75
- 探的时候 `workbench_diy_find`(`read=…, args=["--css", "<选择器>"]`)就是这个口子;
76
- 固化时走 `browserFind`,它内部用的就是 `opencli browser <session> find --css '<选择器>' --limit N --text-max M`,
77
- 回 `{ entries: [{ text, tag, nth }] }` —— **一行的单元格按换行拼在 `text` 里**(`美元\n668.3\n…`),
78
- 所以"切格 + 按列序取值"是两个纯函数,能单测。很多公开站点一条 XHR 都不发,就该走这条。
79
- - **工作台刚重启时桥还没起来**:第一次读会以"命令跑不起来"失败(不是"数据读不到")——
80
- 等一秒再读一次就好。超时**不**重试:那已经等满了。这条与上面两条一样,`browserRead` 里都写好了。
81
- 3. **试一条最小读** —— 先证明"能拿到",再写模块:
82
- - 拿得到结构化输出 → 记下字段形状
83
- - 要登录但已过期 → 记下"未登录"长什么样
84
- - 超时 / 被墙 → 记下这两种失败的形状
85
- 这一步的产出是**一条可重复的命令或请求**,不是"我探过了"。
86
- 4. **固化进模块** —— 先把它照抄进 `design.json`(只有一条读写 `datasource{}`;一条卡有好几条读写 `reads[]`,一条一行;字段一样:哪条读 / 参数 / 凭据 /
87
- 没登录长什么样 / 超时长什么样 / 当初那次调用的原文),再写进模块的**宿主半侧**:用 `@dsh-workbench/shared/host` 导出的
88
- `browserRead({ shell, script, url, loginUrl })`(**接口读**:探明的那一段脚本原样搬进去就行 ——
89
- 两拍、重试、平台码映射、`message` / `hint` / `detail` / `helpUrl` 四个字段都在它里面;
90
- 会话不传就落在读那条上,想给自己一条专线再传 `session: "<unit>"`)或
91
- `browserFind({ shell, css, url, session })`(**DOM 读**:它替你问"现在停在哪一页"(`browser get url`),
92
- 不在就 `open` 再读一遍,失败时把 opencli 自己说的原因带出来),或者只读路由里的一个 HTTP GET。之后卡片只依赖**这一条**;渲染怎么改都不动它。
93
-
94
- 这一步只管**读**。要 agent 真去**改**东西(提交 / 发布 / 建单 / 改配置),那半边走 [WRITES.md](WRITES.md)。
95
-
96
- `opencli` 是**侦察兵,不是日常通道**:探明之后,日常读必须是一条固定的、便宜的、能解释的调用。
97
-
98
- ## 固化时一并写掉三态
99
-
100
- 拿不到数据是常态(第一次读、登录过期、站点改版、接口超时)。卡片**必须**画得出来:
101
-
102
- | 情况 | 卡片说什么 |
9
+ | 用户已在浏览器登录过该系统 | DIY:用 opencli 复用登录探(下文五步) |
10
+ | 要新凭据(账号 / 密钥 / token) | 工程:停,说清谁配什么 |
11
+
12
+ 判据是"要不要新凭据",不是"要不要接新平台"。其余类型见 [DESIGN.md](DESIGN.md) §0。
13
+
14
+ ## 五步
15
+
16
+ 1. **问入口** —— "你平时在哪个网页 / 软件里看这个?"记下入口。不问"用 deploy 还是 jenkins"。
17
+ 把用户的需求翻成站点 / 系统关键词是你的工作。
18
+
19
+ 2. **按四层找数据**:
20
+
21
+ ① **opencli 自带**:`workbench_diy_recon`(`keyword=<站点 / 系统 / 域名>`,或 `url` 取域名)。
22
+ 返回可用能力:站点、命令、`strategy`(PUBLIC / COOKIE / INTERCEPT / UI)、是否需要用户 Chrome 登录、
23
+ 可直接复用的命令。**命中即复用,不装不审批。** 官方发现指引:`opencli skills read opencli-usage`。
24
+ 一个词可能带出多组(如 `arxiv` 带 `hf`、`semanticscholar`),看全。优先 `public`;`cookie` / `ui` /
25
+ `intercept` 先确认用户登录状态。一张卡可有多条读(可跨站点):`design.json` 用 `reads[]` 一条一行,
26
+ 每条写参数 / 凭据 / 未登录 / 超时,卡片按条给失败态。
27
+
28
+ ② **这台机器另装的插件**:recon 一并列出(或 `opencli plugin list`)。参数用 `--help`,或
29
+ `workbench_diy_spec`(`command='<site> <name>'`)取 `--help -f json` 原文(插件与内置均可)。
30
+
31
+ ③ **官方有、这台机器没有** → 安装:`opencli plugin install github:user/repo`、`opencli external install <名>`,
32
+ 或模块清单 `needs` 里 `kind: "extension"` 的那些。安装改这台机器且执行第三方代码 → 需用户审批;优先内网或官方源。
33
+
34
+ ④ **都没有** → 开发:一次性探用下文的 eval;长期用写 adapter(`opencli skills read opencli-adapter-author`,
35
+ 失效用 `opencli-autofix`)。
36
+
37
+ **探**:`workbench_diy_eval`(`read=<地址>, args=["<回 JSON 的脚本>"]`;无接口给 `css=<选择器>`,
38
+ 走固定 DOM 读,返回 `{entries:[{text}]}`)。它复用用户浏览器登录,并把失败归一成阻断屏错误码;
39
+ 两拍、重启重试、平台码映射都在工具内。目标:找到承载数据的位置(列表页或其后的 XHR),
40
+ 记 URL、参数、凭据来源。
41
+
42
+ **Desktop**:这条 `read=` 探索线照旧;Desktop 只是不能对 Electron 里的工作台页做屏调试,
43
+ 不影响站点探索。录制(`_record_*`)也照旧。
44
+
45
+ - 不知道数据在哪:先跑 `performance.getEntriesByType('resource')`,筛 `/api/`、`.json`。
46
+ 有 → 接口读;无但页面有整块表格 → DOM 读。判断一次即可。
47
+ - `opencli` 随工作台预装(隔离运行时 `$DSH_HOME/workbench/opencli/`,启动体检安装,不碰 `~/.opencli`)。
48
+ 未装好时工具会报"隔离运行时还没装好";转述原文,不猜 URL。
49
+ - 脚本约定:不在站点 → `{ ok: false, code: "not-on-site" }`;登录过期 → `{ ok: false, code: "not-logged-in" }`
50
+ (工具归一为 `auth-required`);成功 → `{ ok: true, ...数据 }`。
51
+ - 新 session 的 `eval` 会静默开 `about:blank`(后台容器窗,不抢焦点)。脚本第一行判断
52
+ `location.origin`;不在站点则 `open` 该页,等约 1 秒再 eval。两拍都用 shared 的 `backgroundLine`
53
+ 拼(前缀 `env OPENCLI_WINDOW=background`),不要直接拼 `opencli browser …`。
54
+ - 读与屏用不同会话:`data.read({ kind: 'browser' })` 默认读会话 `workbench-read`;`workbench` 是看 / 动族的屏会话。同名会把用户的页导航走。需要独立标签就传 `session: "<unit>"`。
55
+ - DOM 读不要在宿主脚本里 `querySelectorAll`:宿主产物禁止 `document.`,且无法单测。探时用
56
+ `workbench_diy_find`(`read=…, args=["--css", "<选择器>"]`);固化用
57
+ `data.read({ kind: 'browser', url, script })` 返回 JSON,把"切格 + 按列取值"做成两个纯函数。
58
+ - 工作台刚重启时桥未就绪:首次读报"命令跑不起来",等一秒重读;超时不重试。
59
+
60
+ 3. **试一条最小读** —— 先证明能拿到,再写模块。记录:结构化字段形状;未登录长什么样;超时 / 被墙的形状。
61
+ 产出必须是一条可重复的命令或请求。
62
+
63
+ 4. **落配方** —— 把探明结果填成 `design.json` 里这条读的 `recipe`:`origin`、`request`(method / path /
64
+ query / body / contentType)、`auth`(凭据从哪来、放到哪)、`response`(json / text、取哪一格)。
65
+ 同时把 `key` 写成 lowerCamelCase。配方与"哪条读 / 参数 / 凭据 / 没登录 / 超时 / 当时调用原文"
66
+ 并存:前两格给人看,后一格给壳用。形状见下面「配方」。
67
+ 5. **固化进模块** —— 跑 `dsh-wb-module sync`,它把 `key + recipe` 生成到 `src/datasource.ts`;
68
+ 宿主半侧 `mount({ data })` 只写一行 `data.request(DATASOURCE.<key>, overrides?)`(读)或
69
+ `data.submit(...)`(写,在工具里)。路由把 `DataResult` 摊平成 `{ ok: true, ...data }` /
70
+ `{ ok: false, error }`,卡片只依赖这条路由。写操作走 [WRITES.md](WRITES.md)。
71
+
72
+ ## 配方
73
+
74
+ ```json
75
+ {
76
+ "key": "balance",
77
+ "recipe": {
78
+ "origin": "https://x.example.com",
79
+ "loginUrl": "https://x.example.com/login",
80
+ "request": { "method": "GET", "path": "/api/balance", "query": { "scope": "self" } },
81
+ "response": { "kind": "json", "path": "data" }
82
+ },
83
+ "read": "GET /api/balance(登录态 cookie)",
84
+ "params": "scope 固定 self",
85
+ "credentials": "复用浏览器登录:同源 cookie",
86
+ "notLoggedIn": "401 → 阻断屏,code auth,helpUrl 指 /login",
87
+ "timeout": "25 秒;超时归 timeout,不整卡换文案"
88
+ }
89
+ ```
90
+
91
+ 宿主侧:
92
+
93
+ ```ts
94
+ routes: {
95
+ 'x/balance': async () => {
96
+ const result = await data.request<Balance>(DATASOURCE.balance);
97
+ return result.ok ? { ok: true, ...result.data } : result;
98
+ },
99
+ }
100
+ ```
101
+
102
+ 六种配方:
103
+
104
+ | 配方 | 什么时候用 | `auth` |
105
+ |---|---|---|
106
+ | 公开 | 不要登录 | `{ kind: 'none' }` |
107
+ | cookie | 同源接口,cookie 就够 | 省略(默认) |
108
+ | cookie + CSRF | 表单类后台 | `source: meta / cookieValue`,`place: header` |
109
+ | bearer-refresh | 页面先换 token(如 `/api/auth/refresh`) | `source: endpoint`,`place: header` |
110
+ | bearer-page | token 在页面初始 state / storage | `source: global / storage`,`place: header` |
111
+ | custom | 签名 / WS / 多步 / 一次性 token | 不写 recipe,写 `custom: { why }`,代码里用旧的 `data.read({ kind: 'browser', script })` |
112
+
113
+ 同一次读要打多条接口时(一个 refresh 换 token,再并发取余额、用量、订阅……),不要拆成 N 条配方
114
+ 各调一次:一条配方 + 一格 `calls`,一次换 token、并发打,结果按键回、每格各自成败:
115
+
116
+ ```json
117
+ {
118
+ "key": "wallet",
119
+ "recipe": { "origin": "https://x.example.com",
120
+ "auth": { "source": { "kind": "endpoint", "url": "/api/user/auth/refresh",
121
+ "method": "POST", "body": {}, "path": "data.access_token" },
122
+ "place": { "in": "header", "name": "authorization", "prefix": "Bearer " } },
123
+ "request": { "method": "GET", "path": "/api/subscription/self" } },
124
+ "calls": {
125
+ "balance": { "path": "/api/wallet/balance", "response": { "path": "data.balance" } },
126
+ "usage": { "query": { "days": 7 } }
127
+ }
128
+ }
129
+ ```
130
+
131
+ `sync` 会多生成一个 `DATASOURCE_CALLS.wallet`;代码里一次调用:
132
+
133
+ ```ts
134
+ const results = await data.requestMany(DATASOURCE.wallet, DATASOURCE_CALLS.wallet);
135
+ ```
136
+
137
+ `calls` 的键就是结果名;配方里的 `path` / `query` / `headers` / `response` 是每条的默认值,
138
+ `calls` 里的同名格覆盖它;一次最多 8 条。`calls` 必须配 `recipe`,`design check` 逐格校验。
139
+ `requestMany` 也只读 GET —— 写仍是一条一条走 `submit`。
140
+
141
+ 配方还有两个可选机器字段:`session`(这条读用哪条 opencli 会话,默认 `workbench-read`;要和屏会话分开时写)
142
+ 与 `timeoutMs`(默认 25 秒)。
143
+
144
+ `custom` 只记"为什么配方盖不住",脚本仍在代码里;`design check` 会给 warn。
145
+
146
+ 探 DOM(服务端渲染的表格)不在配方里:用 `_find` 探,固化仍走老的 `data.read({ kind: 'browser', script })`
147
+ 或 `browserFind`(DOM 读走 `find`,不写 eval 脚本)。
148
+
149
+ `opencli` 是**侦察兵,不是日常通道**:探明后,日常读必须是一条固定、便宜、可解释的调用
150
+ (配方就是那条调用;`eval` 只在探测和 `custom` 里出现)。
151
+
152
+ 配方的**执行体由壳决定,不写在模块里**:凭据来源是 `cookie` / `cookieValue` 时,壳可能用宿主侧的
153
+ 公共通道(Node fetch + 浏览器 cookie jar)而不是页面脚本;两者结果形状、错误码、三态都一样。
154
+ 模块不需要知道走的是哪条 —— 照 `data.request(DATASOURCE.<key>)` 写就对了。
155
+
156
+ ## 三态
157
+
158
+ 拿不到数据是常态。卡片必须画得出:
159
+
160
+ | 情况 | 文案 |
103
161
  |---|---|
104
- | 没登录 / 凭据过期 | "去 XX 登录一下"(可点,指到他平时那个入口) |
162
+ | 没登录 / 凭据过期 | "去 XX 登录一下"(可点,指到用户常用入口) |
105
163
  | 超时 / 网络不通 | "这次没读到,稍后重试" + 重试 |
106
- | 站点改版、字段没了 | "读到的形状和预期不一样"(别显示空白,也别显示 0) |
164
+ | 站点改版、字段缺失 | "读到的形状和预期不一样";不显示空白、不显示 0 |
107
165
 
108
- 任何一种输入(待机 / 在读 / 有值 / 有值但空 / 读不到)下抛异常、屏上冒出 `undefined` / `NaN` 的卡片 = 用户眼里的"卡片一直不出来"。这条 `check --only cards` 会拦(见 [REVIEW.md](REVIEW.md))。
166
+ 任何输入(待机 / 在读 / 有值 / 空 / 读不到)下抛异常或出现 `undefined` / `NaN` 的卡片都会让用户认为"卡片不出来"。
167
+ `check --only cards` 拦这条([REVIEW.md](REVIEW.md))。
109
168
 
110
- ## 错误码不是随便写的(阻断屏按它换文案)
169
+ ## 错误码
111
170
 
112
- 外壳的阻断屏只认下面这几个码,`helpUrl` 才是把"重试"变成"去登录"的那一步。平台自己回什么码
113
- (例:`AUTH_UNAUTHORIZED`)**别直接透传**,先映射到这张表里的一个:
171
+ 平台码(如 `AUTH_UNAUTHORIZED`)先映射,不透传:
114
172
 
115
- | 你回的 code | 用户看到 |
173
+ | code | 用户看到 |
116
174
  |---|---|
117
- | `auth-required` + `helpUrl` | "需要登录 XX" + 一颗**去登录** + 一颗**重试**(`helpUrl` 缺了就只剩重试) |
118
- | `cli-timeout` | **一条细提示**(不换整卡)+ 重试 |
119
- | `cli-exec-failed` | 整卡换成"读不到" + 重试,`hint` 写清他该去确认什么 |
120
- | `permission-denied` + `helpUrl` | "没有权限" + 打开权限页 + 重试 |
121
- | 其它 | 整卡换成你给的 `message` + 重试 |
175
+ | `auth` + `helpUrl` | "需要登录 XX" + 去登录 + 重试(无 `helpUrl` 只剩重试) |
176
+ | `timeout` | 细提示(不换整卡)+ 重试 |
177
+ | `command` / `http` / `parse` | 整卡"读不到" + 重试;`hint` 写清确认什么 |
178
+ | `browser` + `helpUrl` | "没有权限" / 页面读失败 + 权限页 + 重试 |
179
+ | 其他 | 整卡显示 `message` + 重试 |
122
180
 
123
- 三个字段各有各的位置:`message` 是标题、`hint` 是下面那句能照做的话、`detail` 是折叠起来的原始
124
- 输出 —— 写全了用户才知道"这一屏为什么长这样、下一步点哪"。
181
+ 字段位置:`message` 标题、`hint` 可照做的一句、`detail` 折叠的原始输出。
125
182
 
126
- ## 你自己的门禁会查
183
+ ## 门禁
127
184
 
128
- - 数据源写在**宿主半侧**,卡片只消费它的返回;
129
- - 只读路由出错时回 `{ ok: false, error: { code, message, hint? } }`(HTTP 仍是 200,外壳原样转发);
130
- - 验收时把「正常 / 空 / 出错」三条路都走一遍 —— 真机上造不出来的那几条,写清**怎么才能真看到**(换个空目录、换个不存在的路径那种),并在模块自己的 `plugin.test.ts` 里把这几态各喂一遍。
185
+ - 数据源在宿主半侧;卡片只消费返回。
186
+ - 只读路由错误返回 `{ ok: false, error: { code, message, hint? } }`(HTTP 200,外壳转发)。
187
+ - 验收走正常 / 空 / 出错三条路;真机造不出的状态写清如何触发,并在 `plugin.test.ts` 各喂一遍。
@@ -1,144 +1,111 @@
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[]`(多条,一条一行;每条可带 `key` + `recipe`,见
104
+ [DATASOURCE.md](DATASOURCE.md)「配方」)+ `writes[]`(写通道;单条可写 `write{}`;没有就不写)+
105
+ `acceptance[]`。`config` / `capabilities` 可为空数组;其余空 = 未定。写通道字段见 [WRITES.md](WRITES.md)。
106
+
107
+ 单态不适用时写 `na`:`{ "key": "idle", "na": "读的是账号,不随项目变" }`,不填 `sees` / `canDo` / `agentCan`。
108
+ `capabilities` 为空时 `states[].agentCan` 可省。`link` 等可选格留空渲染 `—`。
109
+
110
+ `design.json` 只装人话。模块**不声明**"在哪些工作区出现"—— 用户用 `workbench_panels_show` / `_hide` 决定
111
+ ([TOOLS.md](TOOLS.md) §12)。`contracts` 只对键名。