@dsh-workbench/core 0.2.7 → 0.2.8

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.
package/README.md CHANGED
@@ -91,7 +91,9 @@ npx @dsh-workbench/core # ② 起工作台,默认 :3081
91
91
  再打开这一页拉一次即可。见 [ADR-0044](../../docs/adr/0044-config-sync-replaces-the-web-patch-overlay.md)。
92
92
  - ⚠️ **别在 Settings → 插件里把 `@dsh-workbench/core` 关掉**:关掉 = 它那份 bundle patch 整份不再应用
93
93
  (包括里面的"禁用出厂左栏"),界面会**退化成原版 dsh web**(左栏回来、右侧面板消失)。
94
- **数据一个都不丢**;把开关拨回来就恢复 —— 进不去设置页就按 `Meta+,`。别当成故障去重建 profile。
94
+ **数据一个都不丢**;把开关拨回来就恢复 —— 进不去设置页就按 `Meta+,`。进程已经退过的话,
95
+ 启动器读到"bundle 没被选中"会直接拒绝启动并给一条命令:`./bin/dsh-workbench setup`(幂等,
96
+ 把这一项补回 `dsh.profile.bundles`)。别当成故障去重建 profile。
95
97
 
96
98
  ## 常见问题
97
99
 
@@ -102,7 +104,7 @@ npx @dsh-workbench/core # ② 起工作台,默认 :3081
102
104
  | 面板一直空着 | 多半是没登录 Coding(见上);或这个工作区的 git `origin` 不指向 `e.coding.net` |
103
105
  | 打开面板 / 按刷新要等 5–8 秒 | 正常:opencli 每次调用有**固定约 5 秒**开销(起浏览器会话)。**没有缓存**,数据一律实时 |
104
106
  | 体检那屏说「检测浏览器通道 · 没连上」,可面板照样能用 | 本机全局 opencli 与隔离运行时版本不一致,daemon 被顶成对方那版;下一条浏览器命令会自动把它重启回来,体检复检后改回「已连接」 |
105
- | 一切正常,但左栏是出厂样子、右边没有面板 | 工作台插件在 Settings 里被关了(见上一条 ⚠️) |
107
+ | 一切正常,但左栏是出厂样子、右边没有面板 | 工作台插件在 Settings 里被关了(见上一条 ⚠️)。**重启之后**启动器不会再让你拿到这种实例:它读到 `dsh.profile.bundles` 里没有它就直接拒绝启动,并打印 `./bin/dsh-workbench setup` |
106
108
  | 模型列表里只有出厂的几个、没有自己配的供应商 | 新装的 profile 不带设置(ADR-0044):设置 → 「配置同步」→ 选来源 → 勾 `llm-` 那几行 → 同步(即生效)|
107
109
  | 「配置同步」里某一行说 `@x 还没装` | 那一行点名的包不在这个 profile 的 `node_modules` 里:`dsh plugin --profile workbench add @x`(或在模块市场装),(装完 dsh 会自行重组;它报 `restartReason` 时才重启)|
108
110
 
package/bin/dsh-workbench CHANGED
@@ -75,6 +75,13 @@ run_setup() {
75
75
 
76
76
  dsh plugin --profile "$PROFILE" add "$SPEC"
77
77
 
78
+ # 装包与"被选中"是 dsh 那侧的两件事:`dsh plugin add` 的 pnpm 那半管装,
79
+ # 而登记 `dsh.profile.bundles` 是它装完后的对账,那条对账**跳过已存在的依赖** ——
80
+ # 依赖已经在清单里时(重跑 setup、上次装到一半),bundle 名单永远缺那一项。
81
+ # 所以装完再确保选中一遍:缺了就补进 `dsh.profile.bundles`,相等就一个字节不写。
82
+ # 启动器现在拿"选中"当硬门禁(没选中就拒绝启动),这一步就是那条修复命令的落点。
83
+ bash "$REPO_DIR/scripts/seed-profile.sh" ensure-selected
84
+
78
85
  cat <<EOF
79
86
 
80
87
  装好了。起工作台:
@@ -107,10 +114,39 @@ if [ ! -f "$PROFILE_DIR/package.json" ]; then
107
114
  exit 1
108
115
  fi
109
116
 
110
- # 软提醒:bundle 没装的话面板不会出现,但服务照常起来。
111
- if ! grep -q "\"$PKG\"" "$PROFILE_DIR/package.json"; then
112
- echo "dsh-workbench: 提醒 —— 工作台 bundle 还没装进 profile '$PROFILE'," >&2
113
- echo " 侧边栏不会出现「工作台」入口。运行: $REPO_DIR/bin/dsh-workbench setup" >&2
117
+ # ── 硬门禁:工作台 bundle 必须**被选中**(在 `dsh.profile.bundles` 里)─────────
118
+ #
119
+ # 判据只能是 `dsh.profile.bundles`,**不能** grep 整个 package.json:依赖装着 ≠ 被选中。
120
+ # 只有 dependencies 而没有 bundles 时,profile 照常组起来、服务照常监听,只是
121
+ # 侧边栏什么也不出现 —— 用户拿到的是一台"什么都对、就是没有工作台"的实例,
122
+ # 而且没有任何报错。所以这里读到没选中就**拒绝启动**,并给一条能照抄的修复命令。
123
+ #
124
+ # 这条断法是真会发生的:依赖已经在清单里时(重跑 setup、上次装到一半),
125
+ # `dsh plugin add` 的 pnpm 那半只回一句"已是最新",而它装完的登记跳过**已存在**的
126
+ # 依赖,于是 bundle 名单永远缺那一项。setup 为此多了一步"确保选中"
127
+ # (`scripts/seed-profile.sh ensure-selected`),这里给的就是回到那一步的命令。
128
+ BUNDLE_GUARD=0
129
+ WB_PROFILE_MANIFEST="$PROFILE_DIR/package.json" WB_PKG="$PKG" node -e '
130
+ const fs = require("node:fs");
131
+ const manifestPath = process.env.WB_PROFILE_MANIFEST;
132
+ let profile = {};
133
+ try {
134
+ profile = (JSON.parse(fs.readFileSync(manifestPath, "utf8")).dsh || {}).profile || {};
135
+ } catch (error) {
136
+ console.error("dsh-workbench: 读不了 profile 清单 " + manifestPath + ":" + error.message);
137
+ process.exit(2);
138
+ }
139
+ const bundles = Array.isArray(profile.bundles) ? profile.bundles : [];
140
+ if (!bundles.includes(process.env.WB_PKG)) process.exit(1);
141
+ ' || BUNDLE_GUARD=$?
142
+
143
+ if [ "$BUNDLE_GUARD" -ne 0 ]; then
144
+ if [ "$BUNDLE_GUARD" -eq 1 ]; then
145
+ echo "dsh-workbench: profile '$PROFILE' 没选中工作台 bundle($PKG 不在 dsh.profile.bundles 里)。" >&2
146
+ echo " 这样起只会得到一台没有工作台入口的实例,所以这里直接拒绝启动。" >&2
147
+ fi
148
+ echo " 修好它(幂等,会补上这一项): $REPO_DIR/bin/dsh-workbench setup" >&2
149
+ exit 1
114
150
  fi
115
151
 
116
152
  # 清掉从父 dsh 会话继承来的身份变量。
package/docs/HANDOFF.md CHANGED
@@ -77,7 +77,9 @@ npx @dsh-workbench/core
77
77
  Settings → 插件里把 `@dsh-workbench/core` 关掉后,工作台会**退化成原版 dsh web**:
78
78
  左栏回到出厂样式、右侧数据面板消失。这是**设计内的降级**(关掉 = 它那份 bundle patch
79
79
  整份不再应用,连里面的"禁用出厂左栏"也一并失效),**会话 / 面板配置 / 工作区数据都不丢**。
80
- 恢复:插件页把开关拨回来(或按 `Meta+,` 打开设置)。别把它当成故障去重建 profile。
80
+ 恢复:插件页把开关拨回来(或按 `Meta+,` 打开设置);**进程已经退了的**,启动器读到
81
+ "bundle 没被选中"会直接拒绝启动并打印修复命令 —— 跑 `./bin/dsh-workbench setup` 补回来。
82
+ 别把它当成故障去重建 profile。
81
83
 
82
84
  顺带一条:即使关掉工作台,profile 里剩下那层差异(会话/存储/附件隔离)**仍然生效**
83
85
  —— 那是无害且想要的(数据继续分开)。另外启动器每次都会叠一层 `sandbox-policy`,把执行缝的
@@ -91,7 +93,12 @@ Settings → 插件里把 `@dsh-workbench/core` 关掉后,工作台会**退化
91
93
  2. 继承 web 的 **bundle 清单**(装没装东西 —— 安装范畴,走 `dsh plugin` 通路);
92
94
  3. 追加**数据隔离层**(会话 / 存储 / 附件各走 `~/.dsh/workbench/**`);
93
95
  4. `dsh plugin --profile workbench add @dsh-workbench/core` —— 走 **dsh 自己的**
94
- 安装通路(兼容性预检、profile 写锁、装完自动把 bundle 登记进 profile 清单)。
96
+ 安装通路(兼容性预检、profile 写锁、装完自动把 bundle 登记进 profile 清单);
97
+ 5. `./scripts/seed-profile.sh ensure-selected` —— **确保选中**:读 `dsh.profile.bundles`,
98
+ 缺 `@dsh-workbench/core` 就补进去(等价于 dsh 的 `selectBundle(name, true)`,已经在里面
99
+ 就一个字节不写)。为什么单列这一步:`dsh plugin add` 装完的登记**跳过已存在的依赖**,
100
+ 所以重跑 setup(包已在清单里)时它不会补 bundle —— 那一档是静默的(起得来、侧边栏
101
+ 没入口),现在启动器拿"选中"当硬门禁,这一步就是它给的修复命令。
95
102
 
96
103
  **设置不在这一步继承**(这里以前快照 web 的 patch、并继承被它点名的依赖):新机器上
97
104
  `setup` 完的工作台**没有模型供应商**,第一次打开去 设置 → 「配置同步」拉一次即可 ——
@@ -63,7 +63,7 @@
63
63
  | 用 web 那边已经配好的行 | `:3081` → `Meta+,` → 「配置同步」→ 选来源(默认 `web`)→ 勾行 → 同步(`dsh-hmr` 会重组,**不用重启**)。只搬你在那个 profile 里**显式配过**的行;行点名的包没装时页面会说出来(ADR-0044) |
64
64
  | 改 web 设置 | 在 `:3080` 改,落在 `profiles/web/cordis.patch.yml`。工作台**不会**再自动跟着变 —— 要就跟上面那条同步一次;不想跟就不动 |
65
65
  | 重建 profile | `mv ~/.dsh/profiles/workbench ~/.dsh/profiles/workbench.bak` 后重跑 seed |
66
- | **别在设置里关掉工作台** | Settings → 插件里 `@dsh-workbench/core` 那个开关**不要关**。关掉=把它从 profile 的 `bundles` 里移除 → 我们那份 bundle patch(含"禁用出厂左栏")**整份不再应用** → 页面退化成**原版 dsh web**(左栏回来、右侧面板消失),**数据一个都不丢**。恢复:插件页把开关打开(左栏的「插件」入口也在;进不去设置就按 `Meta+,`)。实测过这条降级路径,不是推断 |
66
+ | **别在设置里关掉工作台** | Settings → 插件里 `@dsh-workbench/core` 那个开关**不要关**。关掉=把它从 profile 的 `bundles` 里移除 → 我们那份 bundle patch(含"禁用出厂左栏")**整份不再应用** → 页面退化成**原版 dsh web**(左栏回来、右侧面板消失),**数据一个都不丢**。恢复:进程还在时把开关打开(左栏的「插件」入口也在;进不去设置就按 `Meta+,`);已经重启过的,启动器读到"没选中"会**直接拒绝启动**并给一条修复命令 —— 跑 `./bin/dsh-workbench setup`(幂等,把这一项补回 `dsh.profile.bundles`)即可。实测过这条降级路径,不是推断 |
67
67
 
68
68
  ## 开发循环(改完不用重启)
69
69
 
@@ -170,4 +170,5 @@ WORKBENCH_PORT=3091 pnpm run dev # 换端口(监听与实例同一个端口
170
170
  | 市场页点「卸载」报 `没成功:… 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=` |
171
171
  | 体检那屏「检测浏览器通道 · 没连上」,可面板照样能用 | 本机全局 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` |
172
172
  | Settings 页报 `overridden by a home patch` | 想在 Settings 里改的行被 `$DSH_HOME/cordis.patch.yml` 或 `--patch` 覆盖了 |
173
+ | `./bin/dsh-workbench` 直接拒绝启动,说 `@dsh-workbench/core 不在 dsh.profile.bundles 里` | 包装着但**没被选中**(在 Settings → 插件里关过;或装到一半:`dsh plugin add` 的 pnpm 那半成功、装完的登记跳过了已存在的依赖)。这一档以前是静默的 —— 起得来、只是侧边栏没有工作台。现在拒绝启动是为了不给你一台"什么都对但没入口"的实例:跑它打印的那条 `./bin/dsh-workbench setup`(幂等,`seed-profile.sh ensure-selected` 会把这一项补回去) |
173
174
  | **别人机器上** `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 位) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "个人工作台:Agent 工作台外壳(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -87,7 +87,7 @@
87
87
  "access": "public"
88
88
  },
89
89
  "dependencies": {
90
- "@dsh-workbench/shared": "0.2.7"
90
+ "@dsh-workbench/shared": "0.2.8"
91
91
  },
92
92
  "scripts": {
93
93
  "build": "node scripts/build.mjs && node scripts/build-types.mjs",
@@ -15,6 +15,9 @@
15
15
  # 用法:
16
16
  # ./scripts/seed-profile.sh
17
17
  # WORKBENCH_PROFILE=wb2 ./scripts/seed-profile.sh # 建到别的 profile 名下(试装用)
18
+ # ./scripts/seed-profile.sh ensure-selected # 只做"确保选中"这一步:
19
+ # # 把工作台 bundle 补进 dsh.profile.bundles
20
+ # # (setup 在 `dsh plugin add` **之后**调它)
18
21
 
19
22
  set -euo pipefail
20
23
 
@@ -22,6 +25,7 @@ DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
22
25
  PROFILE="${WORKBENCH_PROFILE:-workbench}"
23
26
  SRC_PROFILE="$DSH_HOME/profiles/web"
24
27
  DST_PROFILE="$DSH_HOME/profiles/$PROFILE"
28
+ PKG='@dsh-workbench/core'
25
29
  REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
26
30
  GIT_TOP="$(git -C "$REPO_DIR" rev-parse --show-toplevel 2>/dev/null || true)"
27
31
  ISOLATION="$REPO_DIR/profile/isolation.patch.yml"
@@ -36,6 +40,53 @@ die() {
36
40
  exit 1
37
41
  }
38
42
 
43
+ # ── 确保工作台 bundle 被**选中**(`ensure-selected` 子命令,幂等)──────────────
44
+ #
45
+ # 为什么单独一步、还要排在 `dsh plugin add` **之后**:装包与"选中"在 dsh 那侧是两件事。
46
+ # `dsh plugin add` 的 pnpm 那半把包装进 profile;登记 `dsh.profile.bundles` 是它装完后的
47
+ # 对账,而那条对账**跳过已存在的依赖** —— 所以依赖已经在清单里时(重跑 setup、上次装到
48
+ # 一半),bundle 名单永远缺那一项。症状是静默的:profile 照常组起来、服务照常监听,
49
+ # 只是侧边栏什么也不出现。启动器现在拿"选中"当硬门禁(拒绝启动 + 打印修复命令),
50
+ # 这一步就是那条修复命令的落点。
51
+ #
52
+ # 与 dsh 自己的 `selectBundle(name, true)` 等价:已经在清单里就一个字节都不写;
53
+ # 补之前先确认包真的装着(`dsh plugin add` 落了 dependencies)—— 把没装的包塞进
54
+ # bundles 只会让 profile 组不起来,比"面板不出现"更糟。
55
+ ensure_selected() {
56
+ local manifest="$DST_PROFILE/package.json"
57
+ [ -f "$manifest" ] ||
58
+ die "找不到 $manifest —— 先跑一次 '$REPO_DIR/bin/dsh-workbench setup'。"
59
+
60
+ WB_MANIFEST="$manifest" WB_PKG="$PKG" node -e '
61
+ const fs = require("node:fs");
62
+ const manifestPath = process.env.WB_MANIFEST;
63
+ const pkg = process.env.WB_PKG;
64
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
65
+ const dependencies = manifest.dependencies || {};
66
+ if (!Object.hasOwn(dependencies, pkg)) {
67
+ console.error("seed-profile: dependencies 里没有 " + pkg + " —— dsh plugin add 那一步没成,先重装一遍。");
68
+ process.exit(1);
69
+ }
70
+ const profile = (manifest.dsh || {}).profile || {};
71
+ const bundles = Array.isArray(profile.bundles) ? profile.bundles : [];
72
+ if (bundles.includes(pkg)) {
73
+ console.log("→ 工作台 bundle 已选中: " + pkg);
74
+ process.exit(0);
75
+ }
76
+ manifest.dsh = Object.assign({}, manifest.dsh, {
77
+ profile: Object.assign({}, profile, { bundles: bundles.concat([pkg]) }),
78
+ });
79
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
80
+ console.log("→ 已把工作台 bundle 补进 dsh.profile.bundles: " + pkg);
81
+ ' ||
82
+ die "'$PKG' 没被选中,也没能补上(原因见上一行)。"
83
+ }
84
+
85
+ if [ "${1:-}" = "ensure-selected" ]; then
86
+ ensure_selected
87
+ exit 0
88
+ fi
89
+
39
90
  [ -f "$SRC_PROFILE/package.json" ] ||
40
91
  die "找不到 $SRC_PROFILE —— 请先跑一次 'dsh web',让 web profile 落地。"
41
92
  [ -f "$ISOLATION" ] || die "找不到 $ISOLATION"
@@ -114,7 +165,7 @@ bash "$REPO_DIR/scripts/seed-workshop.sh"
114
165
  if [ -n "$GIT_TOP" ]; then
115
166
  BUNDLE_TARGET="$REPO_DIR"
116
167
  else
117
- BUNDLE_TARGET="@dsh-workbench/core@^$(node -p "require('$REPO_DIR/package.json').version" 2>/dev/null || echo 0)"
168
+ BUNDLE_TARGET="$PKG@^$(node -p "require('$REPO_DIR/package.json').version" 2>/dev/null || echo 0)"
118
169
  fi
119
170
 
120
171
  cat <<EOF