@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.
- package/client.js +272 -49
- package/dist/client/client.body.js +272 -49
- package/dist/client/index.js +273 -50
- package/dist/host/index.js +100 -32
- package/dist/types/app/client.d.ts +102 -21
- package/dist/types/modules/panels/__tests__/plugin-tool-units-harness.d.ts +69 -0
- package/dist/types/modules/sessions/index.d.ts +1 -0
- package/dist/types/modules/sessions/model/scroll-edges.d.ts +10 -0
- package/dist/types/modules/sessions/model/scroll-edges.test.d.ts +1 -0
- package/dist/types/modules/sessions/model/session-node.d.ts +1 -0
- package/dist/types/shared/api/host/plugin-catalog.d.ts +5 -0
- package/dist/types/shared/api/host/plugin-tools.d.ts +5 -1
- package/dist/types/shared/api/host/plugin-units.d.ts +1 -0
- package/package.json +2 -2
package/dist/host/index.js
CHANGED
|
@@ -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
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
|
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}
|
|
7897
|
-
else if (Array.isArray(value)) argv.push(`--${arg.name}
|
|
7898
|
-
else argv.push(`--${arg.name}
|
|
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)],
|
|
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: () =>
|
|
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
|
|
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
|
|
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: () =>
|
|
8216
|
+
report: () => snapshot(),
|
|
8149
8217
|
dispose() {
|
|
8150
8218
|
for (const item of registered.values()) item.drop();
|
|
8151
8219
|
registered.clear();
|