@dsh-workbench/core 0.3.11 → 1.0.0-beta.1

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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -117
  3. package/bin/dsh-workbench +130 -29
  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 +4632 -2002
  10. package/cordis.patch.yml +3 -3
  11. package/dist/client/client.body.js +4632 -2002
  12. package/dist/client/index.js +5306 -2676
  13. package/dist/host/index.js +6990 -5879
  14. package/dist/types/app/client.d.ts +1 -0
  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 +17 -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 +73 -0
  37. package/dist/types/app/layouts/market/use-module-versions.d.ts +1 -8
  38. package/dist/types/app/layouts/rightbar/MarketView.d.ts +1 -1
  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-busy.d.ts +0 -22
  42. package/dist/types/app/layouts/rightbar/market-change.d.ts +1 -1
  43. package/dist/types/app/layouts/rightbar/market-hint.d.ts +1 -7
  44. package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +0 -7
  45. package/dist/types/app/layouts/rightbar/market-row-icon.d.ts +1 -1
  46. package/dist/types/app/layouts/rightbar/market-row-types.d.ts +2 -2
  47. package/dist/types/app/layouts/rightbar/market-rows.d.ts +1 -1
  48. package/dist/types/app/layouts/rightbar/market-section-icon.d.ts +1 -1
  49. package/dist/types/app/layouts/rightbar/market-settle-hold.d.ts +1 -1
  50. package/dist/types/app/layouts/rightbar/market-settle.d.ts +4 -7
  51. package/dist/types/app/layouts/rightbar/market-shared-rows.d.ts +1 -1
  52. package/dist/types/app/layouts/rightbar/market-update-verdict.d.ts +1 -11
  53. package/dist/types/app/layouts/rightbar/market-view-text.d.ts +5 -1
  54. package/dist/types/app/layouts/rightbar/market-workbench-row.d.ts +1 -1
  55. package/dist/types/app/layouts/rightbar/store.d.ts +0 -12
  56. package/dist/types/app/layouts/sidebar/LongTaskDot.d.ts +0 -6
  57. package/dist/types/app/layouts/sidebar/LongTaskHover.d.ts +0 -6
  58. package/dist/types/app/layouts/sidebar/edition-boot.d.ts +1 -19
  59. package/dist/types/app/layouts/sidebar/edition.d.ts +1 -18
  60. package/dist/types/app/layouts/sidebar/use-attention.d.ts +0 -4
  61. package/dist/types/app/layouts/sidebar/workshop-preset.d.ts +2 -0
  62. package/dist/types/app/layouts/workbench-settings/AdapterSetItem.d.ts +5 -0
  63. package/dist/types/app/layouts/workbench-settings/ConfigSyncRows.d.ts +11 -0
  64. package/dist/types/app/layouts/workbench-settings/NewWorkbenchItem.d.ts +5 -0
  65. package/dist/types/app/layouts/workbench-settings/SettingsItem.d.ts +0 -9
  66. package/dist/types/app/layouts/workbench-settings/SidebarEditionItem.d.ts +0 -6
  67. package/dist/types/app/layouts/workbench-settings/WorkbenchSettingsSection.d.ts +0 -6
  68. package/dist/types/app/units/manifests.d.ts +1 -1
  69. package/dist/types/{app/units → kernel/client}/registry.d.ts +1 -1
  70. package/dist/types/kernel/contract/bundle-install.d.ts +2 -0
  71. package/dist/types/kernel/contract/config-sync.d.ts +2 -0
  72. package/dist/types/kernel/contract/diy.d.ts +1 -0
  73. package/dist/types/kernel/contract/dsh-plugins.d.ts +2 -0
  74. package/dist/types/kernel/contract/index.d.ts +5 -0
  75. package/dist/types/kernel/contract/long-tasks.d.ts +2 -0
  76. package/dist/types/kernel/contract/market-index.d.ts +2 -0
  77. package/dist/types/kernel/contract/platform.d.ts +1 -0
  78. package/dist/types/kernel/contract/preflight-wake.d.ts +2 -0
  79. package/dist/types/kernel/contract/profile-create.d.ts +2 -0
  80. package/dist/types/kernel/contract/sidebar-edition.d.ts +2 -0
  81. package/dist/types/kernel/contract/unit-manifests.d.ts +2 -0
  82. package/dist/types/{shared/api → kernel}/host/mount-units.d.ts +5 -1
  83. package/dist/types/modules/panels/client.d.ts +1 -1
  84. package/dist/types/modules/panels/model/approval.d.ts +3 -15
  85. package/dist/types/modules/panels/model/diy-changelog.d.ts +2 -0
  86. package/dist/types/modules/panels/model/diy-context.d.ts +1 -16
  87. package/dist/types/modules/panels/model/diy-page.d.ts +1 -0
  88. package/dist/types/modules/panels/model/diy-record-open.d.ts +0 -15
  89. package/dist/types/modules/panels/model/diy-record-text.d.ts +0 -2
  90. package/dist/types/modules/panels/model/diy-record.d.ts +0 -30
  91. package/dist/types/modules/panels/model/diy-tools.d.ts +1 -0
  92. package/dist/types/modules/panels/model/long-task-tools.d.ts +0 -11
  93. package/dist/types/modules/panels/model/market-dependency-update.d.ts +1 -7
  94. package/dist/types/modules/panels/model/market-dependency.d.ts +15 -10
  95. package/dist/types/modules/panels/model/market-discover-routes.d.ts +3 -0
  96. package/dist/types/modules/panels/model/market-discover.d.ts +33 -0
  97. package/dist/types/modules/panels/model/market-dispatch.d.ts +3 -3
  98. package/dist/types/modules/panels/model/market-env.d.ts +2 -3
  99. package/dist/types/modules/panels/model/market-registry.d.ts +3 -3
  100. package/dist/types/modules/panels/model/market-restart.d.ts +1 -1
  101. package/dist/types/modules/panels/model/market-route.d.ts +5 -3
  102. package/dist/types/modules/panels/model/market-sources-act.d.ts +9 -0
  103. package/dist/types/modules/panels/model/market-types.d.ts +6 -13
  104. package/dist/types/modules/panels/model/market-update-bundle.d.ts +3 -9
  105. package/dist/types/modules/panels/model/market-verify.d.ts +3 -19
  106. package/dist/types/modules/panels/model/market-workbench-update.d.ts +3 -2
  107. package/dist/types/modules/panels/model/market.d.ts +6 -5
  108. package/dist/types/modules/panels/model/plugin-actions.d.ts +2 -1
  109. package/dist/types/modules/panels/model/plugin-section.d.ts +3 -1
  110. package/dist/types/modules/panels/model/publish-preview-routes.d.ts +2 -0
  111. package/dist/types/modules/panels/unit.d.ts +7 -3
  112. package/dist/types/modules/sessions/model/attention.d.ts +0 -11
  113. package/dist/types/modules/sessions/model/session-node.d.ts +0 -10
  114. package/dist/types/shared/api/diy.d.ts +6 -4
  115. package/dist/types/shared/api/host/approval-call.d.ts +0 -9
  116. package/dist/types/shared/api/host/approval-gate.d.ts +0 -13
  117. package/dist/types/shared/api/host/bundle-routes.d.ts +1 -1
  118. package/dist/types/shared/api/host/diy-artifacts.d.ts +0 -11
  119. package/dist/types/shared/api/host/diy-changelog.d.ts +13 -0
  120. package/dist/types/shared/api/host/diy-components.d.ts +2 -3
  121. package/dist/types/shared/api/host/diy-modules.d.ts +0 -16
  122. package/dist/types/shared/api/host/diy-publish-preview.d.ts +4 -0
  123. package/dist/types/shared/api/host/diy-recon-fs.d.ts +2 -2
  124. package/dist/types/shared/api/host/diy-tokens.d.ts +8 -0
  125. package/dist/types/shared/api/host/host-routes.d.ts +3 -6
  126. package/dist/types/shared/api/host/long-task-notice.d.ts +0 -20
  127. package/dist/types/shared/api/host/long-tasks.d.ts +1 -8
  128. package/dist/types/shared/api/host/market-doc.d.ts +21 -0
  129. package/dist/types/shared/api/host/market-index-fetch.d.ts +37 -0
  130. package/dist/types/shared/api/host/market-source-init.d.ts +22 -0
  131. package/dist/types/shared/api/host/market-sources-store.d.ts +15 -0
  132. package/dist/types/shared/api/host/permission-mode.d.ts +0 -5
  133. package/dist/types/shared/api/host/post-channels.d.ts +1 -1
  134. package/dist/types/shared/api/host/sidebar-edition.d.ts +1 -33
  135. package/dist/types/shared/api/host/unit-roster-facts.d.ts +1 -1
  136. package/dist/types/shared/api/host/workspace-root.d.ts +0 -7
  137. package/dist/types/shared/api/platform/client.d.ts +10 -0
  138. package/dist/types/shared/api/unit-board.d.ts +0 -16
  139. package/dist/types/shared/api/write-outcome.d.ts +0 -12
  140. package/dist/types/shared/components/IconModules.d.ts +0 -6
  141. package/dist/types/shared/components/PanelFoot.d.ts +0 -9
  142. package/dist/types/shared/components/blocker-action.d.ts +0 -13
  143. package/dist/types/shared/dev-surface/index.d.ts +5 -0
  144. package/dist/types/shared/dev-surface/types.d.ts +79 -0
  145. package/dist/types/shared/hooks/bundle-queries.d.ts +1 -13
  146. package/dist/types/shared/hooks/fact-transport.d.ts +4 -0
  147. package/dist/types/shared/hooks/fact-transports.d.ts +0 -4
  148. package/dist/types/shared/hooks/market-queries.d.ts +11 -0
  149. package/dist/types/shared/hooks/panel-state-report.d.ts +0 -12
  150. package/dist/types/shared/hooks/sidebar-edition.d.ts +1 -6
  151. package/dist/types/shared/hooks/use-blocker-actions.d.ts +0 -8
  152. package/dist/types/shared/hooks/use-capabilities.d.ts +0 -59
  153. package/dist/types/shared/hooks/use-diy-debug.d.ts +0 -13
  154. package/dist/types/shared/hooks/use-diy-record.d.ts +0 -13
  155. package/dist/types/shared/hooks/use-shell-long-tasks.d.ts +1 -7
  156. package/icon.svg +17 -28
  157. package/package.json +33 -13
  158. package/preset/diy.patch.yml +1 -1
  159. package/profile/isolation.patch.yml +8 -8
  160. package/profile/sandbox.patch.yml +2 -2
  161. package/scripts/clone-profile.mjs +268 -0
  162. package/scripts/create-workbench.mjs +166 -0
  163. package/scripts/seed-profile.sh +22 -10
  164. package/scripts/seed-workshop.sh +17 -19
  165. package/skills/workbench-module-diy/BUILD.md +4 -11
  166. package/skills/workbench-module-diy/COPY.md +1 -1
  167. package/skills/workbench-module-diy/DATASOURCE.md +107 -22
  168. package/skills/workbench-module-diy/DESIGN.md +2 -1
  169. package/skills/workbench-module-diy/FAMILY.md +48 -68
  170. package/skills/workbench-module-diy/KIT.md +6 -3
  171. package/skills/workbench-module-diy/LINKAGE.md +1 -1
  172. package/skills/workbench-module-diy/PANEL.md +5 -3
  173. package/skills/workbench-module-diy/REVIEW.md +7 -7
  174. package/skills/workbench-module-diy/SKILL.md +20 -11
  175. package/skills/workbench-module-diy/STEPS.md +1 -1
  176. package/skills/workbench-module-diy/TOOLS.md +33 -9
  177. package/skills/workbench-module-diy/WIRING.md +18 -6
  178. package/skills/workbench-module-diy/WRITES.md +30 -4
  179. package/dist/types/shared/api/bundle-install.d.ts +0 -136
  180. package/dist/types/shared/api/config-sync.d.ts +0 -35
  181. package/dist/types/shared/api/dsh-plugins.d.ts +0 -11
  182. package/dist/types/shared/api/host/bundle-manager.d.ts +0 -11
  183. package/dist/types/shared/api/host/config-sync.d.ts +0 -46
  184. package/dist/types/shared/api/host/diy-preset-health.d.ts +0 -35
  185. package/dist/types/shared/api/host/dsh-plugins.d.ts +0 -5
  186. package/dist/types/shared/api/host/installed-module.d.ts +0 -54
  187. package/dist/types/shared/api/host/installed-module.test.d.ts +0 -1
  188. package/dist/types/shared/api/host/opencli-command.d.ts +0 -10
  189. package/dist/types/shared/api/host/opencli-command.test.d.ts +0 -1
  190. package/dist/types/shared/api/host/opencli.test.d.ts +0 -1
  191. package/dist/types/shared/api/host/package-content.d.ts +0 -6
  192. package/dist/types/shared/api/host/package-content.test.d.ts +0 -1
  193. package/dist/types/shared/api/host/package-kind.d.ts +0 -21
  194. package/dist/types/shared/api/host/package-kind.test.d.ts +0 -1
  195. package/dist/types/shared/api/host/package-route.d.ts +0 -23
  196. package/dist/types/shared/api/host/package-route.test.d.ts +0 -1
  197. package/dist/types/shared/api/host/plugin-catalog.d.ts +0 -65
  198. package/dist/types/shared/api/host/plugin-login-hint.d.ts +0 -2
  199. package/dist/types/shared/api/host/plugin-tools.d.ts +0 -14
  200. package/dist/types/shared/api/host/plugin-units.d.ts +0 -48
  201. package/dist/types/shared/api/host/preflight-route.d.ts +0 -15
  202. package/dist/types/shared/api/host/preflight.d.ts +0 -75
  203. package/dist/types/shared/api/host/probe-cache.d.ts +0 -6
  204. package/dist/types/shared/api/host/probe-libs.d.ts +0 -11
  205. package/dist/types/shared/api/host/probe-units.d.ts +0 -17
  206. package/dist/types/shared/api/host/probe-verdict.d.ts +0 -7
  207. package/dist/types/shared/api/host/profile-registries.d.ts +0 -21
  208. package/dist/types/shared/api/host/registry-declaration.d.ts +0 -23
  209. package/dist/types/shared/api/host/release.d.ts +0 -24
  210. package/dist/types/shared/api/host/runtime-seed.d.ts +0 -40
  211. package/dist/types/shared/api/host/self-update.d.ts +0 -52
  212. package/dist/types/shared/api/host/version-range.d.ts +0 -2
  213. package/dist/types/shared/api/long-tasks.d.ts +0 -19
  214. package/dist/types/shared/api/sidebar-edition.d.ts +0 -14
  215. package/dist/types/shared/api/unit-manifests.d.ts +0 -6
  216. package/docs/HANDOFF.md +0 -333
  217. package/docs/OPERATIONS.md +0 -240
  218. package/skills/workbench-module-diy/TOOLING.md +0 -38
  219. /package/dist/types/{app/layouts/rightbar/registry.d.ts → kernel/client/board-registry.d.ts} +0 -0
  220. /package/dist/types/{app/layouts/rightbar/registry.test.d.ts → kernel/contract/contracts.test.d.ts} +0 -0
  221. /package/dist/types/{shared/api/host/bundle-manager.test.d.ts → kernel/contract/index.test.d.ts} +0 -0
  222. /package/dist/types/{shared/api → kernel}/host/unit-readiness.d.ts +0 -0
  223. /package/dist/types/{shared/api → kernel}/host/unit-registry.d.ts +0 -0
  224. /package/dist/types/shared/{api/host/dsh-plugins.test.d.ts → dev-surface/install.d.ts} +0 -0
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # **设置不在这里继承**(这里曾经把 web 的 cordis.patch.yml 快照一份过来):
8
8
  # 工作台自己的 patch 是唯一真相,想搬 web 的哪几行由用户走「设置 → 配置同步」。
9
- # 为什么这么分(安装 vs 设置)、为什么不再是自动的:.scratch/config-sync/spec.md
9
+ # 为什么这么分(安装 vs 设置)、为什么不再是自动的:内部设计记录
10
10
  #
11
11
  # 幂等 / 可续跑:profile 已存在就跳过创建,隔离层已存在就跳过追加
12
12
  # —— 重复运行不会覆盖任何东西。
@@ -23,8 +23,11 @@ set -euo pipefail
23
23
 
24
24
  DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
25
25
  PROFILE="${WORKBENCH_PROFILE:-workbench}"
26
- SRC_PROFILE="$DSH_HOME/profiles/web"
26
+ SRC_PROFILE_NAME="${WORKBENCH_SOURCE_PROFILE:-web}"
27
+ SRC_PROFILE="$DSH_HOME/profiles/$SRC_PROFILE_NAME"
27
28
  DST_PROFILE="$DSH_HOME/profiles/$PROFILE"
29
+ WORKBENCH_HOME="${DSH_WORKBENCH_HOME:-$DSH_HOME/$PROFILE}"
30
+ export DSH_WORKBENCH_HOME="$WORKBENCH_HOME"
28
31
  PKG='@dsh-workbench/core'
29
32
  REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
30
33
  GIT_TOP="$(git -C "$REPO_DIR" rev-parse --show-toplevel 2>/dev/null || true)"
@@ -93,8 +96,10 @@ if [ "${1:-}" = "ensure-selected" ]; then
93
96
  exit 0
94
97
  fi
95
98
 
96
- [ -f "$SRC_PROFILE/package.json" ] ||
97
- die "找不到 $SRC_PROFILE —— 请先跑一次 'dsh web',让 web profile 落地。"
99
+ HAS_SOURCE=0
100
+ if [ -f "$SRC_PROFILE/package.json" ]; then
101
+ HAS_SOURCE=1
102
+ fi
98
103
  [ -f "$ISOLATION" ] || die "找不到 $ISOLATION"
99
104
 
100
105
  DST_PATCH="$DST_PROFILE/cordis.patch.yml"
@@ -107,8 +112,8 @@ DST_PATCH="$DST_PROFILE/cordis.patch.yml"
107
112
  if [ -f "$DST_PROFILE/package.json" ]; then
108
113
  echo "→ profile '$PROFILE' 已存在,跳过创建"
109
114
  else
110
- echo "→ 创建 profile '$PROFILE'(模板: web)"
111
- dsh "$PROFILE" --from-default-profile web --dump-config >/dev/null
115
+ echo "→ 创建 profile '$PROFILE'(模板: ${SRC_PROFILE_NAME})"
116
+ dsh "$PROFILE" --from-default-profile "$SRC_PROFILE_NAME" --dump-config >/dev/null
112
117
  fi
113
118
 
114
119
  [ -f "$DST_PATCH" ] || die "profile 缺少 $DST_PATCH"
@@ -119,8 +124,10 @@ fi
119
124
  # 改过(例如启用了某个可选 bundle)。所以这里把 web 清单里的 bundles
120
125
  # 并进来,同时保留 workbench 自己已有的额外项(例如 @dsh-workbench/core),
121
126
  # 使脚本可重复运行。
127
+ if [ "$HAS_SOURCE" = "1" ]; then
122
128
  WB_MANIFEST="$DST_PROFILE/package.json" \
123
129
  WEB_MANIFEST="$SRC_PROFILE/package.json" \
130
+ WB_SOURCE_NAME="$SRC_PROFILE_NAME" \
124
131
  node -e '
125
132
  const fs = require("node:fs");
126
133
  const read = (p) => JSON.parse(fs.readFileSync(p, "utf8"));
@@ -137,15 +144,18 @@ wb.dsh = Object.assign({}, wb.dsh, {
137
144
  profile: Object.assign({}, webProfile, wbProfile, { bundles: merged }),
138
145
  });
139
146
  fs.writeFileSync(process.env.WB_MANIFEST, JSON.stringify(wb, null, 2) + "\n");
140
- console.log("→ 已继承 web 的 bundle 列表: " + merged.join(", "));
147
+ console.log("→ 已继承 " + process.env.WB_SOURCE_NAME + " 的 bundle 列表: " + merged.join(", "));
141
148
  '
149
+ else
150
+ echo "→ 本机还没有来源 profile '$SRC_PROFILE_NAME';用 dsh 出厂模板的 bundle 列表(如需继承它的设置/插件,先跑一次来源 profile)"
151
+ fi
142
152
 
143
153
  # ── 第 2.5 步:修掉旧版启动器留下的"装出来的那份被当成仓库副本" ──────────
144
154
  #
145
155
  # 旧判据只问 `git rev-parse --show-toplevel`("**某个祖先**是不是仓库"),于是包目录落在任何
146
156
  # 仓库里的机器(最常见:`$HOME` 是 dotfiles 仓库,而 npm/nvm 的前缀就在 `$HOME` 下)在 setup
147
- # 时会把**全局安装目录**以 `link:` 装进 profile —— 市场里工作台那一行因此说「这份是本仓正在
148
- # 开发的那一份,不从源上取。」(不比对源、不提示更新),而 `npm i -g …@latest` 还会悄悄换掉
157
+ # 时会把**全局安装目录**以 `link:` 装进 profile —— 市场里工作台那一行因此打上「本地」标
158
+ # (不比对源、不提示更新),而 `npm i -g …@latest` 还会悄悄换掉
149
159
  # profile 跑的东西。判据窄到只认那一个坏形状:**我自己是装出来的那份**(GIT_TOP 为空)
150
160
  # **且 profile 里那条 `link:` 指着的正是我自己这份目录** —— 指别处的 link:(用户自己链的)不动。
151
161
  if [ -z "$GIT_TOP" ] && [ -f "$DST_PROFILE/package.json" ]; then
@@ -184,6 +194,7 @@ else
184
194
  printf '\n' >>"$DST_PATCH"
185
195
  cat "$ISOLATION" >>"$DST_PATCH"
186
196
  echo "→ 已追加隔离层(sessions / storages / attachments 分家)"
197
+ rm -f "$DST_PATCH.tmp"
187
198
  fi
188
199
 
189
200
  # ── 第 4 步:建 DIY 工坊 ────────────────────────────────────────────────
@@ -206,7 +217,8 @@ cat <<EOF
206
217
 
207
218
  profile '$DST_PROFILE' 的骨架好了(bundle 清单 + 会话/存储/附件隔离层)。
208
219
 
209
- 设置**不在**这里继承:想要 web 那边的模型供应商 / 搜索源 / MCP,起工作台后去
220
+ 设置与本地插件按创建时的勾选从 $SRC_PROFILE_NAME 继承;直接跑 setup 时只继承
221
+ bundle 清单,想要 web 那边的模型供应商 / 搜索源 / MCP,起工作台后去
210
222
  「设置 → 配置同步」按行勾(读的是那个 profile 的 cordis.patch.yml;搬完重启生效)。
211
223
 
212
224
  装 bundle —— 两条**等价**的路,任选一条(都走 dsh 的 runPluginCommand,装完会
@@ -20,6 +20,8 @@
20
20
  set -euo pipefail
21
21
 
22
22
  DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
23
+ WORKBENCH_HOME="${DSH_WORKBENCH_HOME:-$DSH_HOME/workbench}"
24
+ export DSH_WORKBENCH_HOME="$WORKBENCH_HOME"
23
25
  SELF="${BASH_SOURCE[0]}"
24
26
  while [ -L "$SELF" ]; do
25
27
  SELF="$(cd "$(dirname "$SELF")" && pwd -P)/$(readlink "$SELF")"
@@ -36,8 +38,8 @@ MODE="${1:-skeleton}"
36
38
  # 仓库副本,去 link 一个不存在的 `../module-tools`,脚本当场死在 `cd` 上 —— 而它是**后台**
37
39
  # 被拉起来的(宿主自举 / 启动器),症状就是"工坊怎么都没建起来,一声不响"。
38
40
  #
39
- # 所以认仓库要认**本仓的形状**:根上同时有 packages/workbench 与 packages/module-tools。
40
- if [ -n "$GIT_TOP" ] && [ -f "$GIT_TOP/packages/workbench/package.json" ] &&
41
+ # 所以认仓库要认**本仓的形状**:根上同时有 packages/core 与 packages/module-tools。
42
+ if [ -n "$GIT_TOP" ] && [ -f "$GIT_TOP/packages/core/package.json" ] &&
41
43
  [ -f "$GIT_TOP/packages/module-tools/package.json" ]; then
42
44
  IN_REPO=1
43
45
  else
@@ -71,8 +73,8 @@ fi
71
73
  # 冻结接口:状态文件 $DSH_HOME/workbench/diy/.sync.json =
72
74
  # { state: "ok"|"syncing"|"failed", version: <基准版本>, at: <ISO>, reason?: <一句话> }
73
75
  SHELL_ROOT="${DSH_WB_SHELL_ROOT:-}"
74
- DIY_DIR="$DSH_HOME/workbench/diy"
75
- SEED_LOG="$DSH_HOME/workbench/seed.log"
76
+ DIY_DIR="$WORKBENCH_HOME/diy"
77
+ SEED_LOG="$WORKBENCH_HOME/seed.log"
76
78
  SYNC_STATE="$DIY_DIR/.sync.json"
77
79
  SYNC_LOCK="$DIY_DIR/.sync.lock"
78
80
  SYNC_BACKOFF_SECONDS=3600
@@ -103,7 +105,7 @@ target_version() {
103
105
  }
104
106
 
105
107
  # 外壳 manifest 里钉的那份 dsh UI 版本(模块模板默认带的那个 devDependency,见
106
- # `.scratch/dsh-primitives-face/spec.md` §3.1)。源优先**基准**(正在跑的那份外壳),
108
+ # `内部设计记录` §3.1)。源优先**基准**(正在跑的那份外壳),
107
109
  # 没有才退回跑脚本这份副本 —— 与 link_workshop_skills 同一条口径。
108
110
  # 读不到就留空:调用方按"不写这一项"处理(模块 `init` 那边有自己的兜底常量与一句人话)。
109
111
  shell_primitives_version() {
@@ -143,7 +145,7 @@ compute_specs() {
143
145
  SHARED_SPEC="^$version"
144
146
  fi
145
147
  # dsh 原语(模块模板默认带的那个):**精确版**,与模块自己那份 devDependency 对齐 ——
146
- # 类型面要对齐对账过的那一版(`.scratch/dsh-primitives-face/spec.md` §3.1)。
148
+ # 类型面要对齐对账过的那一版(`内部设计记录` §3.1)。
147
149
  # 空 = 读不到外壳 manifest,就**不写这一项**(模块 `init` 有自己的兜底常量)。
148
150
  local primitives_version
149
151
  primitives_version="$(shell_primitives_version)"
@@ -286,9 +288,7 @@ sync_lock_stale() {
286
288
 
287
289
  # 强行清锁:只给"判定持锁者已崩"那条路用(那时锁已经不是原主的了)。
288
290
  drop_sync_lock() {
289
- case "${SYNC_LOCK:-}" in
290
- */workbench/diy/.sync.lock) rm -rf "$SYNC_LOCK" ;;
291
- esac
291
+ if [ "${SYNC_LOCK:-}" = "$WORKBENCH_HOME/diy/.sync.lock" ]; then rm -rf "$SYNC_LOCK"; fi
292
292
  }
293
293
 
294
294
  # 抢锁的返回值:0 = 抢到;1 = 同一代的人正装着(跳过);2 = 这地方建不出锁;3 = 另一代正装着、等过一轮没抢到。
@@ -346,13 +346,11 @@ stop_sync_heartbeat() {
346
346
 
347
347
  # 只删**自己的**锁:锁被清过 / 换了主人时,这里必须放手(否则新持锁者的 install 被砍在半路)。
348
348
  release_sync_lock() {
349
- case "${SYNC_LOCK:-}" in
350
- */workbench/diy/.sync.lock)
351
- if [ -n "${SYNC_LOCK_TOKEN:-}" ] && [ "$(lock_field owner)" = "$SYNC_LOCK_TOKEN" ]; then
352
- rm -rf "$SYNC_LOCK"
353
- fi
354
- ;;
355
- esac
349
+ if [ "${SYNC_LOCK:-}" = "$WORKBENCH_HOME/diy/.sync.lock" ]; then
350
+ if [ -n "${SYNC_LOCK_TOKEN:-}" ] && [ "$(lock_field owner)" = "$SYNC_LOCK_TOKEN" ]; then
351
+ rm -rf "$SYNC_LOCK"
352
+ fi
353
+ fi
356
354
  }
357
355
 
358
356
  # sync:幂等、可重跑、可续。**复核过才写 ok**。
@@ -529,15 +527,15 @@ fi
529
527
  # 转发脚本而不是软链:同 `bin/opencli-isolated` 那条坑 —— pnpm publish 之后包里的脚本没有 x 位,
530
528
  # 软链过去一执行就是 Permission denied。x 位由 chmod 当场给。
531
529
  # ⚠️ 先 rm -f:旧版本留下的软链还在时,`>` 会顺着软链把真脚本覆盖掉。
532
- ENTRY_DIR="$DSH_HOME/workbench/bin"
530
+ ENTRY_DIR="$WORKBENCH_HOME/bin"
533
531
  mkdir -p "$ENTRY_DIR"
534
532
  rm -f "$ENTRY_DIR/dsh-wb-module"
535
533
  cat >"$ENTRY_DIR/dsh-wb-module" <<EOF
536
534
  #!/bin/sh
537
- BIN="$DSH_HOME/workbench/diy/node_modules/.bin/dsh-wb-module"
535
+ BIN="$WORKBENCH_HOME/diy/node_modules/.bin/dsh-wb-module"
538
536
  if [ -x "\$BIN" ]; then exec "\$BIN" "\$@"; fi
539
537
  if command -v pnpm >/dev/null 2>&1; then
540
- exec pnpm --dir "$DSH_HOME/workbench/diy" exec dsh-wb-module "\$@"
538
+ exec pnpm --dir "$WORKBENCH_HOME/diy" exec dsh-wb-module "\$@"
541
539
  fi
542
540
  echo "dsh-wb-module: 工坊的依赖还没装 —— 先跑 dsh-workbench setup" >&2
543
541
  exit 127
@@ -60,17 +60,9 @@ pnpm exec dsh-wb-module init <unit> --dir <工作区>/packages/module-<unit> --t
60
60
 
61
61
  ## 3 命令
62
62
 
63
- ```bash
64
- dsh-wb-module build # 生成 client.js 与 dist/host.js
65
- dsh-wb-module check # 十三条判据
66
- dsh-wb-module check --only cards # 单条(contracts / protocol / bundles / source / arch / lint /
67
- # primitives / kit / cards / standalone / markers / style / alias)
68
- dsh-wb-module check --only primitives [--all|--json] # 查询 dsh 原语
69
- dsh-wb-module lint [--fix] # 模块 eslint;--fix 写回
70
- ```
71
-
72
- 用 `pnpm exec dsh-wb-module …`。`package.json` 六个脚本(`build` / `typecheck` / `test` / `selfcheck` /
73
- `check` / `lint`)必须保留,`contracts` 点名缺失。
63
+ 用 `pnpm exec dsh-wb-module …`;命令、`--only` 判据名与模板参数看 `--help` / `check --list`,不在指引里抄。
64
+ `package.json` 六个脚本(`build` / `typecheck` / `test` / `selfcheck` / `check` / `lint`)必须保留,
65
+ `contracts` 点名缺失。
74
66
 
75
67
  - `check` 不跑 `pnpm test`;状态、换算、边界测试自己跑。
76
68
  - "不适用"不是"通过":`standalone` 需外壳源码在场,`markers` 只扫公开包(DIY 模块 `private`)。
@@ -94,6 +86,7 @@ export function apply(ctx) {
94
86
  }
95
87
  ```
96
88
 
89
+ `mount({ data })` 的 `data` 是统一数据端口(`DataReader`);读数据用 `data.read(...)`,不要自己 import adapter。
97
90
  `mount()` 返回 `{ routes, registerTools, prefs? }`。`routes` 挂 `/workbench/api/<路由>`,只许 GET,
98
91
  键必须在 `<unit>` 或 `<unit>/…` 之下(否则装配抛错)。错误返回
99
92
  `{ ok: false, error: { code, message, hint? } }`(HTTP 200,外壳转发)。`registerTools` 提供模块工具。
@@ -86,4 +86,4 @@
86
86
  | 描述 | 用于查看构建状态的工具 | 看某个环境最近一次构建:状态、耗时、提交;用户说“发布没成功”就先调它。 |
87
87
  | 返回 | 操作成功 | 已发布 v1.2.3 到生产;下一步 `_build` 看结果。 |
88
88
  | 拒绝 | 不允许 | 这个环境是生产;先在确认条上点「发布到生产」。 |
89
- | 日志 | 出错了 | `[cmdb] release v1.2.3 failed: HTTP 502 (retryable)` |
89
+ | 日志 | 出错了 | `[inventory] release v1.2.3 failed: HTTP 502 (retryable)` |
@@ -6,14 +6,14 @@
6
6
 
7
7
  | 情况 | 域 |
8
8
  |---|---|
9
- | 用户已在浏览器登录过该系统 | DIY:用 opencli 复用登录探(下文四步) |
9
+ | 用户已在浏览器登录过该系统 | DIY:用 opencli 复用登录探(下文五步) |
10
10
  | 要新凭据(账号 / 密钥 / token) | 工程:停,说清谁配什么 |
11
11
 
12
12
  判据是"要不要新凭据",不是"要不要接新平台"。其余类型见 [DESIGN.md](DESIGN.md) §0。
13
13
 
14
- ## 四步
14
+ ## 五步
15
15
 
16
- 1. **问入口** —— "你平时在哪个网页 / 软件里看这个?"记下入口。不问"用 argocd 还是 jenkins"。
16
+ 1. **问入口** —— "你平时在哪个网页 / 软件里看这个?"记下入口。不问"用 deploy 还是 jenkins"。
17
17
  把用户的需求翻成站点 / 系统关键词是你的工作。
18
18
 
19
19
  2. **按四层找数据**:
@@ -29,7 +29,7 @@
29
29
  `workbench_diy_spec`(`command='<site> <name>'`)取 `--help -f json` 原文(插件与内置均可)。
30
30
 
31
31
  ③ **官方有、这台机器没有** → 安装:`opencli plugin install github:user/repo`、`opencli external install <名>`,
32
- 或模块清单 `manifest.env.plugins`。安装改这台机器且执行第三方代码 → 需用户审批;优先内网或官方源。
32
+ 或模块清单 `needs` 里 `kind: "extension"` 的那些。安装改这台机器且执行第三方代码 → 需用户审批;优先内网或官方源。
33
33
 
34
34
  ④ **都没有** → 开发:一次性探用下文的 eval;长期用写 adapter(`opencli skills read opencli-adapter-author`,
35
35
  失效用 `opencli-autofix`)。
@@ -39,6 +39,9 @@
39
39
  两拍、重启重试、平台码映射都在工具内。目标:找到承载数据的位置(列表页或其后的 XHR),
40
40
  记 URL、参数、凭据来源。
41
41
 
42
+ **Desktop**:这条 `read=` 探索线照旧;Desktop 只是不能对 Electron 里的工作台页做屏调试,
43
+ 不影响站点探索。录制(`_record_*`)也照旧。
44
+
42
45
  - 不知道数据在哪:先跑 `performance.getEntriesByType('resource')`,筛 `/api/`、`.json`。
43
46
  有 → 接口读;无但页面有整块表格 → DOM 读。判断一次即可。
44
47
  - `opencli` 随工作台预装(隔离运行时 `$DSH_HOME/workbench/opencli/`,启动体检安装,不碰 `~/.opencli`)。
@@ -48,25 +51,107 @@
48
51
  - 新 session 的 `eval` 会静默开 `about:blank`(后台容器窗,不抢焦点)。脚本第一行判断
49
52
  `location.origin`;不在站点则 `open` 该页,等约 1 秒再 eval。两拍都用 shared 的 `backgroundLine`
50
53
  拼(前缀 `env OPENCLI_WINDOW=background`),不要直接拼 `opencli browser …`。
51
- - 读与屏用不同会话:`browserRead` / `browserFind` 默认 `workbench-read`;`workbench` 是看 / 动族的
52
- 屏会话。同名会把用户的页导航走。需要独立标签就传 `session: "<unit>"`。
53
- - DOM 读不要在 `eval` 里 `querySelectorAll`:宿主产物禁止 `document.`,且无法单测。探时用
54
- `workbench_diy_find`(`read=…, args=["--css", "<选择器>"]`);固化用 `browserFind`
55
- (`find --css … --limit N --text-max M`,返回 `{ entries: [{ text, tag, nth }] }`;
56
- 一行单元格按换行拼在 `text` 内,"切格 + 按列取值"做成两个纯函数)。
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,把"切格 + 按列取值"做成两个纯函数。
57
58
  - 工作台刚重启时桥未就绪:首次读报"命令跑不起来",等一秒重读;超时不重试。
58
59
 
59
60
  3. **试一条最小读** —— 先证明能拿到,再写模块。记录:结构化字段形状;未登录长什么样;超时 / 被墙的形状。
60
61
  产出必须是一条可重复的命令或请求。
61
62
 
62
- 4. **固化进模块** —— 先写入 `design.json`(单读 `datasource{}`;多读 `reads[]` 一条一行;字段:
63
- 哪条读 / 参数 / 凭据 / 没登录 / 超时 / 当时调用原文),再写宿主半侧:用 `@dsh-workbench/shared/host` 的
64
- `browserRead({ shell, script, url, loginUrl })`(接口读;两拍、重试、错误码映射、`message` / `hint` /
65
- `detail` / `helpUrl` 内置;不传 `session` 用读会话)或 `browserFind({ shell, css, url, session })`(DOM 读;
66
- 自动判断当前页、必要时 `open`),或一个只读 HTTP GET。卡片只依赖这一条。
67
- 写操作走 [WRITES.md](WRITES.md)。
68
-
69
- `opencli` 是**侦察兵,不是日常通道**:探明后,日常读必须是一条固定、便宜、可解释的调用。
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>)` 写就对了。
70
155
 
71
156
  ## 三态
72
157
 
@@ -87,10 +172,10 @@
87
172
 
88
173
  | code | 用户看到 |
89
174
  |---|---|
90
- | `auth-required` + `helpUrl` | "需要登录 XX" + 去登录 + 重试(无 `helpUrl` 只剩重试) |
91
- | `cli-timeout` | 细提示(不换整卡)+ 重试 |
92
- | `cli-exec-failed` | 整卡"读不到" + 重试;`hint` 写清确认什么 |
93
- | `permission-denied` + `helpUrl` | "没有权限" + 权限页 + 重试 |
175
+ | `auth` + `helpUrl` | "需要登录 XX" + 去登录 + 重试(无 `helpUrl` 只剩重试) |
176
+ | `timeout` | 细提示(不换整卡)+ 重试 |
177
+ | `command` / `http` / `parse` | 整卡"读不到" + 重试;`hint` 写清确认什么 |
178
+ | `browser` + `helpUrl` | "没有权限" / 页面读失败 + 权限页 + 重试 |
94
179
  | 其他 | 整卡显示 `message` + 重试 |
95
180
 
96
181
  字段位置:`message` 标题、`hint` 可照做的一句、`detail` 折叠的原始输出。
@@ -100,7 +100,8 @@ pnpm exec dsh-wb-module design accept # 逐条走通后打勾
100
100
 
101
101
  字段:`unit` / `name` / `oneLine` + `panel[]`(这一格 / 结论 / 去哪)+ `states[]`(五态必填,key 固定
102
102
  `idle|loading|ready|empty|unreadable`)+ `config[]` + `capabilities[]` + `linkage[]` +
103
- `datasource{}`(一条读)或 `reads[]`(多条,一条一行)+ `writes[]`(写通道;单条可写 `write{}`;没有就不写)+
103
+ `datasource{}`(一条读)或 `reads[]`(多条,一条一行;每条可带 `key` + `recipe`,见
104
+ [DATASOURCE.md](DATASOURCE.md)「配方」)+ `writes[]`(写通道;单条可写 `write{}`;没有就不写)+
104
105
  `acceptance[]`。`config` / `capabilities` 可为空数组;其余空 = 未定。写通道字段见 [WRITES.md](WRITES.md)。
105
106
 
106
107
  单态不适用时写 `na`:`{ "key": "idle", "na": "读的是账号,不随项目变" }`,不填 `sees` / `canDo` / `agentCan`。
@@ -1,72 +1,52 @@
1
- # DIY 工具族:参数与返回
1
+ # DIY 工具族:怎么选、怎么串
2
2
 
3
- 本文件是 agent 在 DIY 会话里实际拿到的工具契约。[TOOLS.md](TOOLS.md) 讲模块自身能力。
3
+ 只写工具定义里没有的东西:什么时候用哪把、先后顺序、参数说明之外的坑。
4
+ 逐把的参数与返回以调用时拿到的工具定义为准;模块自身的能力见 [TOOLS.md](TOOLS.md)。
4
5
 
5
- ## 1 前置
6
+ ## 1 选哪把
6
7
 
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
- | 工具 | 契约 |
8
+ | 要做什么 | 用 |
65
9
  |---|---|
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 |
70
-
71
- 左栏键(`[取消]` / `[停]`)只发消息,不改页面;录完无键。场次 TTL 15 分钟;过期或台账变更会停捕获、收标签。
72
- 流程与写纪律见 [WRITES.md](WRITES.md) 步骤 3。
10
+ | 认路、装没装、起不起得来 | `_status`(`unit` 可省);它的「下一步」就是下一件事 |
11
+ | 卡片空白 / 崩了 / 读不到 | `_panel` |
12
+ | 找 opencli 命令、看命令形状 | `_recon` → `_spec`(`--help -f json` 原样) |
13
+ | kit 有什么 / 令牌叫什么 | `_components`(`bucket` / `name` 取声明;`scope: tokens` 现读 `--dsw-*` / `--wb-*`) |
14
+ | 装一次 / 只上板 | `_install` / `_show` |
15
+ | 发布前记这一版改了什么 | `_changelog`(`note` 必填;文件不在就先建) |
16
+ | 发布 | `_publish`,只在用户说「帮我发布」之后 |
17
+ | 看 / 动当前页 | `_state` · `_html` · `_find` · `_screenshot` · `_click` · `_hover` · `_type` · `_fill` · `_keys` · `_scroll` · `_wait` · `_network` · `_dialog` |
18
+ | 控制台 | `_logs_watch` → 操作 → `_logs_read` → `_logs_stop` |
19
+ | 抓「用户点了之后发了什么」 | `_record_prepare` → 用户操作 → `_record_read` → `_record_detail` → `_record_stop` |
20
+
21
+ ## 2 前置
22
+
23
+ - 只在 DIY 模式的会话出现;面板得画在屏上。
24
+ - 看 / 动 / 网络 / 日志族先 `_debug_start`(审批一次),否则整族拒、零命令;收工 `_debug_stop`。
25
+ `_pause` 只暂停看屏。不碰屏的:`_status` / `_panel` / `_components` / `_recon` / `_spec` /
26
+ `_install` / `_show` / `_changelog` / `_publish`;`_find` / `_eval` 带 `read=` 时也不碰。
27
+ - 录制独立:`_record_prepare` 审批一次,其余不问;同时只有一场,要重来先 `_record_stop`。
28
+
29
+ ## 3 参数之外的坑
30
+
31
+ - 浏览器工具都能带 `args` 原样转发;空串 / 纯空白 / 非字符串会被丢弃,词前后 trim ——
32
+ `_fill` 清不空、`_type` 打不了纯空格。
33
+ - `ok` 只表示进程退出。事实在 `stdout`;错误体是 `{"error":{…}}`,`ok` 仍可能是 true。
34
+ 不解析、不翻译;参数不明先 `args: ['<verb>', '--help']`。
35
+ - 卡作用域:`unit` 指卡;`scope: 'card'` / `in: 'card'` / `$card` 需要它,
36
+ `$card` 在 args 里换成 `#wb-card-<unit> .wb-card`。
37
+ - 只读线:带 `read: <地址>` 时跑在 `workbench-read` 会话,不碰屏、不认卡作用域、能代开站点;
38
+ `_eval` 返回 `not-logged-in` 时让用户去登录。
39
+ - `_screenshot` 不给路径时写 artifacts 目录;拿不到选择器就先 `_eval` 量盒再 `clip`。
40
+ - `state` 没有 `scope`;`_select` 不存在(下拉两拍);ref 只在一次快照内有效;
41
+ `_dialog` 不校验 `accept|dismiss`;`_network --detail` 没有 body,要 body 用 `_eval` 的 fetch。
42
+ - 录制按 URL 去静态资源后落盘;`_record_read` 不回体,取体用 `_record_detail`;
43
+ `_record_stop` 先读文件再另存 `.last`,所以先 read / detail 再 stop。取不到时给 DevTools 复制 curl。
44
+ 场次 15 分钟过期;过期或台账变更会停捕获、收标签。
45
+
46
+ ## 4 发布
47
+
48
+ 只在用户明确说「帮我发布」后调。它一次做完预检 → 全门禁 → 按出口发布 → 发后核包,审批一次。
49
+ 发布前先 `_changelog` 把这一版写进 `CHANGELOG.md`:没有这一版的记录,`_publish` 会拦下。
50
+ 审批屏会把这一版的更新日志按 markdown 展示给用户。
51
+ 需要用户接管时返回 `needsUser: { step, text, command? }` 而不抛异常:把 `text` 说给用户、给命令,
52
+ 等「好了」再跑;不向用户要 token,不展示 `package.json`。
@@ -104,12 +104,14 @@ h(Form<{ branch: string }>, {
104
104
 
105
105
  ```tsx
106
106
  h(FormRows, null,
107
- h(FormRow, { label: t('myunit.mode'), hint: t('myunit.modeHint') },
108
- h(SelectField, { value, options, onChange: setMode })))
107
+ h(FormRow, { label: t('myunit.mode'), htmlFor: 'myunit-mode', hint: t('myunit.modeHint') },
108
+ h(SelectField, { id: 'myunit-mode', value, options, onChange: setMode })))
109
109
  ```
110
110
 
111
111
  - 标签归 `FormRow`(默认左置;`layout: 'stacked'` 堆叠)。进了 `FormRow` 就不再给控件 `label` / `hint`。
112
112
  `hint` 与 `problem` 二选一,错误优先。
113
+ - 行是 `<div>`,`<label>` 只包标签文字;要点标签聚焦 / 展开控件,给 `FormRow.htmlFor` 与控件的 `id` 同一个值
114
+ (`Form` 内部按 `field.key` 自动接成 `wb-form-<key>`)。不给 `htmlFor` 就只是排布,点标签不聚焦。
113
115
  - 错误:位置与外观归 `FormRow.problem` + 控件 `invalid`;校验逻辑留在模块(纯函数 + 文案,交给 `validate`)。
114
116
  "是否提交过"与首错聚焦归 `Form`。
115
117
  - 选择:候选多 / 需搜索 / 需分组 / 可能读不到 → `SelectField`;候选少 / 要全览 / 多选 → `ChoiceGroup`。
@@ -130,7 +132,8 @@ h(FormRows, null,
130
132
  ## 7 规则
131
133
 
132
134
  1. 调用点只给数据与语义;外观类名只允许布局槽(通栏、外边距、网格位置)。
133
- 2. 字号与颜色走 kit 与令牌(`--dsw-*` / `--wb-*`)。重点数字用 `--dsw-font-xl-24` + `tabular-nums`;
135
+ 2. 字号与颜色走 kit 与令牌。重点数字用 `--dsw-font-xl-24` + `tabular-nums`;
136
+ 令牌名现查不猜:`workbench_diy_components` 给 `scope: 'tokens'`(现读装着的 dsh 与外壳;`name=--dsw-…` 连浅/深值)。
134
137
  刻度外字面值在模块 `README.md` 说明([PANEL.md](PANEL.md) §6)。
135
138
  3. 自写 CSS 只写布局(flex / grid / gap / 对齐)。
136
139
 
@@ -15,7 +15,7 @@
15
15
  | 其他窗口 / 会话改动 | 跟随变,无需刷新 |
16
16
 
17
17
  一次说清变哪一级:整板 / 一张卡 / 一行。"刷新一下"不合格。只动该动的格,粒度落到"某投影 + 最窄参数"
18
- (`cmdb/builds?env=dev`);否则会整卡重读。
18
+ (`inventory/builds?env=dev`);否则会整卡重读。
19
19
 
20
20
  ## 2 进行中
21
21
 
@@ -86,10 +86,12 @@ write.send({ ignored, timeoutSeconds, workspace }); // 控件:Field + TextFi
86
86
  - 跳整张卡对应的系统 → 卡头 `CardHead.platformHref`;跳具体页面 / 记录 → `Link`;触发动作 → dsh `Button` 或阻断屏 `action`。
87
87
  - 标签 + 输入框用 `Field` + `TextField`,不自写 CSS。
88
88
  - 组件选型照 [KIT.md](KIT.md)。
89
- - **字号与重点**:走刻度(`--dsw-font-*`:11 / 12 / 13 / 14 / 16 / 20 / 24),不整卡同字号。
90
- 最重点的数字用 `--dsw-font-xl-24`(24/32、600),旁注 12;刻度外字面值需在模块 `README.md` 说明。
89
+ - **字号与重点**:走刻度,不整卡同字号 —— 刻度以 `--dsw-font-*` 的定义为准,别背数字
90
+ (`workbench_diy_components` `scope: 'tokens'`、`bucket=font` 现查)。
91
+ 最重点的数字用最大一档(`--dsw-font-xl-24`),旁注用最小一档;刻度外字面值需在模块 `README.md` 说明。
91
92
  成列数字加 `font-variant-numeric: tabular-nums`。
92
- - 颜色与间距走令牌(`--dsw-*` / `--wb-*`);自写 CSS 只写布局(flex / grid / gap / 对齐)。
93
+ - 颜色与间距走令牌;名字现查不猜:`workbench_diy_components` 给 `scope: 'tokens'`(`bucket` 按族、`name` 连浅/深值)。
94
+ 写进 CSS 的令牌名由 `check --only style` 对着现读的清单验,拼错判红。自写 CSS 只写布局(flex / grid / gap / 对齐)。
93
95
 
94
96
  ## 7 状态矩阵
95
97