@dsh-workbench/core 1.1.6 → 1.1.7

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/client.js CHANGED
@@ -2809,6 +2809,15 @@ window.__ModuleLoader__.load({
2809
2809
  }
2810
2810
  return errors;
2811
2811
  }
2812
+ function libRunsProblemsOf(raw, at) {
2813
+ if (raw === void 0) return [];
2814
+ if (!Array.isArray(raw)) return [`${at}.runs 要是数组`];
2815
+ if (raw.length === 0) return [`${at}.runs 是空数组 —— 不自己跑命令就整条删掉`];
2816
+ const names = raw.map((one) => text(one));
2817
+ if (names.some((one) => one === "")) return [`${at}.runs 里有空字符串`];
2818
+ if (new Set(names).size !== names.length) return [`${at}.runs 里有重复的运行时`];
2819
+ return [];
2820
+ }
2812
2821
  function perKindProblemsOf(kind, raw, at) {
2813
2822
  const errors = [];
2814
2823
  if (kind === "bin" || kind === "lib" || kind === "env") {
@@ -2816,6 +2825,7 @@ window.__ModuleLoader__.load({
2816
2825
  }
2817
2826
  if (kind === "bin") errors.push(...binInstallProblemsOf(raw["install"], at));
2818
2827
  if (raw["docs"] !== void 0 && text(raw["docs"]) === "") errors.push(`${at}.docs 是空串`);
2828
+ if (kind === "lib") errors.push(...libRunsProblemsOf(raw["runs"], at));
2819
2829
  if (kind === "extension") {
2820
2830
  if (text(raw["runtime"]) === "") errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
2821
2831
  if (text(raw["name"]) === "") errors.push(`${at}.name 没写`);
@@ -2802,6 +2802,15 @@ function binInstallProblemsOf(raw, at) {
2802
2802
  }
2803
2803
  return errors;
2804
2804
  }
2805
+ function libRunsProblemsOf(raw, at) {
2806
+ if (raw === void 0) return [];
2807
+ if (!Array.isArray(raw)) return [`${at}.runs 要是数组`];
2808
+ if (raw.length === 0) return [`${at}.runs 是空数组 —— 不自己跑命令就整条删掉`];
2809
+ const names = raw.map((one) => text(one));
2810
+ if (names.some((one) => one === "")) return [`${at}.runs 里有空字符串`];
2811
+ if (new Set(names).size !== names.length) return [`${at}.runs 里有重复的运行时`];
2812
+ return [];
2813
+ }
2805
2814
  function perKindProblemsOf(kind, raw, at) {
2806
2815
  const errors = [];
2807
2816
  if (kind === "bin" || kind === "lib" || kind === "env") {
@@ -2809,6 +2818,7 @@ function perKindProblemsOf(kind, raw, at) {
2809
2818
  }
2810
2819
  if (kind === "bin") errors.push(...binInstallProblemsOf(raw["install"], at));
2811
2820
  if (raw["docs"] !== void 0 && text(raw["docs"]) === "") errors.push(`${at}.docs 是空串`);
2821
+ if (kind === "lib") errors.push(...libRunsProblemsOf(raw["runs"], at));
2812
2822
  if (kind === "extension") {
2813
2823
  if (text(raw["runtime"]) === "") errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
2814
2824
  if (text(raw["name"]) === "") errors.push(`${at}.name 没写`);
@@ -2805,6 +2805,15 @@ function binInstallProblemsOf(raw, at) {
2805
2805
  }
2806
2806
  return errors;
2807
2807
  }
2808
+ function libRunsProblemsOf(raw, at) {
2809
+ if (raw === void 0) return [];
2810
+ if (!Array.isArray(raw)) return [`${at}.runs 要是数组`];
2811
+ if (raw.length === 0) return [`${at}.runs 是空数组 —— 不自己跑命令就整条删掉`];
2812
+ const names = raw.map((one) => text(one));
2813
+ if (names.some((one) => one === "")) return [`${at}.runs 里有空字符串`];
2814
+ if (new Set(names).size !== names.length) return [`${at}.runs 里有重复的运行时`];
2815
+ return [];
2816
+ }
2808
2817
  function perKindProblemsOf(kind, raw, at) {
2809
2818
  const errors = [];
2810
2819
  if (kind === "bin" || kind === "lib" || kind === "env") {
@@ -2812,6 +2821,7 @@ function perKindProblemsOf(kind, raw, at) {
2812
2821
  }
2813
2822
  if (kind === "bin") errors.push(...binInstallProblemsOf(raw["install"], at));
2814
2823
  if (raw["docs"] !== void 0 && text(raw["docs"]) === "") errors.push(`${at}.docs 是空串`);
2824
+ if (kind === "lib") errors.push(...libRunsProblemsOf(raw["runs"], at));
2815
2825
  if (kind === "extension") {
2816
2826
  if (text(raw["runtime"]) === "") errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
2817
2827
  if (text(raw["name"]) === "") errors.push(`${at}.name 没写`);
@@ -144,7 +144,8 @@ var NEED_KEYS = [
144
144
  "sources",
145
145
  "secret",
146
146
  "install",
147
- "docs"
147
+ "docs",
148
+ "runs"
148
149
  ];
149
150
  var SOURCE_KEYS = [
150
151
  "kind",
@@ -251,6 +252,15 @@ function binInstallProblemsOf(raw, at) {
251
252
  }
252
253
  return errors;
253
254
  }
255
+ function libRunsProblemsOf(raw, at) {
256
+ if (raw === void 0) return [];
257
+ if (!Array.isArray(raw)) return [`${at}.runs 要是数组`];
258
+ if (raw.length === 0) return [`${at}.runs 是空数组 —— 不自己跑命令就整条删掉`];
259
+ const names = raw.map((one) => text$3(one));
260
+ if (names.some((one) => one === "")) return [`${at}.runs 里有空字符串`];
261
+ if (new Set(names).size !== names.length) return [`${at}.runs 里有重复的运行时`];
262
+ return [];
263
+ }
254
264
  function perKindProblemsOf(kind, raw, at) {
255
265
  const errors = [];
256
266
  if (kind === "bin" || kind === "lib" || kind === "env") {
@@ -258,6 +268,7 @@ function perKindProblemsOf(kind, raw, at) {
258
268
  }
259
269
  if (kind === "bin") errors.push(...binInstallProblemsOf(raw["install"], at));
260
270
  if (raw["docs"] !== void 0 && text$3(raw["docs"]) === "") errors.push(`${at}.docs 是空串`);
271
+ if (kind === "lib") errors.push(...libRunsProblemsOf(raw["runs"], at));
261
272
  if (kind === "extension") {
262
273
  if (text$3(raw["runtime"]) === "") errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
263
274
  if (text$3(raw["name"]) === "") errors.push(`${at}.name 没写`);
@@ -1134,7 +1145,7 @@ var PHASES = [
1134
1145
  function asRecord$7(value) {
1135
1146
  return typeof value === "object" && value !== null ? value : void 0;
1136
1147
  }
1137
- function textOf$5(value, max) {
1148
+ function textOf$6(value, max) {
1138
1149
  if (typeof value !== "string") return "";
1139
1150
  const trimmed = value.trim();
1140
1151
  return trimmed.length > max ? trimmed.slice(0, max) : trimmed;
@@ -1147,8 +1158,8 @@ function atOf(value) {
1147
1158
  function errorOf(raw) {
1148
1159
  const record = asRecord$7(raw);
1149
1160
  if (record === void 0) return void 0;
1150
- const code = textOf$5(record["code"], 120);
1151
- const message = textOf$5(record["message"], PANEL_STATE_MAX_TEXT);
1161
+ const code = textOf$6(record["code"], 120);
1162
+ const message = textOf$6(record["message"], PANEL_STATE_MAX_TEXT);
1152
1163
  if (code === "" && message === "") return void 0;
1153
1164
  return {
1154
1165
  code: code === "" ? "unknown" : code,
@@ -1167,7 +1178,7 @@ function parseCardsReport(payload) {
1167
1178
  ok: false,
1168
1179
  message: "请求体要是一个对象"
1169
1180
  };
1170
- const workspace = textOf$5(record["workspace"], PANEL_STATE_MAX_TEXT);
1181
+ const workspace = textOf$6(record["workspace"], PANEL_STATE_MAX_TEXT);
1171
1182
  if (workspace === "") return {
1172
1183
  ok: false,
1173
1184
  message: "workspace 不能是空的 —— 面板按工作区存"
@@ -1181,9 +1192,9 @@ function parseCardsReport(payload) {
1181
1192
  for (const item of raw.slice(0, 32)) {
1182
1193
  const card = asRecord$7(item);
1183
1194
  if (card === void 0) continue;
1184
- const unit = textOf$5(card["unit"], 200);
1185
- const route = textOf$5(card["route"], 400);
1186
- const phase = textOf$5(card["phase"], 20);
1195
+ const unit = textOf$6(card["unit"], 200);
1196
+ const route = textOf$6(card["route"], 400);
1197
+ const phase = textOf$6(card["phase"], 20);
1187
1198
  const at = atOf(card["at"]);
1188
1199
  if (unit === "" || route === "" || at === null || !PHASES.includes(phase)) continue;
1189
1200
  const error = errorOf(card["error"]);
@@ -1218,9 +1229,9 @@ function parseCrashReport(payload) {
1218
1229
  ok: false,
1219
1230
  message: "请求体要是一个对象"
1220
1231
  };
1221
- const workspace = textOf$5(record["workspace"], PANEL_STATE_MAX_TEXT);
1222
- const unit = textOf$5(record["unit"], 200);
1223
- const reason = textOf$5(record["reason"], PANEL_STATE_MAX_TEXT);
1232
+ const workspace = textOf$6(record["workspace"], PANEL_STATE_MAX_TEXT);
1233
+ const unit = textOf$6(record["unit"], 200);
1234
+ const reason = textOf$6(record["reason"], PANEL_STATE_MAX_TEXT);
1224
1235
  if (workspace === "") return {
1225
1236
  ok: false,
1226
1237
  message: "workspace 不能是空的"
@@ -2033,7 +2044,7 @@ var PLUGIN_LEDGER_FILE = "plugins.json";
2033
2044
  function ledgerKeyOf(entry) {
2034
2045
  return `${entry.opencliHome}\u0000${entry.package}`;
2035
2046
  }
2036
- function textOf$4(value) {
2047
+ function textOf$5(value) {
2037
2048
  if (typeof value !== "string") return void 0;
2038
2049
  const trimmed = value.trim();
2039
2050
  return trimmed === "" ? void 0 : trimmed;
@@ -2041,13 +2052,13 @@ function textOf$4(value) {
2041
2052
  function entryOf$3(value) {
2042
2053
  if (typeof value !== "object" || value === null) return void 0;
2043
2054
  const box = value;
2044
- const opencliHome = textOf$4(box["opencliHome"]);
2045
- const pkg = textOf$4(box["package"]);
2046
- const installedAt = textOf$4(box["installedAt"]);
2055
+ const opencliHome = textOf$5(box["opencliHome"]);
2056
+ const pkg = textOf$5(box["package"]);
2057
+ const installedAt = textOf$5(box["installedAt"]);
2047
2058
  if (opencliHome === void 0 || pkg === void 0 || installedAt === void 0) return void 0;
2048
- const name = textOf$4(box["name"]);
2049
- const registry = textOf$4(box["registry"]);
2050
- const releasedAt = textOf$4(box["releasedAt"]);
2059
+ const name = textOf$5(box["name"]);
2060
+ const registry = textOf$5(box["registry"]);
2061
+ const releasedAt = textOf$5(box["releasedAt"]);
2051
2062
  return {
2052
2063
  opencliHome,
2053
2064
  package: pkg,
@@ -2350,6 +2361,93 @@ function sessionIdOf(exec) {
2350
2361
  return typeof id === "string" && id.trim() !== "" ? id.trim() : null;
2351
2362
  }
2352
2363
  //#endregion
2364
+ //#region ../shared/dist/host/opencli-runner.js
2365
+ var AUTH_REQUIRED_EXIT_CODE = 77;
2366
+ function textOf$4(value) {
2367
+ return typeof value === "string" ? value : "";
2368
+ }
2369
+ function failedOf(args, error) {
2370
+ const head = `Command failed: opencli ${args.join(" ")}`;
2371
+ const auth = error?.pageCode === "auth-required";
2372
+ const helpUrl = textOf$4(error?.helpUrl);
2373
+ const detail = textOf$4(error?.detail);
2374
+ const hint = textOf$4(error?.hint);
2375
+ const failed = new Error([
2376
+ head,
2377
+ ...auth ? ["code: AUTH_REQUIRED", `message: ${textOf$4(error?.message)}`] : [textOf$4(error?.message) || "opencli 这条命令没跑通"],
2378
+ ...detail === "" ? [] : [detail],
2379
+ ...hint === "" ? [] : [hint],
2380
+ ...auth && helpUrl !== "" ? [`help: Please open Chrome or Chromium and log in to ${helpUrl}`] : []
2381
+ ].join("\n"));
2382
+ if (auth) failed.code = AUTH_REQUIRED_EXIT_CODE;
2383
+ return failed;
2384
+ }
2385
+ /**
2386
+ * 把读缝包成一条 opencli 命令的执行器:给还按 `execFile('opencli', args)` 收结果的编排库用,
2387
+ * 让它们跑在面板用的那份 opencli 上(同一套插件、同一份登录态)。
2388
+ *
2389
+ * 失败按 opencli 自己的约定还原:未登录是 `code: AUTH_REQUIRED` + 退出码 77,编排库的
2390
+ * 「等登录再重试」才接得上;其它失败保留 `Command failed: opencli …` 那句外壳。
2391
+ * @param read 模块手里的读缝(`data.read`)。
2392
+ * @param options `timeoutMs` 透传给读缝;不传就用读缝自己的默认值。
2393
+ * @returns 执行器;直接交给编排库的 `setOpencliRunner`。
2394
+ */
2395
+ function opencliRunnerOf(read, options = {}) {
2396
+ return async (args) => {
2397
+ const result = await read({
2398
+ kind: "command",
2399
+ adapter: "opencli",
2400
+ argv: [...args],
2401
+ ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
2402
+ });
2403
+ if (result.ok) return { stdout: JSON.stringify(result.data) };
2404
+ throw failedOf(args, result.error);
2405
+ };
2406
+ }
2407
+ //#endregion
2408
+ //#region ../shared/dist/host/lib-runners.js
2409
+ function runnerOf(runtimes, runtime) {
2410
+ return runtime === "opencli" ? runtimes.opencli : void 0;
2411
+ }
2412
+ function setterNameOf(runtime) {
2413
+ return `set${runtime.charAt(0).toUpperCase()}${runtime.slice(1)}Runner`;
2414
+ }
2415
+ /**
2416
+ * 给声明了 `runs` 的 lib 注入「跑这个运行时」的执行器:库侧按约定暴露 `setOpencliRunner`
2417
+ * (运行时名字首字母大写 + `Runner`),外壳在每次 `load` 之后把执行器交给它。
2418
+ *
2419
+ * 没暴露的(版本旧)就保持它自己的行为(按 PATH 找),只记一条 warn;不熟悉的运行时同样只 warn。
2420
+ * @param libs 原来的读库口。
2421
+ * @param runtimes 外壳手上有的执行器,按运行时名字给。
2422
+ * @param logger 记 warn 的地方(默认不记)。
2423
+ * @returns 包好的读库口;`load` 的签名与返回不变。
2424
+ */
2425
+ function withLibRunners(libs, runtimes, logger) {
2426
+ const warned = /* @__PURE__ */ new Set();
2427
+ const warnOnce = (key, message) => {
2428
+ if (warned.has(key)) return;
2429
+ warned.add(key);
2430
+ logger?.warn?.(message);
2431
+ };
2432
+ return { async load(need, entry) {
2433
+ const module = await libs.load(need, entry);
2434
+ for (const runtime of need.runs ?? []) {
2435
+ const runner = runnerOf(runtimes, runtime);
2436
+ if (runner === void 0) {
2437
+ warnOnce(`${need.name}:${runtime}:unsupported`, `[dsh-workbench] ${need.name} 声明了自己会跑 ${runtime},但工作台没有这个执行器 —— 它会按自己的方式找`);
2438
+ continue;
2439
+ }
2440
+ const setter = typeof module === "object" && module !== null ? module[setterNameOf(runtime)] : void 0;
2441
+ if (typeof setter !== "function") {
2442
+ warnOnce(`${need.name}:${entry}:${runtime}:missing`, `[dsh-workbench] ${need.name} 声明了自己会跑 ${runtime},但这一版没有 ${setterNameOf(runtime)} —— 它会按 PATH 找`);
2443
+ continue;
2444
+ }
2445
+ setter.call(module, runner);
2446
+ }
2447
+ return module;
2448
+ } };
2449
+ }
2450
+ //#endregion
2353
2451
  //#region ../shared/dist/host/value-handoff.js
2354
2452
  function handoffId(entry) {
2355
2453
  return `${String(entry.fact.length)}:${entry.fact}${entry.key}`;
@@ -17486,6 +17584,7 @@ function notifyLongTaskStop(agents, unit, change) {
17486
17584
  //#endregion
17487
17585
  //#region src/app/host.ts
17488
17586
  var name = "dsh-workbench";
17587
+ var LIB_OPENCLI_TIMEOUT_MS = 18e5;
17489
17588
  var inject = ["webServer"];
17490
17589
  async function defaultAdapterSet(ctx) {
17491
17590
  return (await import("@dsh-workbench/adapters-default")).createAdapterSet(ctx);
@@ -17506,6 +17605,7 @@ async function apply(ctx, config) {
17506
17605
  else if (loaded.failure !== "not-configured") ctx.logger?.warn?.(`[dsh-workbench] 适配器集合没生效(${loaded.failure}):${loaded.detail};先用默认集合`);
17507
17606
  ctx.provide(WORKBENCH_ADAPTERS, adapters);
17508
17607
  const opencli = adapters.runtime.opencliCommand(ctx);
17608
+ const libRunners = adapters.libs === void 0 ? void 0 : withLibRunners(adapters.libs, { opencli: opencliRunnerOf((request) => adapters.data.read(request), { timeoutMs: LIB_OPENCLI_TIMEOUT_MS }) }, ctx.logger);
17509
17609
  const shell = ctx.get("shell");
17510
17610
  const routes = createHostRoutes({
17511
17611
  ctx,
@@ -17682,7 +17782,7 @@ async function apply(ctx, config) {
17682
17782
  installable: () => installableUnitsOf(units),
17683
17783
  store,
17684
17784
  data: adapters.data,
17685
- libs: adapters.libs,
17785
+ libs: libRunners,
17686
17786
  git: adapters.git,
17687
17787
  commands: adapters.commands,
17688
17788
  probe: (unit, options) => probeOne(unit, options?.force ?? false),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "个人工作台:Agent 工作台(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -100,8 +100,8 @@
100
100
  "proper-lockfile": "^4.1.2",
101
101
  "which": "^7.0.0",
102
102
  "write-file-atomic": "^8.0.0",
103
- "@dsh-workbench/shared": "1.1.6",
104
- "@dsh-workbench/adapters-default": "1.1.6"
103
+ "@dsh-workbench/shared": "1.1.7",
104
+ "@dsh-workbench/adapters-default": "1.1.7"
105
105
  },
106
106
  "license": "MIT",
107
107
  "repository": {
@@ -107,7 +107,13 @@
107
107
  { "kind": "local", "env": "ACME_PLUGIN_PATH" }
108
108
  ]
109
109
  },
110
- { "kind": "lib", "name": "opencli-workflow", "purpose": "发布编排" },
110
+ {
111
+ "kind": "lib",
112
+ "name": "opencli-workflow",
113
+ "version": "^1.1.25",
114
+ "runs": ["opencli"],
115
+ "purpose": "发布编排"
116
+ },
111
117
  {
112
118
  "kind": "bin",
113
119
  "name": "acme",
@@ -122,6 +128,10 @@
122
128
  - `sources` 按顺序试(本地 → 私有 → 公共)。没写 `sources` 就是「我自己装,你只帮我判」——外壳只探不代装。
123
129
  - `bin` 想让人一键装,就写 `install.line`(一条能直接粘进终端的命令,`platforms` 可限定平台);
124
130
  不写就只显示「缺这条命令」,按钮都不会有。`docs` 是给人和 agent 看的补充链接。
131
+ - `lib` 自己起运行时的命令(比如发布编排库自己 `execFile('opencli')`)就写 `runs: ["opencli"]`:
132
+ 外壳把它手上那份「跑 opencli」的执行器注入进去(库侧约定暴露 `setOpencliRunner`),
133
+ 于是编排与面板用同一份 opencli(同一套插件、同一份登录态)。不写 `runs` 就按库自己的 PATH 找。
134
+ 库版本太旧、没有 `setOpencliRunner` 时外壳只记一条 warn,照旧按 PATH 跑;用 `version` 卡住那一版即可。
125
135
  - `extension` 的 `sources[].kind` 只有 `npm` / `git` / `local`;`lib` 只认 `npm`。
126
136
  未知 `kind` 或 `source.kind` 判红;need 里未知字段只警告,`x-` 前缀忽略。
127
137
  - 未装好文案由模块写:`libInstallCommand({ need, source })`(`@dsh-workbench/shared/host`)拼好源与缓存目录,