@dsh-workbench/core 0.2.2 → 0.2.4

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.
@@ -201,23 +201,37 @@ function quoteShellArg(value) {
201
201
  function tailLines(text, lines = 6) {
202
202
  return String(text ?? "").trim().split("\n").slice(-lines).join("\n");
203
203
  }
204
+ function candidateStarts(raw) {
205
+ const starts = [];
206
+ for (let index = 0; index < raw.length; index += 1) {
207
+ const char = raw[index];
208
+ if (char !== "[" && char !== "{") continue;
209
+ if (index === 0 || raw[index - 1] === "\n") starts.push(index);
210
+ }
211
+ const earliest = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
212
+ if (earliest.length > 0) {
213
+ const head = Math.min(...earliest);
214
+ if (!starts.includes(head)) starts.push(head);
215
+ }
216
+ return starts;
217
+ }
204
218
  function parseJsonLoose(text) {
205
219
  const raw = String(text ?? "").trim();
206
220
  if (!raw) return void 0;
207
221
  try {
208
222
  return JSON.parse(raw);
209
223
  } catch {
210
- const starts = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
211
- if (starts.length === 0) return void 0;
212
- const from = Math.min(...starts);
213
- const close = raw[from] === "[" ? "]" : "}";
214
- const to = raw.lastIndexOf(close);
215
- if (to <= from) return void 0;
216
- try {
217
- return JSON.parse(raw.slice(from, to + 1));
218
- } catch {
219
- return;
224
+ for (const from of candidateStarts(raw)) {
225
+ const close = raw[from] === "[" ? "]" : "}";
226
+ const to = raw.lastIndexOf(close);
227
+ if (to <= from) continue;
228
+ try {
229
+ return JSON.parse(raw.slice(from, to + 1));
230
+ } catch {
231
+ continue;
232
+ }
220
233
  }
234
+ return;
221
235
  }
222
236
  }
223
237
  var PANEL_STATE_MAX_TEXT = 2e3;
@@ -2126,6 +2140,18 @@ function textOutput(text) {
2126
2140
  text
2127
2141
  }];
2128
2142
  }
2143
+ function textRender(_args, value) {
2144
+ const text = value?.text;
2145
+ return textOutput(typeof text === "string" ? text : JSON.stringify(value));
2146
+ }
2147
+ var TEXT_OUTPUT = {
2148
+ schema: {
2149
+ type: "object",
2150
+ properties: {},
2151
+ additionalProperties: true
2152
+ },
2153
+ render: textRender
2154
+ };
2129
2155
  function jsonText(value) {
2130
2156
  return JSON.stringify(value);
2131
2157
  }
@@ -2724,8 +2750,11 @@ function createRegistryNotebook(dir) {
2724
2750
  };
2725
2751
  }
2726
2752
  //#endregion
2727
- //#region src/shared/api/host/diy.ts
2753
+ //#region src/shared/api/diy.ts
2728
2754
  var WORKSHOP_RELATIVE = "workbench/diy";
2755
+ var WORKSHOP_TITLE = "工作台DIY";
2756
+ //#endregion
2757
+ //#region src/shared/api/host/diy.ts
2729
2758
  function workshopPathOf(ctx) {
2730
2759
  const homePath = ctx.get("dshHomePath");
2731
2760
  if (typeof homePath !== "function") return null;
@@ -4353,13 +4382,39 @@ function outcomeOf(deps, pkg, change, plugin) {
4353
4382
  };
4354
4383
  }
4355
4384
  async function installDshBundle(deps, spec, options) {
4356
- const change = await deps.manager.install({
4357
- spec,
4385
+ return settle(deps, spec, options, await installOnce(deps, spec, options));
4386
+ }
4387
+ async function replaceDshBundle(deps, spec, options) {
4388
+ const first = await installOnce(deps, spec, options);
4389
+ if (first.errorCode !== "already-installed") return settle(deps, spec, options, first);
4390
+ const pkg = bundleNameOf(first, spec);
4391
+ const removed = await deps.remove(pkg);
4392
+ if (removed.application !== "applied") return {
4393
+ change: removed,
4394
+ module: deps.installed?.(pkg) ?? {
4395
+ kind: "unknown",
4396
+ pkg
4397
+ },
4398
+ supply: { state: "no-needs" }
4399
+ };
4400
+ return settle(deps, spec, options, await installOnce(deps, spec, options));
4401
+ }
4402
+ function installOptions(options) {
4403
+ return {
4358
4404
  ...options?.registry === void 0 ? {} : { registry: options.registry },
4359
4405
  ...options?.requestId === void 0 ? {} : { requestId: options.requestId },
4360
4406
  ...options?.approvedBuilds === void 0 ? {} : { approvedBuilds: options.approvedBuilds }
4407
+ };
4408
+ }
4409
+ async function installOnce(deps, spec, options) {
4410
+ const change = await deps.manager.install({
4411
+ spec,
4412
+ ...installOptions(options)
4361
4413
  });
4362
4414
  if (change === null) throw new Error("插件管理器回的安装结果看不懂。");
4415
+ return change;
4416
+ }
4417
+ async function settle(deps, spec, options, change) {
4363
4418
  const pkg = bundleNameOf(change, spec);
4364
4419
  const module = deps.installed?.(pkg) ?? {
4365
4420
  kind: "unknown",
@@ -4813,6 +4868,11 @@ function createMarketService(options) {
4813
4868
  ...registries === void 0 ? {} : { registries },
4814
4869
  ...logger === void 0 ? {} : { logger }
4815
4870
  };
4871
+ async function removeInstalledBundle(manager, pkg) {
4872
+ const first = await manager.remove(pkg);
4873
+ if (first === null) throw new Error("插件管理器回的卸载结果看不懂。");
4874
+ return retryAfterRegistryFix(registryFix, manager, pkg, first);
4875
+ }
4816
4876
  function routeDeps(manager) {
4817
4877
  return {
4818
4878
  manager,
@@ -4820,6 +4880,7 @@ function createMarketService(options) {
4820
4880
  runAll,
4821
4881
  rememberSource: (manager, pkg, registry) => rememberInstalledSource(registryFix, manager, pkg, registry),
4822
4882
  supplyAfter,
4883
+ remove: (pkg) => removeInstalledBundle(manager, pkg),
4823
4884
  ...shell === void 0 ? {} : { shell },
4824
4885
  ...installed === void 0 ? {} : { installed },
4825
4886
  ...ledger === void 0 ? {} : { ledger },
@@ -4962,9 +5023,7 @@ function createMarketService(options) {
4962
5023
  const unit = module.kind === "module" ? module.unit : void 0;
4963
5024
  const manifest = unit === void 0 ? void 0 : catalog().find((item) => item.id === unit);
4964
5025
  const before = unit === void 0 ? void 0 : await capabilityOf(unit, true);
4965
- const first = await manager.remove(pkg);
4966
- if (first === null) throw new Error("插件管理器回的卸载结果看不懂。");
4967
- const change = await retryAfterRegistryFix(registryFix, manager, pkg, first);
5026
+ const change = await removeInstalledBundle(manager, pkg);
4968
5027
  if (change.application !== "applied" || unit === void 0) return {
4969
5028
  change,
4970
5029
  keptFor: []
@@ -4981,8 +5040,10 @@ function createMarketService(options) {
4981
5040
  return requireBundles().cancel(requestId);
4982
5041
  },
4983
5042
  async updateBundle(unit) {
4984
- const pkg = (modules?.() ?? []).find((item) => item.unit === unit)?.pkg;
4985
- if (pkg === void 0) return null;
5043
+ const found = (modules?.() ?? []).find((item) => item.unit === unit);
5044
+ if (found === void 0) return null;
5045
+ if (found.local === true) throw new Error(`这份模块装的是本地那一份(${found.pkg}),不从源上更新 —— 要更新先把它换成源上的版本。`);
5046
+ const pkg = found.pkg;
4986
5047
  const read = installed?.(pkg) ?? {
4987
5048
  kind: "unknown",
4988
5049
  pkg
@@ -4995,7 +5056,7 @@ function createMarketService(options) {
4995
5056
  force: true
4996
5057
  });
4997
5058
  const spec = latest === void 0 ? pkg : `${pkg}@${latest}`;
4998
- const outcome = await installDshBundle(routeDeps(requireBundles()), spec, { ...registry === void 0 ? {} : { registry } });
5059
+ const outcome = await replaceDshBundle(routeDeps(requireBundles()), spec, { ...registry === void 0 ? {} : { registry } });
4999
5060
  await plugins.sweep();
5000
5061
  await service.update(unit).catch(() => void 0);
5001
5062
  return outcome;
@@ -5023,7 +5084,7 @@ function createMarketService(options) {
5023
5084
  if (missing.length > 0) throw new Error(`源上已经有 ${read.pkg}@${latest},但这一份发布还没铺齐:${missing.join("、")} 在这一版上还拉不到。装上会缺依赖,稍等再试 —— 市场页那颗「重新检查」会重新探一次。`);
5024
5085
  }
5025
5086
  const spec = latest === void 0 ? `${read.pkg}@latest` : `${read.pkg}@${latest}`;
5026
- const outcome = await installDshBundle(routeDeps(requireBundles()), spec, { ...registry === void 0 ? {} : { registry } });
5087
+ const outcome = await replaceDshBundle(routeDeps(requireBundles()), spec, { ...registry === void 0 ? {} : { registry } });
5027
5088
  logger?.info?.(`[dsh-workbench] 已更新工作台 ${read.version} → ${latest ?? "最新版"}`);
5028
5089
  return outcome;
5029
5090
  },
@@ -5162,8 +5223,19 @@ function render$1(entries, shared) {
5162
5223
  if (shared.length === 0) return modules;
5163
5224
  return `${modules}\n\n共用依赖(只有升级):\n${shared.map((dep) => `${dep.id} · ${stateOfDependency(dep)}${versionsOf(dep)}` + (dep.usedBy.length === 0 ? "" : ` · 供 ${dep.usedBy.join("、")} 使用`)).join("\n")}`;
5164
5225
  }
5226
+ var REMOVE_BLOCKED = /* @__PURE__ */ new Set([
5227
+ "not-removable",
5228
+ "management-required",
5229
+ "stop-profile",
5230
+ "bundle-in-use"
5231
+ ]);
5232
+ function failureText(change) {
5233
+ const code = change.errorCode ?? change.application;
5234
+ if (code === "already-installed") return "它已经在这份 profile 里,而这次没能把它卸下来(这一版 dsh 不肯就地覆盖装,得先卸再装)";
5235
+ return REMOVE_BLOCKED.has(code) ? "旧版卸不掉,而这一版 dsh 又不许就地覆盖装 —— 让用户重启一次 dsh,或者从终端更新" : code;
5236
+ }
5165
5237
  function describeInstall(spec, module, supply, change) {
5166
- return `${change.application === "applied" ? "装好了,已生效" : change.application === "restart-required" ? "装好了,要重启一次才生效" : `没成功:${change.errorCode ?? change.application}`}:${module.kind === "module" ? `${module.unit}(${module.pkg})` : module.pkg}${supply.state === "installed" ? ";它要的 opencli 插件也装上了" : supply.state === "pending-restart" ? ";重启后再装它要的 opencli 插件" : supply.state === "not-a-module" ? ";但它不是工作台模块(没有 dsh.workbench 声明,不会出现在面板上)" : supply.state === "failed" ? `;但它要的插件没装上:${supply.reason}` : ""}(spec:${spec})`;
5238
+ return `${change.application === "applied" ? "装好了,已生效" : change.application === "restart-required" ? "装好了,要重启一次才生效" : `没成功:${failureText(change)}`}:${module.kind === "module" ? `${module.unit}(${module.pkg})` : module.pkg}${supply.state === "installed" ? ";它要的 opencli 插件也装上了" : supply.state === "pending-restart" ? ";重启后再装它要的 opencli 插件" : supply.state === "not-a-module" ? ";但它不是工作台模块(没有 dsh.workbench 声明,不会出现在面板上)" : supply.state === "failed" ? `;但它要的插件没装上:${supply.reason}` : ""}(spec:${spec})`;
5167
5239
  }
5168
5240
  var OUTPUT_SCHEMA$1 = {
5169
5241
  type: "object",
@@ -5191,7 +5263,7 @@ function describePluginUninstall(pkg, outcome) {
5191
5263
  return `已从「你装的插件」里去掉 ${pkg}。${outcome.kept === void 0 ? "" : pluginKeptText(outcome.kept)}`;
5192
5264
  }
5193
5265
  function describeWorkbenchUpdate(change) {
5194
- if (change.application === "failed" || change.application === "cancelled") return `没成功:${change.errorCode ?? change.application}`;
5266
+ if (change.application === "failed" || change.application === "cancelled") return `没成功:${failureText(change)}`;
5195
5267
  return change.application === "restart-required" ? "工作台更新到新版了 —— 这一版要重启一次 dsh 才生效(重启由用户来,别自己重启)。" : "工作台更新到新版了 —— 客户端与宿主自己换上,不用重启。";
5196
5268
  }
5197
5269
  function usersOf(shared, names) {
@@ -6499,9 +6571,6 @@ function bundleInspectHandler(ctx) {
6499
6571
  };
6500
6572
  }
6501
6573
  //#endregion
6502
- //#region src/shared/api/diy.ts
6503
- var WORKSHOP_TITLE = "工作台DIY";
6504
- //#endregion
6505
6574
  //#region src/shared/api/dsh-plugins.ts
6506
6575
  var DSH_PLUGINS_ROUTE = "dsh-plugins";
6507
6576
  //#endregion
@@ -7614,6 +7683,8 @@ function parseCommandRows(stdout) {
7614
7683
  name,
7615
7684
  description: textOf(record["description"]),
7616
7685
  access: textOf(record["access"]),
7686
+ browser: record["browser"] === true,
7687
+ siteSession: textOf(record["siteSession"]),
7617
7688
  ...textOf(record["domain"]) === "" ? {} : { domain: textOf(record["domain"]) },
7618
7689
  args: argsOf(record["args"])
7619
7690
  });
@@ -7626,6 +7697,9 @@ function commandOf(plugin, row) {
7626
7697
  name: row.name,
7627
7698
  description: row.description,
7628
7699
  access: row.access,
7700
+ browser: row.browser,
7701
+ siteSession: row.siteSession,
7702
+ ...row.domain === void 0 ? {} : { domain: row.domain },
7629
7703
  args: row.args
7630
7704
  };
7631
7705
  }
@@ -7799,6 +7873,8 @@ var SCHEMA_TYPE = {
7799
7873
  string: "string"
7800
7874
  };
7801
7875
  var KEY_ARG_LIMIT = 3;
7876
+ var RUN_CODE_TOOL = "run_code";
7877
+ var PLUGIN_PROMPT_BUDGET = 4e3;
7802
7878
  function pluginToolName(plugin, command) {
7803
7879
  return `${plugin}_${command}`.toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/_{2,}/g, "_").replace(/^_+|_+$/g, "");
7804
7880
  }
@@ -7807,7 +7883,7 @@ function schemaTypeOf(arg) {
7807
7883
  }
7808
7884
  function propertyOf(arg) {
7809
7885
  const type = schemaTypeOf(arg);
7810
- const enumValues = arg.choices.length > 0 ? arg.choices.map((choice) => choice) : void 0;
7886
+ const enumValues = type === "string" && arg.choices.length > 0 ? [...arg.choices] : void 0;
7811
7887
  return {
7812
7888
  type,
7813
7889
  ...arg.help.trim() === "" ? {} : { description: arg.help.trim() },
@@ -7815,6 +7891,9 @@ function propertyOf(arg) {
7815
7891
  ...arg.defaultValue === void 0 ? {} : { default: arg.defaultValue }
7816
7892
  };
7817
7893
  }
7894
+ function pluginPromptChars(command) {
7895
+ return pluginToolDescription(command).length + command.args.reduce((sum, arg) => sum + arg.help.trim().length, 0);
7896
+ }
7818
7897
  function pluginToolParameters(command) {
7819
7898
  const properties = {};
7820
7899
  for (const arg of command.args) properties[arg.name] = propertyOf(arg);
@@ -7834,6 +7913,11 @@ function pluginToolDescription(command) {
7834
7913
  function pluginToolWrites(command) {
7835
7914
  return command.access !== "read";
7836
7915
  }
7916
+ function pluginToolSession(command) {
7917
+ if (!command.browser) return "none";
7918
+ if (command.siteSession === "persistent" || command.siteSession === "ephemeral") return command.siteSession;
7919
+ return command.access === "read" ? "persistent" : "ephemeral";
7920
+ }
7837
7921
  function valuesOf(raw) {
7838
7922
  return typeof raw === "object" && raw !== null ? raw : {};
7839
7923
  }
@@ -7850,9 +7934,9 @@ function pluginToolArgv(command, raw) {
7850
7934
  if (arg.positional) continue;
7851
7935
  const value = values[arg.name];
7852
7936
  if (value === void 0 || value === null) continue;
7853
- if (typeof value === "boolean") argv.push(`--${arg.name}`, value ? "true" : "false");
7854
- else if (Array.isArray(value)) argv.push(`--${arg.name}`, value.map((item) => String(item)).join(","));
7855
- else argv.push(`--${arg.name}`, String(value));
7937
+ if (typeof value === "boolean") argv.push(`--${arg.name}=${value ? "true" : "false"}`);
7938
+ else if (Array.isArray(value)) argv.push(`--${arg.name}=${value.map((item) => String(item)).join(",")}`);
7939
+ else argv.push(`--${arg.name}=${String(value)}`);
7856
7940
  }
7857
7941
  return argv;
7858
7942
  }
@@ -7890,11 +7974,6 @@ function pluginApprovalFor(command) {
7890
7974
  }
7891
7975
  //#endregion
7892
7976
  //#region src/shared/api/host/plugin-units.ts
7893
- var TOOL_OUTPUT_SCHEMA = {
7894
- type: "object",
7895
- properties: {},
7896
- additionalProperties: true
7897
- };
7898
7977
  function signatureOf(version, commands) {
7899
7978
  return [version, ...commands.map((command) => `${command.name}:${command.access}:${command.args.map((arg) => `${arg.name}${arg.required ? "!" : ""}`).join("+")}`)].join("|");
7900
7979
  }
@@ -7904,6 +7983,10 @@ function toolsOf(plugin, commands, taken) {
7904
7983
  const claimed = /* @__PURE__ */ new Set();
7905
7984
  for (const command of commands) {
7906
7985
  const name = pluginToolName(plugin, command.name);
7986
+ if (name === "run_code") {
7987
+ problems.push(`工具名 ${RUN_CODE_TOOL}(${plugin} ${command.name})是 dsh 的保留名(PTC 的传送工具)—— 这条命令这次不合成`);
7988
+ continue;
7989
+ }
7907
7990
  if (taken.has(name) || claimed.has(name)) {
7908
7991
  problems.push(`工具名 ${name}(${plugin} ${command.name})已经被占着 —— 这条命令这次不合成(先来的那份说了算)`);
7909
7992
  continue;
@@ -7946,12 +8029,15 @@ function planOf(options, registered) {
7946
8029
  problems.push(...built.problems);
7947
8030
  for (const tool of built.tools) taken.add(tool.name);
7948
8031
  if (built.tools.length === 0 && commands.length === set.commands.length) problems.push(`「${plugin}」的命令一条都没能合成工具`);
8032
+ const chars = built.tools.reduce((sum, tool) => sum + pluginPromptChars(tool.command), 0);
8033
+ if (chars > 4e3) problems.push(`「${plugin}」合成出来的 ${String(built.tools.length)} 把工具,描述与参数说明合起来 ${String(chars)} 字,超过 ${String(PLUGIN_PROMPT_BUDGET)} 字的合成面预算 —— 这些字每一步请求都跟着走,插件该精简或按工作区少开一点`);
7949
8034
  rows.push({
7950
8035
  plugin,
7951
8036
  pkg: row.pkg,
7952
8037
  version: set.version,
7953
8038
  commands: set.commands.length,
7954
8039
  synthesized: built.tools.length,
8040
+ chars,
7955
8041
  tools: built.tools.map((tool) => tool.name)
7956
8042
  });
7957
8043
  if (built.tools.length === 0) continue;
@@ -7972,7 +8058,7 @@ function planOf(options, registered) {
7972
8058
  problems
7973
8059
  };
7974
8060
  }
7975
- function faceOf(unit, opencliFor) {
8061
+ function faceOf(unit, opencliFor, note) {
7976
8062
  const byTool = new Map(unit.tools.map((tool) => [tool.name, tool.command]));
7977
8063
  const manifest = {
7978
8064
  id: unit.id,
@@ -8000,14 +8086,14 @@ function faceOf(unit, opencliFor) {
8000
8086
  name: tool.name,
8001
8087
  description: pluginToolDescription(tool.command),
8002
8088
  parameters: pluginToolParameters(tool.command),
8003
- output: {
8004
- schema: TOOL_OUTPUT_SCHEMA,
8005
- render: (_args, value) => textOutput(jsonText(value))
8006
- },
8089
+ output: TEXT_OUTPUT,
8007
8090
  async execute(raw, exec) {
8008
8091
  const workspace = sessionWorkspace(exec);
8009
8092
  try {
8010
- return (await opencli.json([tool.command.name, ...pluginToolArgv(tool.command, raw)], workspace === null ? {} : { workdir: workspace })).value;
8093
+ return { text: jsonText((await opencli.json([tool.command.name, ...pluginToolArgv(tool.command, raw)], {
8094
+ session: pluginToolSession(tool.command),
8095
+ ...workspace === null ? {} : { workdir: workspace }
8096
+ })).value) };
8011
8097
  } catch (error) {
8012
8098
  throw new Error(pluginLoginHint(error, tool.command.domain), { cause: error });
8013
8099
  }
@@ -8016,7 +8102,15 @@ function faceOf(unit, opencliFor) {
8016
8102
  return {
8017
8103
  routes: {},
8018
8104
  factsOf: () => [],
8019
- registerTools: () => definitions.map((definition) => tools.register(definition)),
8105
+ registerTools: () => {
8106
+ const disposers = [];
8107
+ for (const definition of definitions) try {
8108
+ disposers.push(tools.register(definition));
8109
+ } catch (error) {
8110
+ note(`「${unit.plugin}」的 ${definition.name} 这把工具没注册上(${error instanceof Error ? error.message : String(error)})—— 只废这一把,同插件其余命令照常`);
8111
+ }
8112
+ return disposers;
8113
+ },
8020
8114
  needsApproval: (call) => {
8021
8115
  const command = call.name === void 0 ? void 0 : byTool.get(call.name);
8022
8116
  return command === void 0 ? void 0 : pluginApprovalFor(command)(call);
@@ -8025,7 +8119,7 @@ function faceOf(unit, opencliFor) {
8025
8119
  }
8026
8120
  };
8027
8121
  }
8028
- function reconcile(registered, units, options) {
8122
+ function reconcile(registered, units, options, note) {
8029
8123
  const wanted = new Map(units.map((unit) => [unit.id, unit]));
8030
8124
  for (const [id, item] of [...registered]) {
8031
8125
  const next = wanted.get(id);
@@ -8038,12 +8132,13 @@ function reconcile(registered, units, options) {
8038
8132
  registered.set(unit.id, {
8039
8133
  signature: unit.signature,
8040
8134
  tools: unit.tools.map((tool) => tool.name),
8041
- drop: options.registry.registerPlugin(faceOf(unit, options.opencliFor))
8135
+ drop: options.registry.registerPlugin(faceOf(unit, options.opencliFor, note))
8042
8136
  });
8043
8137
  }
8044
8138
  }
8045
8139
  function createPluginUnits(options) {
8046
8140
  const registered = /* @__PURE__ */ new Map();
8141
+ const mountProblems = /* @__PURE__ */ new Set();
8047
8142
  let report = {
8048
8143
  ok: false,
8049
8144
  at: 0,
@@ -8053,6 +8148,18 @@ function createPluginUnits(options) {
8053
8148
  let inflight;
8054
8149
  let lastTouch = 0;
8055
8150
  let warned = "";
8151
+ function note(problem) {
8152
+ if (mountProblems.has(problem)) return;
8153
+ mountProblems.add(problem);
8154
+ options.logger?.warn?.(`[dsh-workbench] ${problem}`);
8155
+ }
8156
+ function snapshot() {
8157
+ const extra = [...mountProblems];
8158
+ return extra.length === 0 ? report : {
8159
+ ...report,
8160
+ problems: [...report.problems, ...extra]
8161
+ };
8162
+ }
8056
8163
  function warn(problems) {
8057
8164
  const text = [...problems].sort().join("\n");
8058
8165
  if (text === "" || text === warned) return;
@@ -8060,6 +8167,7 @@ function createPluginUnits(options) {
8060
8167
  options.logger?.warn?.(`[dsh-workbench] opencli 插件命令面:\n${text}`);
8061
8168
  }
8062
8169
  async function run(force) {
8170
+ mountProblems.clear();
8063
8171
  const probed = await options.catalog.refresh(force ? { force: true } : {});
8064
8172
  if (!probed.ok) {
8065
8173
  const problems = [...probed.problems, "这次探测没成功:上面这些插件行的工具面维持在上一份(不是\"这些插件没有命令\")"];
@@ -8070,10 +8178,10 @@ function createPluginUnits(options) {
8070
8178
  problems
8071
8179
  };
8072
8180
  warn(problems);
8073
- return report;
8181
+ return snapshot();
8074
8182
  }
8075
8183
  const plan = planOf(options, registered);
8076
- reconcile(registered, plan.units, options);
8184
+ reconcile(registered, plan.units, options, note);
8077
8185
  report = {
8078
8186
  ok: true,
8079
8187
  at: Date.now(),
@@ -8081,7 +8189,7 @@ function createPluginUnits(options) {
8081
8189
  problems: plan.problems
8082
8190
  };
8083
8191
  warn(plan.problems);
8084
- return report;
8192
+ return snapshot();
8085
8193
  }
8086
8194
  function refresh(callOptions = {}) {
8087
8195
  if (inflight !== void 0) return inflight;
@@ -8102,7 +8210,7 @@ function createPluginUnits(options) {
8102
8210
  options.logger?.warn?.(`[dsh-workbench] 探 opencli 插件命令面时抛了错:${String(error)}`);
8103
8211
  });
8104
8212
  },
8105
- report: () => report,
8213
+ report: () => snapshot(),
8106
8214
  dispose() {
8107
8215
  for (const item of registered.values()) item.drop();
8108
8216
  registered.clear();