@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
@@ -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
@@ -1,150 +1,79 @@
1
- # 实现参考(DIY 域):把模块造成一个独立包
1
+ # 实现:落点、模板、命令、两个面
2
2
 
3
- 本文只管"怎么落":工坊、骨架、文件、命令、两个面。接上外壳与交付(依赖 / 禁区 / 装 / 热换 / 发布)
4
- 在 [WIRING.md](WIRING.md)。设计口径看 [DESIGN.md](DESIGN.md) 与 [STEPS.md](STEPS.md);
5
- 门禁红了看 [REVIEW.md](REVIEW.md)。
3
+ 只管怎么落。依赖 / 安装 / 热换 / 发布见 [WIRING.md](WIRING.md);需求见 [DESIGN.md](DESIGN.md) / [STEPS.md](STEPS.md);
4
+ 门禁见 [REVIEW.md](REVIEW.md)。
6
5
 
7
- ## 0 一个模块就是一个独立的 dsh 插件包
6
+ ## 0 模块结构
8
7
 
9
- 它不是工作台源码的一部分,**不认识工作台源码**:它把自己插进 dsh 的 profile,
10
- 外壳通过服务名 `workbenchUnits` 收下它。所以"造模块"只发生在一个目录里,不动外壳一个字节。
11
-
12
- 它同时是两半:
13
-
14
- | 半 | 文件 | 跑在哪 | 干什么 |
8
+ | 半 | 文件 | 运行于 | 职责 |
15
9
  |---|---|---|---|
16
- | 宿主半侧 | `src/host.ts` → `dist/host.js` | Node(dsh 宿主进程) | 注册服务、路由、agent 工具、长任务 |
17
- | 浏览器半侧 | `src/client.tsx` → `client.js` | 页面里 | 画那张卡片、注册审批明细渲染器 |
10
+ | 宿主 | `src/host.ts` → `dist/host.js` | dsh 宿主进程(Node) | 服务、路由、agent 工具、长任务 |
11
+ | 浏览器 | `src/client.tsx` → `client.js` | 页面 | 卡片、审批明细渲染 |
18
12
 
19
- 两侧不许互相 import。它们只通过外壳交出来的服务名相接。
13
+ 两侧不互相 import,只经服务名 `workbenchUnits` 相接。
20
14
 
21
- ## 1 落点:工坊
15
+ ## 1 落点
22
16
 
23
17
  ```
24
- $DSH_HOME/workbench/diy/ ← 工坊(一个 pnpm workspace,依赖已经装好、与外壳同代)
25
- package.json pnpm-workspace.yaml .npmrc node_modules/
18
+ <工作区>/ ← `workbench_diy_status` 的 `工作区:`
19
+ pnpm-workspace.yaml ← 包声明
26
20
  packages/
27
- module-<unit>/ ← 你要造的模块住这儿
21
+ module-<unit>/
28
22
  ```
29
23
 
30
- 一个模块一个目录,一个目录一个包。**不要**把源码写到别处,也不要去动工坊根上的配置。
24
+ 工作区可以是 monorepo / 单包 / 空项目。模块必须落在会话工作区内,包目录由工作区声明
25
+ (`pnpm-workspace.yaml` 的 `packages:`、`package.json` 的 `workspaces`,或根本身即包)。不改工作区根配置。
31
26
 
32
- ⚠️ **骨架不在就先补上**:启动器会自愈(少了才建),没建起来时跑一次 `dsh-workbench setup`(幂等)
33
- 再回来跑模块命令 —— 没有骨架时门禁会把"模块不在任何 workspace 里"讲成"声明写错了",那是症状。
34
- 工具链跟外壳**不同代**时不用你管:**每一次工作台启动**都会拿"正在跑的那份外壳"(基准经
35
- `DSH_WB_SHELL_ROOT` 显式传入)比一次,不同代就在后台把它同步到同代 —— 所以升级之后
36
- **下一次启动**就对上了(`workbench_diy_status` 里能看到「⏳ 工坊工具链正在后台同步到 <版本>」,
37
- 或同步没成时的「⚠️ 工坊工具链还是上一代(…):<原因>」加一条修法;同步那几行在
38
- `$DSH_HOME/workbench/seed.log`)。
27
+ ⚠️ 无可放代码的包(无 `package.json` 或未声明 workspace)→ 问用户:在此建 workspace,还是去工坊脚手架工作区。
28
+ 不改用户仓库为 monorepo。从「造一个模块」进入的会话已在工坊,`<dir>` 即工坊。
29
+ 工具链与外壳不同代不用处理:启动时比对并后台同步(`workbench_diy_status` 有状态)。
39
30
 
40
- 工坊里的 `@dsh-workbench/*` **只有一份**(工坊根的 `link:` + 一份 `pnpm.overrides` 钉着):模块自己的
41
- `pnpm build` / `pnpm exec dsh-wb-module` 和工坊根的 `.bin/dsh-wb-module` 是同一个二进制,
42
- `check --only kit` 比的也是用户**真正在跑**的那份外壳。别在模块里另装一份——两代并存是静默的
43
- (你会在模块里看到旧模板、旧报错,而工坊根那份什么都说得好好的)。
31
+ 模块内的 `pnpm build` / `pnpm exec dsh-wb-module` 与工作区根同一二进制,不要在模块里另装(会静默出现两代)。
44
32
 
45
- ## 2 从模板改,不从零写
33
+ ## 2 从模板开始
46
34
 
47
35
  ```bash
48
- pnpm exec dsh-wb-module init <unit> --dir <工坊>/packages/module-<unit> --template diy --description "<一句话>"
36
+ pnpm exec dsh-wb-module init <unit> --dir <工作区>/packages/module-<unit> --template diy --description "<一句话>"
49
37
  ```
50
38
 
51
- `<unit>` 是小写短横线标识(如 `my-thing`),它同时是单元 id、包名后缀、卡片样式前缀。
52
- **它必须全局唯一** —— 和工作台里其它模块、和外壳里已有的单元都不能撞;撞了装上去当场抛。
53
-
54
- 生成的骨架是**能直接跑的**(十三条判据 + 六个脚本开箱全绿;门禁还在,模板改坏了当场报)。
39
+ `<unit>`:小写短横线,同时是单元 id、包名后缀、样式前缀;全局唯一(与已装模块、外壳单元冲突时装配抛错)。
40
+ 骨架开箱即绿(十三条判据 + 六脚本)。
55
41
 
56
- **两个模板,按需要挑**(加 `--template <名字>`):
57
-
58
- | 模板 | 是什么 | 什么时候用 |
42
+ | 模板 | 内容 | 适用 |
59
43
  |---|---|---|
60
- | `minimal`(不写就是它) | 空壳:卡壳、文案、测试都接好了,但**没有数据源** | 你已经清楚要读什么、想从零写 |
61
- | `diy` | 一张**读当前项目的样板卡**:宿主侧一条真读目录的只读路由、卡片五态(待机 / 加载 / 就绪 / 就绪但空 / 出错) | 你要照着改出一个"真能出数据"的最小版本 —— **建议先用它** |
62
-
63
- ⚠️ `diy` 那份是**给别人照抄的**,所以它自己也必须开箱全绿 —— 门禁会**两个模板各 init 一遍、各跑一遍全套**。
44
+ | `minimal`(默认) | 空壳:卡壳、文案、测试;无数据源 | 已明确读什么 |
45
+ | `diy` | 样板卡:宿主一条只读路由 + 五态 | 需要可运行的数据样板(推荐) |
64
46
 
65
- 骨架是**分层多文件**的(不是单文件):目录**按需要出现** —— 空壳可以只有 `components/`,
66
- 有状态逻辑才需要 `model/`(`diy` 两样都有,`minimal` 不硬造 `model/`)。骨架里的文件各有其职
67
- (`minimal` 是空壳那几份,`diy` 多一份 `design.json`、`src/model/`、`src/components/OverviewCard.test.ts` 与 `types/styles.d.ts`,见下面那张 ⚠️):
47
+ 骨架分层:`src/model/**`(纯逻辑)、`src/components/**`(视图 + 同目录 `*.module.css`)、
48
+ `src/messages.ts`、`src/host.ts`、`src/client.tsx`、`scripts/smoke.mjs`、`design.json`。
49
+ 模板原文可直接读。非显然项:
68
50
 
69
- | 文件 | 职责 |
51
+ | 文件 | 要点 |
70
52
  |---|---|
71
- | `package.json` | 自述清单:包名、`dsh.bundle.patch`、`dsh.client`(`external` 默认带 `@dsh-workbench/core/client` 与 dsh 原语那行)、`dsh.workbench.unit`、`dsh.workbenchFaces{host,client}`(工具链字段,门禁必填)、六个脚本、peer 依赖;原语那行由 `init` 盖精确版([KIT.md](KIT.md) §8) |
72
- | `arch.config.mjs` | 模块自己的**架构方言**:**只有一行**工厂调用 `moduleArch()`(从 `@dsh-workbench/module-tools/arch` 来)。层序表 / 两个运行时 / 阈值只有那一份,随工具链同代 |
73
- | `eslint.config.mjs` | 模块自己的**语法卫生方言**:**只有两行** —— `moduleLint({ rootDir: import.meta.dirname })`(从 `@dsh-workbench/module-tools/lint` 来;`rootDir` 把 tsconfig 根钉在这个模块上,多份配置并存时 typescript-eslint 才猜得出)。规则 / ignores 只有那一份,随工具链同代;要放行某条就从 `moduleLint({ rules, ignores })` 开口子,别改方言那些块(判据比整份 8 块,逐块点名),也别手写一份自己的(自己 import 方言再照抄整份则分不出来) |
74
- | `cordis.patch.yml` | 把自己插进 profile 的那一行 |
75
- | `src/model/**` | 纯逻辑:类型、换算、状态机(`diy` 是 `overview.ts` + 配对测试)。两个运行时都能读,不碰 react / kit / dsh / `node:*` |
76
- | `src/components/**` | 浏览器侧的视图件,**样式与它同目录**(`X.module.css`;`diy` 是 `OverviewCard.tsx`)。整片算 client 面,宿主侧碰不得 |
77
- | `src/host.ts` | 宿主面:`manifest` + `mount()`(路由 / 工具);类型从 `src/model/` 引,同一个概念不留两处真相 |
78
- | `src/client.tsx` | 浏览器面:`manifest` + `card` —— **只剩面**(注册 + 把 props 交给 `components/` 那个组件) |
79
- | `src/messages.ts` | zh / en 文案;键必须带 `<unit>.` 前缀,两种语言同构 |
80
- | `src/plugin.test.ts` | 单测:两侧都把自己的面注册进了外壳(`diy` 还多一条宿主路由) |
81
- | `src/components/X.test.ts` | 组件自己的渲染断言(`diy` 把 `OverviewCard` 的五态各画一遍拆在这儿。**测试文件不吃方言的 S16「文件 200 行」**(2026-10-19 起内置豁免,后缀式与 `__tests__/` 两种命名都算),所以并回一个文件也不会红 —— 拆开只为读着顺) |
82
- | `vitest.config.ts` | 测试配置(`@/modules/<unit>/` 的别名) |
83
- | `scripts/smoke.mjs` | `selfcheck`:把 `client.js` 当浏览器加载器跑一遍,看卡片真进没进外壳 |
84
- | `tsconfig.json` | `paths` 里 `@/modules/<unit>/*` → `./src/*`,值必须是**相对路径** |
85
- | `types/globals.d.ts` | 经典 JSX 运行时的全局类型垫片 |
86
- | `types/styles.d.ts` | CSS Modules 的类名表声明(`declare module '*.module.css'`)。`globals.d.ts` 顶层有 import、是模块,那里的 `declare module` 会变成模块增强而不是环境声明 —— 用了 `X.module.css` 就必须有这一份(`diy` 有) |
87
- | `design.json` | 三张表 + 状态矩阵 + 数据源记录 + 验收清单(**唯一真相**;README 里那一段由它生成) |
88
- | `README.md` | 这个模块自己的说明 |
89
-
90
- 改哪一个:**画什么改 `src/components/*.tsx` + 它同目录的 `*.module.css`,纯逻辑(换算 / 状态机 / 类型)
91
- 改 `src/model/**`,文案改 `src/messages.ts`;`src/client.tsx` 只留注册那一层**,需要能力再动 `src/host.ts`。
92
- 模板已经把组件套好了外壳的卡壳(`kit.components.Card`)、三态与测试;你只改里面。
93
- **宿主侧不许 import `src/components/**`** —— 那是跨运行时,`arch` 判据当场红(层号拦不住它:
94
- components 第 2 层、host 第 10 层)。
95
-
96
- ⚠️ **三张表落在 `design.json`**(`diy` 骨架自带一份填好的,`minimal` 里没有 —— 用它就照
97
- [DESIGN.md](DESIGN.md) §5 建一个):把它改成你刚问出来的那份,**再改代码**。
98
- README 里 `<!-- dsh-wb:design:start/end -->` 之间那一段**由它生成**(`design render`)—— 手改会判红。
99
-
100
- ## 3 命令与脚本
101
-
102
- ```bash
103
- dsh-wb-module build # 出 client.js 与 dist/host.js
104
- dsh-wb-module check # 十三条判据,全跑
105
- dsh-wb-module check --only cards # 单跑一条(contracts / protocol / bundles / source / arch / lint /
106
- # primitives / kit / cards / standalone / markers / style / alias)
107
- dsh-wb-module check --only primitives [--all|--json] # 查询口:dsh 原语里有什么
108
- dsh-wb-module lint [--fix] # 跑模块自己那份 eslint 配置;--fix 写回
109
- ```
110
-
111
- 三张表的填/查/生成/打勾见 [DESIGN.md](DESIGN.md) §5。
53
+ | `arch.config.mjs` | 一行 `moduleArch()`(`@dsh-workbench/module-tools/arch`);层序、运行时、阈值随工具链 |
54
+ | `eslint.config.mjs` | 两行 `moduleLint({ rootDir: import.meta.dirname })`;`rootDir` 钉 tsconfig 根 |
55
+ | `types/styles.d.ts` | 用 `X.module.css` 必须有;`globals.d.ts` 是模块,其 `declare module` 不算环境声明 |
56
+ | `design.json` | 三张表 / 矩阵 / 数据源 / 验收;README 段由它生成(`minimal` 无此文件,按 [DESIGN.md](DESIGN.md) §5 建) |
112
57
 
113
- ⚠️ 写成 `pnpm exec dsh-wb-module …`(工坊里 PATH 上也有同名入口,由启动器挂);
114
- 模块目录里更省事的是那几个脚本(`pnpm build` / `pnpm selfcheck`)。
58
+ 修改位置:视图改 `src/components/*.tsx` + 同目录 `*.module.css`;逻辑改 `src/model/**`;文案改 `src/messages.ts`;
59
+ `src/client.tsx` 只留注册。宿主侧不 import `src/components/**`(跨运行时,`arch` 判红)。
115
60
 
116
- `package.json` 里那前四个脚本都要留着(契约判据会查);`check` 是第五条(`arch` 方言的 M09 要这个脚本名);`lint` 是第六条(`lint` 判据要它):
61
+ ## 3 命令
117
62
 
118
- ```bash
119
- pnpm build # 构建
120
- pnpm typecheck # tsc --noEmit(init 后先 pnpm install)
121
- pnpm test # vitest
122
- pnpm selfcheck # 构建 + 把 client.js 当加载器跑一遍(`diy` 骨架里还多一条 `design check`)
123
- pnpm check # dsh-wb-module check:十三条判据
124
- pnpm lint # dsh-wb-module lint:语法卫生(`pnpm lint -- --fix` 写回)
125
- ```
63
+ 用 `pnpm exec dsh-wb-module …`;命令、`--only` 判据名与模板参数看 `--help` / `check --list`,不在指引里抄。
64
+ `package.json` 六个脚本(`build` / `typecheck` / `test` / `selfcheck` / `check` / `lint`)必须保留,
65
+ `contracts` 点名缺失。
126
66
 
127
- **自己的门禁自己过,红了别抛给用户。** 十三条判据在 [REVIEW.md](REVIEW.md) 里逐条翻了人话。
128
- ⚠️ **`check` 不替你跑 `pnpm test`**(它只看形状与产物):状态级的渲染、换算、边界都在你自己的
129
- `test` 里 —— 骨架开箱就带一份,改了数据源与渲染就顺手把它跑一遍,别让红线留在包里。
130
- ⚠️ **工坊里恒有两条是「不适用」,那不是「通过」**:`独立构建` 要外壳源码在场才跑得起来,
131
- `公开包无内部标识` 只扫要发出去的包(DIY 模块一律 `private`)。汇总行下面会把
132
- 这次没查的那几条逐条列出来;所以「8 通过 · 2 不适用」不等于十三条全绿 —— 别把这句话念给用户听。
67
+ - `check` 不跑 `pnpm test`;状态、换算、边界测试自己跑。
68
+ - "不适用"不是"通过":`standalone` 需外壳源码在场,`markers` 只扫公开包(DIY 模块 `private`)。
69
+ 汇总行会列出未检查项。
133
70
 
134
- 装上板那一步不是命令,是一套 agent 工具:`workbench_diy_list` 看工坊里有什么、
135
- `workbench_diy_status`(`unit=<单元 id>`)看它现在走到哪一步、`workbench_diy_install` 装上并上板、
136
- `workbench_diy_show` 只上板 —— 见 §7。**上完板自己逐条走一遍(这就是验收那一拍)**:浏览器那 14 把
137
- (`state` / `get html` / `find` / `click` / `hover` / `type` / `fill` / `keys` / `scroll` / `wait` / `eval` /
138
- `screenshot` / `network` / `dialog`,argv 与结果原样转发,全表见 [FAMILY.md](FAMILY.md))打的是
139
- **用户当前那一页**上工坊那块板里的那张卡 —— 用户那一页不会被换掉,但他得把工作台切到前台。
140
- **看屏 / 动屏那一族(浏览器那 14 把 / `logs_*`)要先 `workbench_diy_debug_start`**(`unit=…`;
141
- 会请他点头一次)—— 没有这场约定它们一律当场拒;不看屏了先 `_pause`、收工 `_stop`。
142
- 卡片有没有报错先看 `workbench_diy_panel` 的「崩过 / 读到了」;真要这一页的控制台,用
143
- `workbench_diy_logs_watch` → 做那件事 → `workbench_diy_logs_read`(钩子有上限,见 [REVIEW.md](REVIEW.md) 四步取证的 4)。
71
+ 安装 / 上板是 agent 工具:`workbench_diy_install`、`workbench_diy_show`、`workbench_diy_status`
72
+ ([WIRING.md](WIRING.md) §3)。上板后逐条走查即验收。
144
73
 
145
- ## 4 两个面的形状
74
+ ## 4 两个面
146
75
 
147
- 宿主面(`src/host.ts`):
76
+ 宿主面:
148
77
 
149
78
  ```ts
150
79
  export const name = '<包名>';
@@ -152,25 +81,20 @@ export const inject = ['workbenchUnits'];
152
81
 
153
82
  export function apply(ctx) {
154
83
  const units = ctx.get('workbenchUnits');
155
- if (units === undefined) return; // 外壳没交这个服务:报一声,别静默
84
+ if (units === undefined) return;
156
85
  ctx.effect(() => units.register({ manifest, mount }), '<unit>: 注册');
157
86
  }
158
87
  ```
159
88
 
160
- `mount()` 返回 `{ routes, registerTools, prefs? }`:
161
- `routes` 是交给外壳去挂 `/workbench/api/<路由>` 的只读投影(只许 GET);
162
- `registerTools` 交出这个模块的 agent 工具。**路由的键必须住自己那一格**(`<unit>` 或 `<unit>/…`),
163
- 否则装配期直接抛;只读路由要表达"出错"时回 `{ ok: false, error: { code, message, hint? } }`(HTTP 仍是 200,外壳原样转发)。
164
-
165
- 浏览器面(`src/client.tsx`):`units.registerUnit({ manifest, messages, card })`。
166
- `card` 拿到 `{ t, world, refreshToken, onRefresh, onSummary }`(`world.current.workspace` 就是当前项目),
167
- 返回一棵元素树。**三态用外壳给的组件,别自己画**:
168
- 加载 → `kit.components.SkeletonRows`;空 → `kit.components.EmptyState`(不带按钮);
169
- 出错 → `kit.components.BlockerState` + `kit.lib.blockerOfError(error, t)`。
170
- ⚠️ **`idle` 是独立的一格**(没工作区、或取数被 `enabled: false` 关掉时),不许画成"空" ——
171
- "空"和"还没开始"是两件事,说错了他会去查他根本没配的东西。
89
+ `mount({ data })` 的 `data` 是统一数据端口(`DataReader`);读数据用 `data.read(...)`,不要自己 import adapter。
90
+ `mount()` 返回 `{ routes, registerTools, prefs? }`。`routes` 挂 `/workbench/api/<路由>`,只许 GET,
91
+ 键必须在 `<unit>` 或 `<unit>/…` 之下(否则装配抛错)。错误返回
92
+ `{ ok: false, error: { code, message, hint? } }`(HTTP 200,外壳转发)。`registerTools` 提供模块工具。
172
93
 
173
- **卡片必须在拿不到数据时也画得出来**(第一次读、卡在审批、平台没登录)。
174
- 任何一种输入(待机 / 在读 / 有值 / 有值但空 / 读不到)下抛异常、屏上冒出 `undefined` / `NaN` 的卡片 = 用户眼里的"卡片一直不出来"。这是 `check --only cards` 守的那条。
94
+ 浏览器面:`units.registerUnit({ manifest, messages, card })`。`card` 收到
95
+ `{ t, world, refreshToken, onRefresh, onSummary }`(`world.current.workspace` 为当前项目),返回元素。
96
+ 三态用 kit:加载 `kit.components.SkeletonRows`;空 `kit.components.EmptyState`(无按钮);
97
+ 错误 `kit.components.BlockerState` + `kit.lib.blockerOfError(error, t)`。
175
98
 
176
- → 拿到依赖、装上、改一行、发出去:[WIRING.md](WIRING.md)。
99
+ `idle` 是独立态(无工作区或 `enabled: false`),不画成空态。
100
+ 卡片在任意输入下不得抛异常或出现 `undefined` / `NaN`(`check --only cards`)。
@@ -0,0 +1,89 @@
1
+ # 文案:先判读者
2
+
3
+ 一块模块同时给三种人写字:卡片给人、工具描述给 agent、原始输出给开发者。判错读者,后面全错。
4
+
5
+ ## 1 三类读者
6
+
7
+ | 读者 | 读什么 | 判据 |
8
+ |---|---|---|
9
+ | **agent** | 工具描述、参数说明、返回、错误、`reason` / `{blocked}`、中断通知、`context.render` | 读完知道下一次调什么 |
10
+ | **终端用户** | 卡片、空态、阻断、按钮、配置、审批屏、agent 的交代 | 读完知道点哪 / 要不要做,且不写代码 |
11
+ | **开发者** | 日志、`detail`、原始输出 | 排障时能对照、能检索 |
12
+
13
+ 审批字段的归属容易混:
14
+
15
+ | 字段 | 读者 |
16
+ |---|---|
17
+ | `needsApproval.question` / `fallback`、`approvalDetail` / `approval.render`、`approval_note` | 终端用户 |
18
+ | `needsApproval.reason`(deny)、`{blocked}` | agent |
19
+
20
+ ## 2 两层:主句 + 原文
21
+
22
+ 同一处既有用户又有技术细节时,成对写:
23
+
24
+ - **主句**:给人,不含错误码、路径、堆栈、内部词。
25
+ - **原文**:给开发者,原样,放 `detail` / 折叠区 / 日志。
26
+
27
+ 两条判据:
28
+
29
+ 1. 遮住原文只读主句:用户知道发生了什么、下一步做什么。
30
+ 2. 遮住主句只读原文:开发能定位。
31
+
32
+ ## 3 人侧
33
+
34
+ 1. **可复述**:不写代码的人读完能说出下一步。
35
+ 2. **名词看得见**:用界面上的词;`unit` / 套件 / 宿主 / schema / payload / 运行时 不进主句。
36
+ 3. **一句一个意思**:不靠括号补解释,一句不放两个动作。
37
+ 4. **不露空值**:读不到就说读不到,不写 `0` / `undefined` / `NaN` / 裸键名。
38
+
39
+ 状态四义、时间写相对时间、单位跟数字:见 [PANEL.md](PANEL.md) §7。
40
+ 审批那一屏的写法:见 [DESIGN.md](DESIGN.md) §3 —— 标题一句,事实进详情,明细一行一个事实。
41
+ 中文与英文都写;英文重写,不直译。
42
+
43
+ ## 4 agent 侧
44
+
45
+ **agent 自己推得出的,不写。** 工具名、参数名、schema、上一次返回已经说了的,不重复第二遍。
46
+
47
+ - 删:字段清单、枚举释义、参数名复述、理由、输出里已有的话。
48
+ - 留:写与危险、跨工具下一步与字面量、非显然的默认与上限、会误导的坑、安全事实。
49
+ - 描述:一句“是什么” + 一句“什么时候用”;不写“这个工具用于……”。
50
+ - 返回:事实 + 总数 / 分页 + 下一次调用;ID、版本、单位原样。
51
+ - 错误:发生了什么 + 能不能重试 + 下一步;不甩堆栈。
52
+ - 通知:自包含 —— 谁做了什么 + 哪件事变了 + 现在做什么。
53
+
54
+ 加字先减字。模块工具的提示词没有字数闸,自己守。
55
+
56
+ ## 5 开发侧
57
+
58
+ - **原文**:日志、堆栈、命令不翻译、不美化。
59
+ - **可检索**:保留错误码、路径、ID、时间戳。
60
+ - **不进主句**:这些内容用户能看到时,折叠并标注“原始输出 / 堆栈 / 命令”;主句不依赖它。
61
+
62
+ 日志一行:目标 + 发生了什么 + 关键值原样。不写“出错了”这种没有定位信息的句子。
63
+
64
+ ## 6 硬规则
65
+
66
+ - 读不到写读不到,不写 0。
67
+ - 主句只放人话;错误码、路径、堆栈、内部词放原文。
68
+ - 事实原样:ID、版本、单位、时间戳不改写、不四舍五入。
69
+ - 不引用不存在的工具、字段、页面、命令。
70
+ - 不写“系统内部错误 / 请稍候”这类没有信息的句子。
71
+
72
+ ## 7 验
73
+
74
+ 1. 真卡片:五态、两语各看一遍;`dsh-wb-module check --only cards` 抓 `undefined` / `NaN` / 裸键。
75
+ 2. 遮住测试两条都过。
76
+ 3. 工具:注册后读描述,逐句问“模型推得出来吗”;真调一次读返回;漏参数读报错。
77
+ 4. 拿五个用户会说的话试:只凭描述能不能挑中这把工具。
78
+
79
+ ## 8 例
80
+
81
+ | 面 | 改前 | 改后 |
82
+ |---|---|---|
83
+ | 空态 | 暂无数据 | 这个环境还没有构建记录。换一个环境,或先跑一次构建。 |
84
+ | 阻断 | 系统内部错误,请稍后重试 | 这次没读到发布记录。检查网络后重试。 |
85
+ | 按钮 | 确定 | 发布到生产 |
86
+ | 描述 | 用于查看构建状态的工具 | 看某个环境最近一次构建:状态、耗时、提交;用户说“发布没成功”就先调它。 |
87
+ | 返回 | 操作成功 | 已发布 v1.2.3 到生产;下一步 `_build` 看结果。 |
88
+ | 拒绝 | 不允许 | 这个环境是生产;先在确认条上点「发布到生产」。 |
89
+ | 日志 | 出错了 | `[inventory] release v1.2.3 failed: HTTP 502 (retryable)` |