@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
package/docs/HANDOFF.md DELETED
@@ -1,333 +0,0 @@
1
- # 把工作台给别人用
2
-
3
- 工作台是一个 **dsh 插件包**,现在发在**公网 npm** 上:`@dsh-workbench/core`
4
- (实测 `npm view @dsh-workbench/core version` 出号,`npm pack` 的产物里
5
- `dsh.bundle.patch` / `cordis.patch.yml` / `client.js` / `dist/host/index.js` 都在)。
6
- 所以**对方不需要配任何私有源**就能装。
7
-
8
- 两种给法,先挑一种:
9
-
10
- | 给法 | 对方要做的事 | 换来的 |
11
- | --- | --- | --- |
12
- | **A. 装进他自己的 `dsh web`**(最短) | Plugins → Add plugin → `@dsh-workbench/core` → Enable now → **刷新页面** | 就在那份 web 里换成工作台;**同一个 profile、同一份会话**、同一个端口 |
13
- | **B. 独立实例**(要数据分家就选它) | `npx --yes @dsh-workbench/core@latest setup` → `npx @dsh-workbench/core` | 另一个 profile、另一个端口(:3081),他自己那份 `dsh web` **一行不改** |
14
-
15
- 一条路径一句话:**A = 点一下装(单 profile);B = `setup` 然后起(双 profile)**。
16
- 取舍与代价见 [`docs/adr/0037`](../../../docs/adr/0037-workbench-installs-into-any-profile.md)。
17
-
18
- ## 一、对方机器上要有的
19
-
20
- | # | 东西 | 为什么 | 怎么有 |
21
- | --- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
22
- | 1 | `dsh` | 本体。本仓库在 `0.2.1-rc.2` 上验过,**低于它的会被拒绝** —— 见第八节 | `npm i -g @deepseek-ai/dsh@latest`(公网 npm 上有) |
23
- | 2 | `node` + **`pnpm`** | dsh 的 `plugin` 子命令(以及 web 的 Plugins 页)都是**转发给 pnpm** 的;没有 pnpm 会直接报 `dsh: pnpm was not found` | 各自装 |
24
- | 3 | 跑过一次 `dsh web` | 走 A 需要它给出那个 profile;走 B 时 `setup` 拿 `~/.dsh/profiles/web` 当模板。没有它会**明确报错**,不会静默建出半个 profile | `dsh web` |
25
- | 4 | 一个能解析这个包的源 | 包在**公网 npm** 上,默认源即可。只有将来改发内网源时,才要 `npm config set @<scope>:registry …` + `npm login`(带 scope 的包只去 `.npmrc` 里给这个 scope 指的源找) | `npm view @dsh-workbench/core version` 出号即可 |
26
- | 5 | Coding 账号 + opencli 登录态 | 面板的数据来自 opencli 的 coding 插件 | 见第五节 |
27
-
28
- ## 二、A:装进他自己的 `dsh web`(插件页,一条龙)
29
-
30
- 在 `dsh web`(:3080)里:
31
-
32
- 1. 侧栏 **Plugins → Add plugin**,填 `@dsh-workbench/core`;
33
- 2. **Install → Enable now**;
34
- 3. **刷新一次页面**(客户端 bundle 是页面加载时装进去的)。
35
-
36
- 装完左栏就是工作台、右侧多出面板列。缺的那几件(隔离 opencli 的入口、`dsh-wb-module`、
37
- DIY 工坊骨架、可写根)由**外壳自己**补齐 —— 不用跑 `setup`,也不用起第二个实例。
38
-
39
- 差别(先跟对方说清):**会话 / 存储不分家**(接着用他原来那份)、**没有独立端口与进程**、
40
- **改外壳源码不热重载**、DIY 工坊依赖是首次启动在后台装的。工坊的工具链(那三个
41
- `@dsh-workbench/*`)此后**每次启动**都会与**正在跑的那份外壳**比一次(基准经 `DSH_WB_SHELL_ROOT`
42
- 显式传入 —— 宿主给加载中那份、启动器给 profile 里那份),不同代就在后台增量同步到同代 ——
43
- 升级外壳不用手动删工坊,**下一次启动**就对上了;同步不上时 `workbench_diy_status` 会说清
44
- (差在哪、为什么、怎么修),日志在 `$DSH_HOME/workbench/seed.log`。
45
-
46
- ## 三、B:两步装(独立 profile,占 :3081)
47
-
48
- ```bash
49
- # ① 建 profile + 把工作台装进去,然后起(占 :3081,dsh web 继续用 :3080)
50
- npx --yes @dsh-workbench/core@latest setup
51
- npx @dsh-workbench/core
52
- ```
53
-
54
- > 若团队后来把包改发到内网源,这两条命令之前要各加一次:
55
- > `npm config set @<scope>:registry "$NPM_REGISTRY"` 与 `npm login --registry=$NPM_REGISTRY`。
56
- > 不设的话症状是 `404 Not Found - GET https://registry.npmjs.org/@<scope>%2F…`。
57
-
58
- > ⚠️ **0.1.6 的已知缺陷**:`setup` 可能死在
59
- > `…/@dsh-workbench/core/scripts/seed-profile.sh: Permission denied`(发出去的包里随包脚本丢了
60
- > 可执行位,`pnpm publish` 只给 `bin` 字段点名那条 755)。应急:给那份副本补上 x 位,
61
- > 再重跑同一条 `npx` 命令 ——
62
- > `chmod +x ~/.npm/_npx/*/node_modules/@dsh-workbench/core/{scripts/seed-profile.sh,bin/opencli-isolated}`。
63
- > 0.1.7 起启动器已不吃 x 位,重装那个版本即可根治。
64
-
65
- ### 设置从哪来:**工作台自己那份说了算**,想要 web 的行就同步一次(ADR-0044)
66
-
67
- 工作台**不再**把 `profiles/web/cordis.patch.yml` 叠在自己上面 —— 叠层是最高层,
68
- 它盖住的行在 `:3081` 的 Settings 里**改不动**(dsh 的 config-editor 会被那句
69
- `overridden by a home patch or command-line overlay` 挡在写文件之前)。
70
- 现在的工作方式:
71
-
72
- - **要配什么就在 `:3081` 配**,落在 `profiles/workbench/cordis.patch.yml` —— 那是它唯一的那份设置文档;
73
- - **想用 web 那边已经配好的行**(模型供应商 / API key 引用 / 搜索源 / MCP)⇒ `:3081` →
74
- `Meta+,` → 「配置同步」→ 选来源 `web` → 勾行 → 同步(即生效,不用重启)。
75
- 它读的是那个 profile 的 `cordis.patch.yml`,按行搬,只搬**显式配过**的行;
76
- 同 id 两边内容不同时**默认不覆盖**,要覆盖得自己勾;行点名的包没装会当场说出来。
77
- - **web 后来改了不会自动过来** —— 再打开这一页,那几行会重新以「差异」标签出现在这一页,拉一次即可。
78
-
79
- ### 一条"看起来坏了、其实没坏"的情况
80
-
81
- Settings → 插件里把 `@dsh-workbench/core` 关掉后,工作台会**退化成原版 dsh web**:
82
- 左栏回到出厂样式、右侧数据面板消失。这是**设计内的降级**(关掉 = 它那份 bundle patch
83
- 整份不再应用,连里面的"禁用出厂左栏"也一并失效),**会话 / 面板配置 / 工作区数据都不丢**。
84
- 恢复:插件页把开关拨回来(或按 `Meta+,` 打开设置);**进程已经退了的**,启动器读到
85
- "bundle 没被选中"会直接拒绝启动并打印修复命令 —— 跑 `./bin/dsh-workbench setup` 补回来。
86
- 别把它当成故障去重建 profile。
87
-
88
- 顺带一条:即使关掉工作台,profile 里剩下那层差异(会话/存储/附件隔离)**仍然生效**
89
- —— 那是无害且想要的(数据继续分开)。另外启动器每次都会叠一层 `sandbox-policy`,把执行缝的
90
- 可写根指到 `$DSH_HOME/workbench`(`profile/sandbox.patch.yml`)—— 少了它,工作台自己跑 npm
91
- (自动装 opencli、市场装插件)会被沙箱拒写。它在 Settings 里显示为
92
- `overridden by a ... command-line overlay`,**属正常**。
93
-
94
- ### `setup` 到底做了什么(幂等,可以重复跑)
95
-
96
- 1. 拿 `~/.dsh/profiles/web` 当模板建 `workbench` profile;
97
- 2. 继承 web 的 **bundle 清单**(装没装东西 —— 安装范畴,走 `dsh plugin` 通路);
98
- 3. 追加**数据隔离层**(会话 / 存储 / 附件各走 `~/.dsh/workbench/**`);
99
- 4. `dsh plugin --profile workbench add @dsh-workbench/core` —— 走 **dsh 自己的**
100
- 安装通路(兼容性预检、profile 写锁、装完自动把 bundle 登记进 profile 清单);
101
- 5. `./scripts/seed-profile.sh ensure-selected` —— **确保选中**:读 `dsh.profile.bundles`,
102
- 缺 `@dsh-workbench/core` 就补进去(等价于 dsh 的 `selectBundle(name, true)`,已经在里面
103
- 就一个字节不写)。为什么单列这一步:`dsh plugin add` 装完的登记**跳过已存在的依赖**,
104
- 所以重跑 setup(包已在清单里)时它不会补 bundle —— 那一档是静默的(起得来、侧边栏
105
- 没入口),现在启动器拿"选中"当硬门禁,这一步就是它给的修复命令。
106
- 6. `scripts/seed-workshop.sh`(`seed-profile.sh:155-160` 调它)—— 建 / 补齐 **DIY 工坊**:
107
- 骨架缺才建、依赖缺才装、写 `dsh-wb-module` 入口。**这一步不做代际对齐**:代际比对的基准是
108
- "正在跑的那份外壳"(`DSH_WB_SHELL_ROOT`),而 `setup` 这趟 profile 里那份还没装 / 还是旧的,
109
- 所以它**不设基准、这一轮不比对**。**重跑 `setup` 只建骨架 / 装依赖;工坊工具链对上正在跑的那一代
110
- 发生在下一次启动**(启动器按 profile 里那份、宿主按加载中那份,同一个基准)—— 与 README 的
111
- "升级要重启一次"一致,不必手动删工坊。
112
-
113
- **设置不在这一步继承**(这里以前快照 web 的 patch、并继承被它点名的依赖):新机器上
114
- `setup` 完的工作台**没有模型供应商**,第一次打开去 设置 → 「配置同步」拉一次即可 ——
115
- 这样每一行都归工作台自己,改得动。有意共享的只有两样:**API 凭据**
116
- (`~/.dsh/.credentials.yaml`,一机一把 key)和 `$DSH_HOME/cordis.patch.yml`(机器公共层,
117
- 对所有 profile 实时生效)。
118
-
119
- ## 四、不想用 npx?仓库副本也可以
120
-
121
- `bin/dsh-workbench` 认得出自己是不是仓库副本(看有没有 `.git`):
122
-
123
- ```bash
124
- git clone <仓库> && cd dsh-workbench && pnpm install
125
- ./bin/dsh-workbench setup # 仓库副本:以 link 方式装本目录,改源码立刻生效
126
- ./bin/dsh-workbench
127
- ```
128
-
129
- 两种身份装出来的东西**同一个包名**,所以对方以后换成正装也不会打架。
130
-
131
- ## 五、数据通路(面板要有数据)
132
-
133
- 1. **opencli 隔离运行时**。第一次打开工作台,启动体检会**自己装**它(`@jackwener/opencli`,
134
- 公网包),落在 `$DSH_HOME/workbench/opencli/toolchain/`,**与对方本机的 `opencli` 完全隔离**
135
- —— 插件目录靠改 `HOME` 分开(见 `bin/opencli-isolated` 开头),所以这里装/卸插件
136
- 不碰他本机的 `~/.opencli`。
137
-
138
- 装不上时(没网、源不通)失败态里有一条能照抄到终端的命令。**那条得在终端跑**:工作台
139
- 自己发起的命令走 dsh 的执行缝、带文件沙箱,终端没有 —— 沙箱放行的可写根由启动器声明
140
- (`profile/sandbox.patch.yml` → `$DSH_HOME/workbench`),少了它"自己装"会死在 EPERM。
141
-
142
- 2. **交叉依赖**(opencli 生态的 npm 包,例如 cmdb 发布流程用的 `opencli-workflow`)。
143
- 它**不是 opencli 插件**(不在 `plugin list` 里、没有子命令),所以单独一类:
144
- 装在**共享**的 `$DSH_HOME/workbench/opencli/libs/`(多模块声明同一份只装一次),
145
- 版本与源由**用到它的模块**在清单里声明(`env.libs`),工作台据此在体检/市场里说"缺了 / 有新版",
146
- 并给一条能照抄的命令:
147
-
148
- ```bash
149
- npm i --prefix "$HOME/.dsh/workbench/opencli/libs" opencli-workflow@latest \
150
- --registry "$NPM_REGISTRY" \
151
- --cache "$HOME/.dsh/workbench/opencli/npm-cache"
152
- ```
153
-
154
- 缺它的时候,发布那条流程(`workbench_cmdb_release`)会**明说**"还没装 + 上面这条命令",
155
- 而不是抛一句 `Cannot find module`。理由与边界见
156
- [ADR-0015](../../../docs/adr/0015-cross-dependency-vs-opencli-plugin.md)。
157
-
158
- ⚠️ 清单里那个 `version` 是**版本契约**(`^1.1.23` 这种范围;留空 = 任意版本都满足):
159
- 共享件只装一份,"装了但不是这个模块声明要的那一支"同样算**没装好**——
160
- 模块在市场上报「未装」、面板上也不画那张卡(见
161
- [ADR-0016](../../../docs/adr/0016-installed-means-usable.md))。
162
-
163
- 3. **登录 Coding**。面板报"未登录"时,卡片上有「去登录 ↗」——在新标签里登完**切回来按
164
- 卡片上那颗「重试」**(刻意不做自动重试:登录什么时候完成我们不知道)。若仍是未登录,
165
- 就在终端用**隔离运行时那条**跑一次:
166
-
167
- ```bash
168
- $HOME/.dsh/workbench/opencli/bin/opencli coding team-current
169
- ```
170
-
171
- ⚠️ 是**这条**,不是 `which opencli` 那条 —— 两条的插件目录不同,登错了面板还是空的。
172
-
173
- 4. **coding 插件**(私有包,在内网源上)。**不用配任何东西**:那个源由模块自己带着
174
- (清单里的 `installSource.registryDefault`),市场页那一行直接给「安装」。
175
-
176
- 只有插件**不在默认那个源**上时,才用环境变量覆盖:
177
-
178
- ```bash
179
- export DSH_WORKBENCH_CODING_REGISTRY=<那个源>
180
- ```
181
-
182
- 装的时候 `bin/opencli-isolated` 会把 `~/.npmrc` 指回去,就为了拿到源的 token;
183
- npm 缓存则一律落在那台机器自己的隔离运行时里(**不碰** `~/.npm`)。
184
-
185
- ## 六、不用管的
186
-
187
- - **`dsh web` 一行不改**:`./scripts/verify.sh` 是外部证据 —— 比对 web 的 profile 哈希、
188
- 两个 profile 的组合树差异、数据是否分家,而不是自证。
189
- - 会话 / 存储 / 附件各自独立;工作台是**另一个端口、另一个进程**,可与 `dsh web` 同开。
190
- - 改工作台设置只落在 `profiles/workbench/cordis.patch.yml`。
191
-
192
- ## 七、发版(发起人)
193
-
194
- ```bash
195
- cd <仓库根>
196
- pnpm release:dry # 彩排:门禁 → 选版本号 → 构建 → publish --dry-run;不上传,结束回滚版本号
197
- pnpm release # 真发:上面那一串 + 逐包上传 + git 收尾(提交 / tag / 推送)
198
- ```
199
-
200
- 不用先 `npm version`:版本号由脚本选(交互,或 `--target 0.2.0`),同步到根与所有包的
201
- manifest;彩排结束、以及「零上传」的失败,都会自动回滚。
202
-
203
- **门禁整次发布只跑一遍**(preflight 那次 `pnpm run check`;`--resume` 也跑)。发布走
204
- `pnpm -r publish --ignore-scripts`:各包的 `prepublishOnly` / `prepack` 是留给**手敲**
205
- `pnpm publish` 的人兜底的(外壳那份 = 整仓门禁);不关掉它们,pnpm 会在**彩排与真发各再跑
206
- 一遍**整门禁 —— 一次发布就是 3 遍门禁 + 3 遍 vitest + 每个包多两轮构建(实测 pnpm 10.32.1:
207
- `--dry-run` 一样触发)。来历见
208
- [`../../../.scratch/dsh-ui-alignment/issues/12-s18-pduiv2.md`](../../../.scratch/dsh-ui-alignment/issues/12-s18-pduiv2.md) §18.45。
209
-
210
- 脚本把这几件事变成**硬失败**(都在任何上传之前):
211
-
212
- - **工作区不干净** → 默认拒(发布会把它们 `git add -A` 一起提交),`--allow-dirty` 才放行;
213
- - **目标版本必须高于当前版本**;目标版本若在源上**已完整存在**,直接拒(要补发用 `--resume`);
214
- - **npm 没登录** → 就地拉起 `npm login`;
215
- - **发布产物里零夹具** → 发布构建**之后**、任何上传**之前**跑 `check:release-artifacts --shipped`
216
- (`packages/workbench/scripts/check-release-artifacts.mjs`):逐份扫**真会进 tarball** 的那批文件
217
- (`client.js` / `dist/client/*` / `dist/types/**` / `board.css`),内容里出现夹具标记、或文件名本身
218
- 就是夹具(`dev/**`、`*-mock.*`、`__tests__/*.d.ts`…)就当场拒。这条闸审的是**包内那份**,所以它
219
- 紧跟发布构建;它要是在构建之后报了红,先看它给的是哪一种 —— 产物**被别的进程重写**了(多半是
220
- `pnpm run dev` 的 watcher:审计前后各记一次指纹,对不上就是它)还是**构建出来就脏**。
221
- 它替掉了原先只看 `client.js`
222
- 一个文件的那条单文件断言 —— 0.2.16 的 tarball 就是那么漏出去的(`dist/client/index.js` 是残留
223
- 开发档、`dist/types/**` 带着整棵夹具树);来历、三条检查与代价见
224
- [`../../../.scratch/dev-only-fixtures/spec.md`](../../../.scratch/dev-only-fixtures/spec.md) §9;
225
- - **彩排不过**(`publish --dry-run` 打不出包)→ 零上传退出。
226
-
227
- 只能自己盯、脚本不查的两条:
228
-
229
- - **`dist/` 在 `.gitignore` 里**:npm 打包的兜底就是 `.gitignore`,不在 `files` 里显式列出
230
- 就会漏掉构建产物 —— 症状不是报错,而是"别人装上后宿主侧起不来"(外壳与 `module-hello` 的
231
- `files` 都显式列了 `dist` / `client.js`,但发布脚本不核这件事);
232
- - **随包脚本的可执行位**:`pnpm publish` 只给 `package.json#bin` 点名的那**一个**文件 755,
233
- 其余在 tarball 里一律 644(`npm pack` 会保留,而 git 里的 x 位不进 tarball)——
234
- 所以随包脚本一律不吃 x 位:启动器里 `bash` 呼 `seed-profile.sh`、opencli 那条稳定路径写成
235
- 自己拥有的转发脚本。这条由外壳 `selfcheck` 的判据钉着(门禁里跑)。
236
- 这类错**本机复现不出来**,只在别人机器上炸
237
- (0.1.6 就是这么坏在 `seed-profile.sh: Permission denied` 上的,见
238
- [`.scratch/dsh-ui-alignment/issues/53-18-41-permission-denied-pnpm.md`](../../../.scratch/dsh-ui-alignment/issues/53-18-41-permission-denied-pnpm.md) §18.41)。
239
-
240
- ## 八、版本与兼容(实话)
241
-
242
- 外壳在 `package.json` 里声明了 **`engines.dsh: ">=0.2.1-rc.2"`**(本仓验过的那一代,2026-10-18)。
243
- 这条声明是后补的,来历见
244
- [`.scratch/dsh-version-floor/spec.md`](../../../.scratch/dsh-version-floor/spec.md):真实用户装了
245
- `dsh 0.2.0-rc.2`(当时 npm `latest` 还是它)+ 工作台,工作台照常起得来,**只有 DIY 模式整个不见、
246
- 一句错都不报** —— `preset/diy.patch.yml` 是从 dsh 0.2.1-rc.2 那份 `standard.patch.yml` 派生的,
247
- 里面一行 `tool-schedule` 在 0.2.0 上根本不存在,preset 挂不起来;而 dsh 的模式选择器**只列挂得起来的**
248
- (healthy presets only),于是"工作台 DIY"从用户面前消失。同一代的 `uiWorkspace.startSession()`
249
- 也**不收** `agentPreset`(0.2.1-alpha.3 才收),所以这不是"少一把工具"的降级档,是 DIY 模式整块不可用。
250
-
251
- 因此**声明一个下限**(而不是"谁都能装"):dsh 自 `0.2.1-alpha.4` 起预检**先读 `engines.dsh`**
252
- (声明了它,peer 就被整体忽略;写 `workspace:` 一律判不兼容;`engines` 不是对象或 `engines.dsh`
253
- 不是字符串会让清单不可读并**直接拒绝**),不满足就拒绝安装,
254
- 可用 `dsh plugin --profile workbench allow-version <包@版本> --dsh-version <版本> --accept-risk` 豁免。
255
- 旧到不认识 `engines.dsh` 的 dsh(0.2.0 及更早只读 `peerDependencies`)由**启动器那道硬门禁**兜
256
- (`bin/dsh-workbench`,与"bundle 没被选中"同一档:拒绝启动 + 打印升级命令)。
257
-
258
- ⚠️ **别把下限改写成 `peerDependencies['@deepseek-ai/dsh']`**:pnpm 的 `auto-install-peers`
259
- (v11 实测:连 `peerDependenciesMeta.optional` 的 peer 也照装)会把**整套 dsh**拖进 profile。
260
- 下限只有一处真相(`package.json` 的 `engines.dsh`),启动器读它、不另写版本号 —— selfcheck 有一条判据
261
- 钉着"下限 = `@deepseek-ai/dsh-client-ui-primitives` 那个 pin"。
262
-
263
- 代价要清楚:工作台依赖 dsh 的注入面(`@deepseek-ai/dsh-client-ui-slots`、
264
- `@deepseek-ai/dsh-client-locale`,见 `dsh.client.inject`)与一批**按名字取的服务**
265
- (`pluginManager` / `profileContext` / `dshHomePath` / `tools` / `webServer` / `shell`),
266
- 这些面在 dsh 升级时会动。对方的 dsh 版本和上面验过的差得远时,**先在一台上验一次**再铺开。
267
- 每次升级要对账哪些面、上一次对账的结论,见
268
- [`.scratch/dsh-0.2.1-rc.2/spec.md`](../../../.scratch/dsh-0.2.1-rc.2/spec.md)。
269
-
270
- **devDependency 里的 `@deepseek-ai/dsh-client-ui-primitives` 跟着"验过的 dsh 版本"走**
271
- ([ADR-0028 §4.1](../../../docs/adr/0028-workbench-ui-uses-dsh-primitives.md)):它只出类型,
272
- 但类型比运行时旧一代,编译期就替不了你把关。当前两份都钉 `0.2.1-rc.2`
273
- (`packages/workbench` / `packages/shared`;改完要跑一次 `pnpm install` 更新 `pnpm-lock.yaml`),
274
- **升这一份时要连 `engines.dsh` 一起升**(selfcheck 那条判据把两者钉在一起)。
275
-
276
- > `NPM_REGISTRY` 是**你们自己的**内网 npm 源地址(形如 `https://<源主机>/<路径>/`)。
277
- > 本仓不写它:开源包里不该出现任何内网地址。
278
-
279
- ## 发布(一个脚本,一条命令)
280
-
281
- ```bash
282
- pnpm release # 门禁 → 选版本号 → 构建 → 彩排 → 逐包上传 → git 收尾
283
- pnpm release:dry # 同上,但 publish 只 dry-run:零上传,结束回滚版本号
284
- ```
285
-
286
- `scripts/release.mjs` 是**唯一**的发布入口。发什么、发到哪、谁先谁后,都从各包自述清单读:
287
-
288
- - **发不发** = `packages/*` 一级目录里,非 `private` 且自述了 `publishConfig`(`access` 或 `registry`);
289
- 私有模块必须显式写发布目标这条,由门禁的 `contracts` 判据守着(不许默认落到公网);
290
- - **发到哪** = 包自己的 `publishConfig.registry`(pnpm 在 recursive publish 里逐包取它),没写的
291
- 落到脚本顶部的 `REGISTRY`(npmjs);本仓现在这 4 个包都发公网,命令一律带 `--access public`。
292
- 注意 `inspectTarget` **只查 npmjs** —— "这个版本发出去没有"对发内网源的包看不准;
293
- - **什么顺序** = pnpm 自己拓扑排序(`shared` → `core` → 用到它们的模块);
294
- - **市场什么时候说"有新版本"** = 要**整份发布铺齐**才亮:`core` / `shared` / `module-tools` 三包
295
- 在同一版上都拉得到才算数([ADR-0043](../../../docs/adr/0043-version-prompt-waits-for-the-whole-release.md))。
296
- 所以中断续跑那段窗口里、或镜像还没同步全时,市场**不会**提示更新 —— 铺齐后等 10 分钟,
297
- 或让用户点市场页那颗「重新检查」即可(用户主动的时机一路 `force`,不等缓存);
298
- - **发什么** = pnpm 按各包 `files` / `.gitignore` 打包;**清单里有没有漏文件仍然没人替你核**(见上一章
299
- 「只能自己盯」)—— 现在机器核的只是**内容**:`check:release-artifacts --shipped` 盯着这批产物里
300
- 零夹具。要看清单用 `dsh-wb-module publish`:它只 dry-run、打印清单,不发布。
301
-
302
- 发布必须走 `pnpm publish`(不是 `npm publish`):只有 pnpm 会把 `workspace:*` 改写成真实版本,
303
- `npm pack` / `npm publish` 不会 —— 直接发别人装不上。
304
-
305
- ### 版本同步规则(约定 —— 门禁只守其中一条)
306
-
307
- | 关系 | 规则 |
308
- | --- | --- |
309
- | 模块 ↔ 外壳 | 模块 `peerDependencies['@dsh-workbench/core'] = ^0.1.0`。**`^0.x` 锁次版本**:外壳升到 `0.2.0` 时 `^0.1.0` 不再满足 —— 要把每个模块的范围一起改(或一起升)。门禁只守"peer 依赖存在"(`contracts` 判据),**范围合不合体没人替你算**,靠自觉 |
310
- | `shared` ↔ 外壳/模块 | 依赖写的是 `workspace:*`,发布时会被改写成**当时那个版本号** → `shared` 必须先发;`shared` 升版后,用到它的包要重新发一次才会带上新范围 |
311
- | `module-tools` ↔ `init` 模板 | 模板里 `@dsh-workbench/core` / `module-tools` 的范围要跟着本仓版本走,否则 `init` 生成的是过期范围 —— **现在没有任何判据检查它**(上一版 release 脚本查过,换成现在这份之后这条没了),升外壳次版本时记得一起改 |
312
-
313
- > **`0.2.0` 给模块作者的一处变更**(很小,但旧写法会红):`check:cards` 现在要求"清单说没有卡片"
314
- > 与"产物里登记没登记卡片"**两处说法一致** —— 声明写 `"card": null` 的模块**不要再挂占位卡**
315
- > (`UnitClientFace.card` 自 `0.2.0` 起可选,见 [ADR-0038 §6.20](../docs/adr/0038-module-declaration-protocol.md))。
316
- > 有卡片的模块零迁移。反过来,`card: null` 的模块在**旧外壳/旧工具链**上仍按旧口径被要求交卡片 ⇒
317
- > 模块仓把依赖提到 `^0.2.0` 要与外壳发版**同一个窗口**做(先发 `0.2.0`,模块仓再删那张卡片函数、
318
- > 提范围、跑模块门禁)。
319
-
320
- ### 中断了怎么续跑(发布是可中断的)
321
-
322
- 脚本把现场写进 `.release-state.json`(已进 `.gitignore`)。断了就:
323
-
324
- ```bash
325
- pnpm release:resume # 从现场恢复;**已在源上的包自动跳过**,只发剩下的
326
- ```
327
-
328
- - **不带 `--resume` 重跑会被拒**:目标版本必须高于当前版本;源上若已有同名同版本,脚本会说"先提版本号"。这正是为续跑设计的语义(不是 bug)。
329
- - **`--resume` 照样跑一遍门禁**:断了之后很可能改过包,而门禁整次发布只有 preflight 那一处。真要跳过用 `--skip-gate`(发布不会再补跑一遍)。
330
- - `pnpm release:dry` 是**彩排**:改版本号 → 构建 → `publish --dry-run` → **回滚本地版本号**,不上传。
331
- - 发布**成功之后**才做 git 收尾:`git add -A` → `commit -m "chore: 发布版本 v<ver>"` → `tag v<ver>` → `push` → `push origin v<ver>`。git 收尾失败时包已经发出去了,脚本会把该手打的命令打出来,并提示可以 `pnpm release:resume`。
332
- - 常用旗标:`--target <ver>` 跳出版本选择 · `-y/--yes` 跳过确认 · `--allow-dirty` · `--skip-gate` / `--skip-rehearsal`(都不推荐) · `--skip-build`(用磁盘上现有产物 —— `prepack` 已关,不再兜底重建)。
333
- - 依赖:`enquirer`(交互选择版本)与 `semver`(版本比较)是根上的 devDependencies。
@@ -1,229 +0,0 @@
1
- # 操作
2
-
3
- 装机、起实例、验收、排障。命令本身在 `package.json` 的 `scripts` 里,这里只写**顺序**和**判据**。
4
-
5
- ## 装一次
6
-
7
- 一条命令版(②+④ 都在里面,幂等,可重复跑):
8
-
9
- ```bash
10
- ./scripts/verify.sh --record # ① 记录 dsh web 的基线(越早越好)
11
- ./bin/dsh-workbench setup # ② 建 workbench profile 并把工作台装进 profile
12
- ./bin/dsh-workbench # ③ 起工作台(另开一个终端)
13
- ```
14
-
15
- 想拆开做也行:`./scripts/seed-profile.sh` 只建 profile 骨架(继承 + 隔离层),
16
- 装 bundle 则在**工作台进程里**做 —— `plugin_manager` 只作用于当前 profile:
17
-
18
- > 在工作台的会话里让 agent 执行 `install_bundle target=<本仓库绝对路径>`
19
-
20
- `setup` 与 agent 那条路等价:都走 dsh 的 `runPluginCommand`(兼容性预检、profile 写锁、
21
- 装完把 bundle 登记进清单)。
22
-
23
- **完成判据**:`:3081` 的页面出现工作台的左栏;`:3080` 一切照旧。
24
-
25
- 工作台用的 opencli 是它自己的**隔离运行时**(`~/.dsh/workbench/opencli/toolchain`,版本由
26
- `OPENCLI_PIN` 锁),但它连的 **daemon 是整机一个** —— 见下面「隔离边界」。
27
-
28
- 前提:`dsh` 和 `pnpm` 在 PATH 上(`dsh plugin` 是转发给 pnpm 的),
29
- **`dsh` 不低于验过的那一代**(`engines.dsh`;旧到不认识它的 dsh 由启动器当场拒绝并给
30
- `npm i -g @deepseek-ai/dsh@latest` —— 低于它的 dsh 上 DIY 模式整个不出现且不报错,见
31
- [`.scratch/dsh-version-floor/spec.md`](../../../.scratch/dsh-version-floor/spec.md)),
32
- 且 `~/.dsh/profiles/web` 已存在(跑过一次 `dsh web` 即可)—— seed 拿它当**模板**
33
- (bundle 清单),之后也是「配置同步」的默认来源。
34
-
35
- **给别人用**(两条路:插件页装进他自己的 web / 独立实例 `setup`;包在公网 npm 上)见 [`HANDOFF.md`](HANDOFF.md)。
36
-
37
- ### 变体:装进**当前** profile(在 `dsh web` 里装,不起第二个实例)
38
-
39
- `dsh web` 侧栏 **Plugins → Add plugin** → 填 `@dsh-workbench/core`(公网 npm 上有,默认源即可)
40
- → **Install → Enable now** → **刷新页面**。装的就是 `dsh web` 自己那个 profile。
41
-
42
- **完成判据**:刷新后左栏是工作台的、右侧出现面板列;`$DSH_HOME/workbench/opencli/bin/opencli`
43
- 存在且指向本包 `bin/opencli-isolated`;`$DSH_HOME/workbench/bin/dsh-wb-module` 可执行
44
- (这三条不再依赖启动器 —— 宿主自举写的)。
45
-
46
- 这条路上没有启动器,缺的几件由**宿主自举**补齐(建目录、写隔离 opencli 入口、前插 `PATH`、
47
- 后台建 DIY 工坊骨架),可写根由 bundle 层那一行 `sandbox-policy` 放行 —— 见
48
- [ADR-0037](../../../docs/adr/0037-workbench-installs-into-any-profile.md)。
49
-
50
- 自举是**静默**的:它写了什么只在宿主日志里,后台脚本(工坊骨架 / 依赖)的输出在
51
- `$DSH_HOME/workbench/seed.log`。DIY 那个工坊“怎么都没建起来”时先看这两个地方。
52
-
53
- 差别:**会话 / 存储不隔离**(接着用你原来那份)、**没有独立端口与进程**、
54
- **改外壳源码不热重载**、DIY 工坊依赖是首次启动在后台装的(几百 MB,失败只落日志)。
55
- 此后**每次启动**都会比对工坊里那三个 `@dsh-workbench/*` 与**正在跑的那份外壳**的版本,不同代就在后台**增量同步**
56
- (见下面「工坊工具链怎么跟着外壳走」)。
57
-
58
- 撤销:Plugins 页卸载 `@dsh-workbench/core`(配置与数据不清);`~/.dsh/workbench/**` 想清就手删。
59
-
60
- ## 日常
61
-
62
- | 想做什么 | 怎么做 |
63
- |---|---|
64
- | 开工作台 | `./bin/dsh-workbench` |
65
- | 开发(监听改动 + 起工作台) | `pnpm run dev` —— profile 不对会先 `setup`;`:3081` 已在跑就只监听 |
66
- | 换端口 | `WORKBENCH_PORT=3091 ./bin/dsh-workbench` |
67
- | 改工作台设置 | 在 `:3081` 的 Settings 页改,落在 `profiles/workbench/cordis.patch.yml`。**这是工作台唯一的那份设置文档**(没有别处叠在它上面),所以模型供应商 / 搜索源 / MCP 每一行都改得动 |
68
- | 用 web 那边已经配好的行 | `:3081` → `Meta+,` → 「配置同步」→ 选来源(默认 `web`)→ 勾行 → 同步(`dsh-hmr` 会重组,**不用重启**)。只搬你在那个 profile 里**显式配过**的行;行点名的包没装时页面会说出来(ADR-0044) |
69
- | 改 web 设置 | 在 `:3080` 改,落在 `profiles/web/cordis.patch.yml`。工作台**不会**再自动跟着变 —— 要就跟上面那条同步一次;不想跟就不动 |
70
- | 重建 profile | `mv ~/.dsh/profiles/workbench ~/.dsh/profiles/workbench.bak` 后重跑 seed |
71
- | **别在设置里关掉工作台** | Settings → 插件里 `@dsh-workbench/core` 那个开关**不要关**。关掉=把它从 profile 的 `bundles` 里移除 → 我们那份 bundle patch(含"禁用出厂左栏")**整份不再应用** → 页面退化成**原版 dsh web**(左栏回来、右侧面板消失),**数据一个都不丢**。恢复:进程还在时把开关打开(左栏的「插件」入口也在;进不去设置就按 `Meta+,`);已经重启过的,启动器读到"没选中"会**直接拒绝启动**并给一条修复命令 —— 跑 `./bin/dsh-workbench setup`(幂等,把这一项补回 `dsh.profile.bundles`)即可。实测过这条降级路径,不是推断 |
72
-
73
- ## 开发循环(改完不用重启)
74
-
75
- ```bash
76
- pnpm run dev # 一条命令:监听 src/**(重建产物)+ 起工作台 :3081
77
- ```
78
-
79
- 它会先确认 profile 里装的是**本仓这份**(不对就先跑 `setup`),等**首次构建完成**再起实例,
80
- `:3081` 已经有实例在跑时就只监听、不重复起第二个。只想监听、实例自己另开终端起:
81
-
82
- ```bash
83
- DSH_WB_DEV_NO_LAUNCH=1 pnpm run dev
84
- WORKBENCH_PORT=3091 pnpm run dev # 换端口(监听与实例同一个端口)
85
- ```
86
-
87
- 改 `src/**` → 约 1 秒后界面就是新的,**不用重启、不用刷新**。两条链路都是 dsh 自带的:
88
-
89
- | 半侧 | 链路 | 代价 |
90
- |---|---|---|
91
- | 浏览器 | Vite 重建 `client.js` → `dsh-client-hmr` 每 500ms stat-poll 到 mtime/大小变化 → `/plugins/events` 发 `rebuilt` → 浏览器作废旧模块、预取新包、换 fiber | 插件内的 React 状态会丢;会话 / 工作区 / 连接状态保留 |
92
- | 宿主 | Vite 重建 `dist/host/index.js` → `dsh-hmr` 按模块 realpath 匹配并重载插件 | 插件重建,进程与其它插件不受影响 |
93
-
94
- `cordis.patch.yml`(组合层)与 `package.json` 的 `dsh.profile.bundles` **也在监听里**:`dsh-hmr` 变了就 `recomposeProfile`,
95
- 不用重启(它报 `restartReason` 时才按提示重启)。仍然要重启的是改 `vite.config.ts`。
96
-
97
- 宿主侧监听为什么要环境变量:出厂 `hmr` 的 `root` 默认是 `[]`(不监听),
98
- 而相对路径以 profile 目录为基准 —— 我们的包在 profile 里只是一个指向本仓库的符号链接,
99
- 从 profile 目录出发走不到 `/Volumes/…`,chokidar 收不到事件(实测 `root: ['.']` 无效)。
100
- 所以启动器导出 `DSH_WORKBENCH_ROOT`,补丁里用 `!!js process.env.DSH_WORKBENCH_ROOT ?? '.'`。
101
-
102
- ## 验收
103
-
104
- ```bash
105
- ./scripts/verify.sh
106
- ```
107
-
108
- 五项检查,全部是"外部约束的证据"而不是自证:
109
-
110
- 0. **代码接线** —— 槽位名 / key / id / locale 是否对得上(语法检查抓不到这类错)
111
- 1. **web 是否被动过** —— 与 `--record` 的哈希逐一比对
112
- 2. **组合树** —— `dsh workbench --dump-config`:工作台行在位、出厂 `ui-sidebar` 被禁用、
113
- 启动器里没有 web 设置的叠层(脚本直接判,见 ADR-0044)。两棵树**本来就不该相同**
114
- (工作台不再继承 web 的设置),差异只打出来给人看
115
- 3. **数据是否分家** —— `~/.dsh/sessions` 与 `~/.dsh/workbench/sessions` 各自独立
116
- 4. **隔离行是否在位** —— 三个根行都在 `profiles/workbench/cordis.patch.yml` 里
117
-
118
- 另外两项要 GUI / agent 才能验:
119
-
120
- - **槽位注册**:让 agent 执行 `cordis_inspect_query` → `Slots.listSubTree`,
121
- 看对应 `root` 的 `occupants` 里有没有我们的条目。**这是权威证据**,别靠日志推断。
122
- - **视觉一致性**:亮色 / 暗色各看一次,确认颜色全部来自 `--dsw-*` 令牌。
123
-
124
- ## 隔离边界
125
-
126
- | 资源 | 是否隔离 | 落在哪 |
127
- |---|---|---|
128
- | 会话历史 | ✅ | `~/.dsh/workbench/sessions/` |
129
- | 工作区 / 会话投影缓存 | ✅ | 走 `storage-domain` → `storage-json` 根 |
130
- | 附件 | ✅ | `~/.dsh/workbench/attachments/v1/` |
131
- | 模块配置 | ✅ | `~/.dsh/workbench/workspaces/<工作区>/<模块 id>/`(见 [ADR-0011](../../../docs/adr/0011-module-config-lives-in-its-own-folder.md)) |
132
- | API 凭据 | ❌ **有意共享** | `~/.dsh/.credentials.yaml`,两边同一把 key |
133
- | opencli 运行时(CLI + 插件) | ✅ | `~/.dsh/workbench/opencli/`(`HOME` 指到 `…/home`,见 [ADR-0014](../../../docs/adr/0014-opencli-cost-has-one-owner.md) §2.2) |
134
- | opencli **daemon** | ❌ **整机一个,隔离不了** | 端口 **19825 被扩展写死**(`background.js` 里 `DAEMON_PORT = 19825`;CLI 侧 `OPENCLI_DAEMON_PORT` 设非默认值直接报错)。所以本机的全局 opencli 与工作台的隔离运行时**抢同一个 daemon**:两边版本不一致时,谁的命令后跑谁就把 daemon 重启成自己那版 |
135
- | profile 的 pnpm 源(私有 scope) | ⚠️ **由工作台写进去** | `<profile>/.npmrc` 里的 `@scope:registry=…`,前面带一行 `# dsh-workbench 写的…` 标记。**为什么在工作台这一侧写**:dsh 的卸载没有源参数(`runPnpm(['remove', name])`),pnpm 只能从 profile 的 `.npmrc` 链知道源;不写它,装私有源的模块能成、卸不了。规则与边界见 [ADR-0030](../../../docs/adr/0030-private-registries-live-in-the-profile-pnpm-config.md):只加不改、只写 scope 行、不碰 `~/.npmrc`。**删了会怎样**:下次从这个源装会再写回来;已经装着的那种,点卸载时撞 404 会自愈补回再重试一次 |
136
-
137
- ## 工作台自己怎么升级、出事怎么救
138
-
139
- 工作台那一行的「更新」**不会把自己摘掉**(见 [ADR-0045](../../docs/adr/0045-workbench-self-update-runs-outside-the-workbench.md)):
140
- 点下去之后工作台把现场写到 `$DSH_HOME/workbench/self-update.sh|json|log`,然后起一个**独立进程**去做
141
- `dsh plugin --profile <profile> add @dsh-workbench/core@<版本>` —— 就地换版本,一行都不摘,
142
- 所以它换的就是工作台自己也没关系(换的是外头那个进程)。
143
-
144
- - 成功:`self-update.json` 里 `state: "ok"`,`version` 是新版;**重启一次 dsh** 才用得上(面板会这么说)。
145
- - 换版没成:脚本自己把旧版本装回去,`state: "rolled-back"` —— 工作台不会因此消失。
146
- - 回滚也没成:`state: "failed"`,日志最后一行给出救法。终端敲这一条即可:
147
- `dsh plugin --profile <profile> add @dsh-workbench/core@<版本>`(`<版本>` 取源上最新,或 `latest`)。
148
-
149
- 两种安装形态的差别:
150
-
151
- | 怎么装的 | profile 里那一条 | 市场自更新 |
152
- |---|---|---|
153
- | `npm i -g @dsh-workbench/core` 然后 `dsh-workbench setup` | `@dsh-workbench/core@^x.y.z`(源上) | 能(就是上面那一条) |
154
- | 仓库副本(`pnpm run dev` / `link:` 本目录) | `link:…` | 拒:不把开发副本换成源上那份 |
155
-
156
- 全局那一份**启动器**不会跟着升级(它只是启动器:真正加载的是 profile 里那份)。换 profile 或重建之前,
157
- 先 `npm i -g @dsh-workbench/core@latest`;已经在跑的 profile 走市场那一颗「更新」。
158
-
159
- ### 起不来:`failed to parse overlay …/cordis.patch.yml`(YAMLException: end of the stream …)
160
-
161
- 早期版本 `seed-profile.sh` 会把隔离层直接接到 dsh 新模板的 patch 后面,而那份模板的内容是「横幅注释 +
162
- 一个空数组 `[]`」—— 空数组本身是一个**完整的 YAML 文档**,后面再接序列就解析不了,profile 一条都组不起来。
163
- 现在的脚本追加前会先把那行单独的 `[]` 让位(`selfcheck` 有判据钉着)。如果你手上那份 profile 是旧脚本建出来的,
164
- 手工把 `cordis.patch.yml` 里**单独一行**的 `[]` 删掉再起即可(其余内容不用动)。
165
-
166
- ### 工坊工具链怎么跟着外壳走
167
-
168
- 工坊(`$DSH_HOME/workbench/diy`)里那三个 `@dsh-workbench/*` 必须与**当前跑着的外壳**同代 ——
169
- 不同代就是"指引是新的、判据与模板是旧的",报错会指着你的声明而不是指着版本。
170
-
171
- - **判据**:工坊 `node_modules/@dsh-workbench/{core,module-tools,shared}/package.json` 里**实装**的
172
- `version`,与**基准版本**(见下条)**全等**。不看 `package.json` 里写的范围,也不信 `pnpm` 的退出码 ——
173
- 装完会按盘上那份复核实装版本,只有真的同代才写 `ok`。
174
- - **基准 = 正在跑的那份外壳**,由调用者经 `DSH_WB_SHELL_ROOT` 显式传进来:
175
- 宿主自举给**加载中的那份**(`packageRootOf(import.meta.url)`),启动器给 **profile 里那份**
176
- `node_modules/@dsh-workbench/core`(只在它真能读出 `package.json` 时才给)。**谁都没传 → 这一轮不比对**
177
- (不读工坊实装版本、不起同步、不写 `.sync.json`、不动工坊一个字节)。
178
- ⚠️ **跑脚本的那份副本不算基准**:安装态下启动器与宿主是**两份物理副本**,谁后跑谁说了算会来回漂
179
- —— 现在启动器那份落后**不会再**把工坊往回拽(实测那一次:启动器 0.3.0 / profile 0.3.1 / 工坊已跟到
180
- 0.3.1,旧口径把工坊降级成 0.3.0,见 [ADR-0056](../../../docs/adr/0056-workshop-toolchain-follows-the-shell.md) 补记)。
181
- - **触发**:每次启动都会比一次(`bin/dsh-workbench` 与宿主自举走的那条 `entry`)。
182
- 无参那一趟(建骨架 / 重跑 `setup`)**只有带基准时才比** —— 启动器兜底建骨架时带,`setup` 那趟
183
- **不设基准 → 空转**。所以**重跑 `setup` 不做代际对齐**,对齐发生在**下一次启动**工作台时
184
- (与 README 的"升级要重启一次"一致)。比对是**便宜**的:只读四份 `package.json` 的版本。
185
- - **不一致 → 后台自愈**:起一个 `nohup` 的 `seed-workshop.sh sync`,把工坊根的 `devDependency`
186
- 范围与 `pnpm.overrides` 都对齐到基准那一代,再增量 `pnpm install`,跑完复核实装版本。
187
- 启动路径不等它。
188
- - **状态文件**:`$DSH_HOME/workbench/diy/.sync.json`(`state` / `version` / `at` / `reason`),四种态:
189
- - `ok`:复核过、同代。⚠️ **`ok` 可能是旧版本的记录** —— 判据**永远是实装版本**,
190
- `.sync.json` 只用于退避与说人话,别拿它当"装好了"的证据;
191
- - `syncing`:正在后台同步;超过 15 分钟还停在这,`workbench_diy_status` 会说
192
- 「⚠️ 工坊工具链上一次同步没走完(这条停在 <at>,超过 15 分钟了)」;
193
- - `failed`:**这一轮没修好**(原因在 `reason` 里),`workbench_diy_status` 会直说
194
- 「⚠️ 工坊工具链还是上一代(…;外壳已经是 <版本>):<原因>」并把修法一起给出来;
195
- - 文件不在:还没有同步记录(首次 / 天然同代);**实装版本与外壳同代时不出声**,不同代时显示
196
- 「⏳ 工坊工具链还是上一代(工坊里 <包> <版本>,外壳 <版本>)—— 下一轮工作台启动时会对齐到 <版本>;一直这样就看 `$DSH_HOME/workbench/seed.log`」。
197
- - **去哪看**:日志在 `$DSH_HOME/workbench/seed.log`(每次同步都写:比对的实装版本、开始自愈、
198
- `pnpm install` 的原话、复核结果);状态也可以直接问 agent:`workbench_diy_status`。
199
- - **失败了怎么修**:先照状态里那条命令来 —— `cd "$DSH_HOME/workbench/diy" && pnpm install`;
200
- 还是不行就把工坊目录删掉再重启一次工作台(骨架会自己重建;`packages/` 下你造的模块先留着或另存)。
201
- 两句常见原因:PATH 上没有 `pnpm`(装好后 `cd <工坊> && pnpm install`)、
202
- 工坊根 `package.json` 被手改坏了(修回一份合法 JSON)。
203
- - **离线 / 装不动**:就停在旧一代,但**看得见**(`failed` + `reason` + 修法)。同一个外壳版本上失败过,
204
- 一小时内不再重试(`SYNC_BACKOFF_SECONDS=3600`);外壳换了一代就是新问题,会重新试。
205
- 自愈锁的过期判据是 15 分钟(`SYNC_LOCK_STALE_MINUTES=15`,持锁者崩了不会被永久锁死)。
206
-
207
- ## 排障
208
-
209
- | 症状 | 原因 |
210
- |---|---|
211
- | 页面白屏、只显示 `Failed to load plugins` | 有插件在 `slots.register` 到未声明的槽(抛错即整个 entry 激活失败)。看控制台里我们打的 `[dsh-workbench] apply failed` |
212
- | 面板一直空着、`/workbench/api/issues` 返回 `coding-not-logged-in` | opencli 登录态失效:在终端跑 `opencli coding team-current` 完成登录 |
213
- | 面板显示"这个项目不是 Coding 仓库" | 该工作区的 git `origin` 不指向 `e.coding.net`(或没有 origin) |
214
- | 打开面板/按刷新要等 5–8 秒 | 正常:opencli 每次调用有**固定约 5 秒**开销(起浏览器会话;接口本身只要 ~260ms)。**没有缓存**——数据一律实时,列表 = 缺陷 + 状态目录两次调用(限并发 2,实测约 7 秒) |
215
- | 改了源码没变化 | 忘了 `pnpm run build`,或没刷新页面;改过 `vite.config.ts` 后没重启(正在跑的 watch 用的还是旧配置) |
216
- | 跑门禁/发版时 `check:release-artifacts` 报「判据自己碰了包内产物」,或 `--shipped` 报一份莫名其妙的产物红 | 多半是**假红** —— 见下面「发版那条闸报红的三种假红」 |
217
- | 市场页点「卸载」报 `没成功:… ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/@你的私有 scope/…` 之类 | 这个 profile 里的私有包不在 pnpm 默认源上,而 dsh 的卸载**不带源**。工作台会自己补上 `<profile>/.npmrc` 的 `@scope:registry=` 再重试一次([ADR-0030](../../../docs/adr/0030-private-registries-live-in-the-profile-pnpm-config.md));**还失败**说明那个包没有 scope(一行 scope 行表达不了),这时才需要人/agent 上手:`dsh plugin --profile <p> remove '<包名>' --config.registry=<那个源>`(`pnpm remove` 不认 `--registry=`),或者给 profile 自己配一行默认 `registry=` |
218
- | 体检那屏「检测浏览器通道 · 没连上」,可面板照样能用 | 本机全局 opencli 与隔离运行时版本不一致 ⇒ daemon 被顶成对方那版(`Stale daemon detected: daemon vX != CLI vY`)。下一条浏览器命令会自动把它重启回来,体检也会复检后改回「已连接」([ADR-0024](../../../docs/adr/0024-warm-the-sites-before-the-panel.md) §3.2.1)。**要彻底不抖**:让两边同版本 —— 把 `OPENCLI_PIN`(`src/shared/config/constants.ts`)对齐到 `opencli --version`,再 `npm i --prefix ~/.dsh/workbench/opencli/toolchain @jackwener/opencli@<那个版本> --cache ~/.dsh/workbench/opencli/npm-cache` |
219
- | Settings 页报 `overridden by a home patch` | 想在 Settings 里改的行被 `$DSH_HOME/cordis.patch.yml` 或 `--patch` 覆盖了 |
220
- | `./bin/dsh-workbench` 直接拒绝启动,说 `@dsh-workbench/core 不在 dsh.profile.bundles 里` | 包装着但**没被选中**(在 Settings → 插件里关过;或装到一半:`dsh plugin add` 的 pnpm 那半成功、装完的登记跳过了已存在的依赖)。这一档以前是静默的 —— 起得来、只是侧边栏没有工作台。现在拒绝启动是为了不给你一台"什么都对但没入口"的实例:跑它打印的那条 `./bin/dsh-workbench setup`(幂等,`seed-profile.sh ensure-selected` 会把这一项补回去) |
221
- | **别人机器上** `npx @dsh-workbench/core setup` 报 `scripts/seed-profile.sh: Permission denied`(或体检永远说"隔离运行时没装好") | 发出去的包里**随包脚本丢了 x 位**:`pnpm publish` 只给 `package.json#bin` 点名那条文件 755,其余一律 644(`npm pack` 会保留,而 `git` 里的 x 位不进 tarball —— 所以**本机永远复现不出来**)。0.1.6 的已知缺陷:应急 `chmod +x <包目录>/{scripts/seed-profile.sh,bin/opencli-isolated}`,正规解是升到 0.1.7(启动器与 `selfcheck` 已改成不吃 x 位) |
222
-
223
- **发版那条闸(`check:release-artifacts`)报红的三种假红**:
224
-
225
- | 来路 | 看到什么 |
226
- |---|---|
227
- | 环境里带着 `WB_STAGE_DIR`(判据内部的暂存开关) | 发布构建落进临时根、包内那份没动,而 `--shipped` 审的正是包内那份 ⇒ 假红。发版构建会**当场忽略它**并打一行说明;发版环境里别带它 |
228
- | `pnpm run dev` 的 watcher 在审计中间重写产物 | 发版脚本点名「**被别的进程重写**」(审计前后各记一次指纹,对不上就是它);门禁那条会说"判据自己碰了包内产物"。先停 watcher 再跑 |
229
- | 在开发树上直接跑 `--shipped` | 本来就红(包内那份是开发档):`--shipped` 的前提是**刚跑过发布构建**,判据会自述这一档。自查用默认档 —— 它建到临时根、不碰开发产物 |