@dsh-workbench/core 0.2.3 → 0.2.5

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,25 +201,42 @@ 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
  }
237
+ var DAY = 864e5;
238
+ 30 * DAY;
239
+ 365 * DAY;
223
240
  var PANEL_STATE_MAX_TEXT = 2e3;
224
241
  var PHASES = [
225
242
  "idle",
@@ -2126,6 +2143,18 @@ function textOutput(text) {
2126
2143
  text
2127
2144
  }];
2128
2145
  }
2146
+ function textRender(_args, value) {
2147
+ const text = value?.text;
2148
+ return textOutput(typeof text === "string" ? text : JSON.stringify(value));
2149
+ }
2150
+ var TEXT_OUTPUT = {
2151
+ schema: {
2152
+ type: "object",
2153
+ properties: {},
2154
+ additionalProperties: true
2155
+ },
2156
+ render: textRender
2157
+ };
2129
2158
  function jsonText(value) {
2130
2159
  return JSON.stringify(value);
2131
2160
  }
@@ -7657,6 +7686,8 @@ function parseCommandRows(stdout) {
7657
7686
  name,
7658
7687
  description: textOf(record["description"]),
7659
7688
  access: textOf(record["access"]),
7689
+ browser: record["browser"] === true,
7690
+ siteSession: textOf(record["siteSession"]),
7660
7691
  ...textOf(record["domain"]) === "" ? {} : { domain: textOf(record["domain"]) },
7661
7692
  args: argsOf(record["args"])
7662
7693
  });
@@ -7669,6 +7700,9 @@ function commandOf(plugin, row) {
7669
7700
  name: row.name,
7670
7701
  description: row.description,
7671
7702
  access: row.access,
7703
+ browser: row.browser,
7704
+ siteSession: row.siteSession,
7705
+ ...row.domain === void 0 ? {} : { domain: row.domain },
7672
7706
  args: row.args
7673
7707
  };
7674
7708
  }
@@ -7842,6 +7876,8 @@ var SCHEMA_TYPE = {
7842
7876
  string: "string"
7843
7877
  };
7844
7878
  var KEY_ARG_LIMIT = 3;
7879
+ var RUN_CODE_TOOL = "run_code";
7880
+ var PLUGIN_PROMPT_BUDGET = 4e3;
7845
7881
  function pluginToolName(plugin, command) {
7846
7882
  return `${plugin}_${command}`.toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/_{2,}/g, "_").replace(/^_+|_+$/g, "");
7847
7883
  }
@@ -7850,7 +7886,7 @@ function schemaTypeOf(arg) {
7850
7886
  }
7851
7887
  function propertyOf(arg) {
7852
7888
  const type = schemaTypeOf(arg);
7853
- const enumValues = arg.choices.length > 0 ? arg.choices.map((choice) => choice) : void 0;
7889
+ const enumValues = type === "string" && arg.choices.length > 0 ? [...arg.choices] : void 0;
7854
7890
  return {
7855
7891
  type,
7856
7892
  ...arg.help.trim() === "" ? {} : { description: arg.help.trim() },
@@ -7858,6 +7894,9 @@ function propertyOf(arg) {
7858
7894
  ...arg.defaultValue === void 0 ? {} : { default: arg.defaultValue }
7859
7895
  };
7860
7896
  }
7897
+ function pluginPromptChars(command) {
7898
+ return pluginToolDescription(command).length + command.args.reduce((sum, arg) => sum + arg.help.trim().length, 0);
7899
+ }
7861
7900
  function pluginToolParameters(command) {
7862
7901
  const properties = {};
7863
7902
  for (const arg of command.args) properties[arg.name] = propertyOf(arg);
@@ -7877,6 +7916,11 @@ function pluginToolDescription(command) {
7877
7916
  function pluginToolWrites(command) {
7878
7917
  return command.access !== "read";
7879
7918
  }
7919
+ function pluginToolSession(command) {
7920
+ if (!command.browser) return "none";
7921
+ if (command.siteSession === "persistent" || command.siteSession === "ephemeral") return command.siteSession;
7922
+ return command.access === "read" ? "persistent" : "ephemeral";
7923
+ }
7880
7924
  function valuesOf(raw) {
7881
7925
  return typeof raw === "object" && raw !== null ? raw : {};
7882
7926
  }
@@ -7893,9 +7937,9 @@ function pluginToolArgv(command, raw) {
7893
7937
  if (arg.positional) continue;
7894
7938
  const value = values[arg.name];
7895
7939
  if (value === void 0 || value === null) continue;
7896
- if (typeof value === "boolean") argv.push(`--${arg.name}`, value ? "true" : "false");
7897
- else if (Array.isArray(value)) argv.push(`--${arg.name}`, value.map((item) => String(item)).join(","));
7898
- else argv.push(`--${arg.name}`, String(value));
7940
+ if (typeof value === "boolean") argv.push(`--${arg.name}=${value ? "true" : "false"}`);
7941
+ else if (Array.isArray(value)) argv.push(`--${arg.name}=${value.map((item) => String(item)).join(",")}`);
7942
+ else argv.push(`--${arg.name}=${String(value)}`);
7899
7943
  }
7900
7944
  return argv;
7901
7945
  }
@@ -7933,11 +7977,6 @@ function pluginApprovalFor(command) {
7933
7977
  }
7934
7978
  //#endregion
7935
7979
  //#region src/shared/api/host/plugin-units.ts
7936
- var TOOL_OUTPUT_SCHEMA = {
7937
- type: "object",
7938
- properties: {},
7939
- additionalProperties: true
7940
- };
7941
7980
  function signatureOf(version, commands) {
7942
7981
  return [version, ...commands.map((command) => `${command.name}:${command.access}:${command.args.map((arg) => `${arg.name}${arg.required ? "!" : ""}`).join("+")}`)].join("|");
7943
7982
  }
@@ -7947,6 +7986,10 @@ function toolsOf(plugin, commands, taken) {
7947
7986
  const claimed = /* @__PURE__ */ new Set();
7948
7987
  for (const command of commands) {
7949
7988
  const name = pluginToolName(plugin, command.name);
7989
+ if (name === "run_code") {
7990
+ problems.push(`工具名 ${RUN_CODE_TOOL}(${plugin} ${command.name})是 dsh 的保留名(PTC 的传送工具)—— 这条命令这次不合成`);
7991
+ continue;
7992
+ }
7950
7993
  if (taken.has(name) || claimed.has(name)) {
7951
7994
  problems.push(`工具名 ${name}(${plugin} ${command.name})已经被占着 —— 这条命令这次不合成(先来的那份说了算)`);
7952
7995
  continue;
@@ -7989,12 +8032,15 @@ function planOf(options, registered) {
7989
8032
  problems.push(...built.problems);
7990
8033
  for (const tool of built.tools) taken.add(tool.name);
7991
8034
  if (built.tools.length === 0 && commands.length === set.commands.length) problems.push(`「${plugin}」的命令一条都没能合成工具`);
8035
+ const chars = built.tools.reduce((sum, tool) => sum + pluginPromptChars(tool.command), 0);
8036
+ if (chars > 4e3) problems.push(`「${plugin}」合成出来的 ${String(built.tools.length)} 把工具,描述与参数说明合起来 ${String(chars)} 字,超过 ${String(PLUGIN_PROMPT_BUDGET)} 字的合成面预算 —— 这些字每一步请求都跟着走,插件该精简或按工作区少开一点`);
7992
8037
  rows.push({
7993
8038
  plugin,
7994
8039
  pkg: row.pkg,
7995
8040
  version: set.version,
7996
8041
  commands: set.commands.length,
7997
8042
  synthesized: built.tools.length,
8043
+ chars,
7998
8044
  tools: built.tools.map((tool) => tool.name)
7999
8045
  });
8000
8046
  if (built.tools.length === 0) continue;
@@ -8015,7 +8061,7 @@ function planOf(options, registered) {
8015
8061
  problems
8016
8062
  };
8017
8063
  }
8018
- function faceOf(unit, opencliFor) {
8064
+ function faceOf(unit, opencliFor, note) {
8019
8065
  const byTool = new Map(unit.tools.map((tool) => [tool.name, tool.command]));
8020
8066
  const manifest = {
8021
8067
  id: unit.id,
@@ -8043,14 +8089,14 @@ function faceOf(unit, opencliFor) {
8043
8089
  name: tool.name,
8044
8090
  description: pluginToolDescription(tool.command),
8045
8091
  parameters: pluginToolParameters(tool.command),
8046
- output: {
8047
- schema: TOOL_OUTPUT_SCHEMA,
8048
- render: (_args, value) => textOutput(jsonText(value))
8049
- },
8092
+ output: TEXT_OUTPUT,
8050
8093
  async execute(raw, exec) {
8051
8094
  const workspace = sessionWorkspace(exec);
8052
8095
  try {
8053
- return (await opencli.json([tool.command.name, ...pluginToolArgv(tool.command, raw)], workspace === null ? {} : { workdir: workspace })).value;
8096
+ return { text: jsonText((await opencli.json([tool.command.name, ...pluginToolArgv(tool.command, raw)], {
8097
+ session: pluginToolSession(tool.command),
8098
+ ...workspace === null ? {} : { workdir: workspace }
8099
+ })).value) };
8054
8100
  } catch (error) {
8055
8101
  throw new Error(pluginLoginHint(error, tool.command.domain), { cause: error });
8056
8102
  }
@@ -8059,7 +8105,15 @@ function faceOf(unit, opencliFor) {
8059
8105
  return {
8060
8106
  routes: {},
8061
8107
  factsOf: () => [],
8062
- registerTools: () => definitions.map((definition) => tools.register(definition)),
8108
+ registerTools: () => {
8109
+ const disposers = [];
8110
+ for (const definition of definitions) try {
8111
+ disposers.push(tools.register(definition));
8112
+ } catch (error) {
8113
+ note(`「${unit.plugin}」的 ${definition.name} 这把工具没注册上(${error instanceof Error ? error.message : String(error)})—— 只废这一把,同插件其余命令照常`);
8114
+ }
8115
+ return disposers;
8116
+ },
8063
8117
  needsApproval: (call) => {
8064
8118
  const command = call.name === void 0 ? void 0 : byTool.get(call.name);
8065
8119
  return command === void 0 ? void 0 : pluginApprovalFor(command)(call);
@@ -8068,7 +8122,7 @@ function faceOf(unit, opencliFor) {
8068
8122
  }
8069
8123
  };
8070
8124
  }
8071
- function reconcile(registered, units, options) {
8125
+ function reconcile(registered, units, options, note) {
8072
8126
  const wanted = new Map(units.map((unit) => [unit.id, unit]));
8073
8127
  for (const [id, item] of [...registered]) {
8074
8128
  const next = wanted.get(id);
@@ -8081,12 +8135,13 @@ function reconcile(registered, units, options) {
8081
8135
  registered.set(unit.id, {
8082
8136
  signature: unit.signature,
8083
8137
  tools: unit.tools.map((tool) => tool.name),
8084
- drop: options.registry.registerPlugin(faceOf(unit, options.opencliFor))
8138
+ drop: options.registry.registerPlugin(faceOf(unit, options.opencliFor, note))
8085
8139
  });
8086
8140
  }
8087
8141
  }
8088
8142
  function createPluginUnits(options) {
8089
8143
  const registered = /* @__PURE__ */ new Map();
8144
+ const mountProblems = /* @__PURE__ */ new Set();
8090
8145
  let report = {
8091
8146
  ok: false,
8092
8147
  at: 0,
@@ -8096,6 +8151,18 @@ function createPluginUnits(options) {
8096
8151
  let inflight;
8097
8152
  let lastTouch = 0;
8098
8153
  let warned = "";
8154
+ function note(problem) {
8155
+ if (mountProblems.has(problem)) return;
8156
+ mountProblems.add(problem);
8157
+ options.logger?.warn?.(`[dsh-workbench] ${problem}`);
8158
+ }
8159
+ function snapshot() {
8160
+ const extra = [...mountProblems];
8161
+ return extra.length === 0 ? report : {
8162
+ ...report,
8163
+ problems: [...report.problems, ...extra]
8164
+ };
8165
+ }
8099
8166
  function warn(problems) {
8100
8167
  const text = [...problems].sort().join("\n");
8101
8168
  if (text === "" || text === warned) return;
@@ -8103,6 +8170,7 @@ function createPluginUnits(options) {
8103
8170
  options.logger?.warn?.(`[dsh-workbench] opencli 插件命令面:\n${text}`);
8104
8171
  }
8105
8172
  async function run(force) {
8173
+ mountProblems.clear();
8106
8174
  const probed = await options.catalog.refresh(force ? { force: true } : {});
8107
8175
  if (!probed.ok) {
8108
8176
  const problems = [...probed.problems, "这次探测没成功:上面这些插件行的工具面维持在上一份(不是\"这些插件没有命令\")"];
@@ -8113,10 +8181,10 @@ function createPluginUnits(options) {
8113
8181
  problems
8114
8182
  };
8115
8183
  warn(problems);
8116
- return report;
8184
+ return snapshot();
8117
8185
  }
8118
8186
  const plan = planOf(options, registered);
8119
- reconcile(registered, plan.units, options);
8187
+ reconcile(registered, plan.units, options, note);
8120
8188
  report = {
8121
8189
  ok: true,
8122
8190
  at: Date.now(),
@@ -8124,7 +8192,7 @@ function createPluginUnits(options) {
8124
8192
  problems: plan.problems
8125
8193
  };
8126
8194
  warn(plan.problems);
8127
- return report;
8195
+ return snapshot();
8128
8196
  }
8129
8197
  function refresh(callOptions = {}) {
8130
8198
  if (inflight !== void 0) return inflight;
@@ -8145,7 +8213,7 @@ function createPluginUnits(options) {
8145
8213
  options.logger?.warn?.(`[dsh-workbench] 探 opencli 插件命令面时抛了错:${String(error)}`);
8146
8214
  });
8147
8215
  },
8148
- report: () => report,
8216
+ report: () => snapshot(),
8149
8217
  dispose() {
8150
8218
  for (const item of registered.values()) item.drop();
8151
8219
  registered.clear();