@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 +10 -0
- package/dist/client/client.body.js +10 -0
- package/dist/client/index.js +10 -0
- package/dist/host/index.js +119 -19
- package/package.json +3 -3
- package/skills/workbench-module-diy/TOOLS.md +11 -1
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 没写`);
|
package/dist/client/index.js
CHANGED
|
@@ -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 没写`);
|
package/dist/host/index.js
CHANGED
|
@@ -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$
|
|
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$
|
|
1151
|
-
const message = textOf$
|
|
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$
|
|
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$
|
|
1185
|
-
const route = textOf$
|
|
1186
|
-
const phase = textOf$
|
|
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$
|
|
1222
|
-
const unit = textOf$
|
|
1223
|
-
const reason = textOf$
|
|
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$
|
|
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$
|
|
2045
|
-
const pkg = textOf$
|
|
2046
|
-
const installedAt = textOf$
|
|
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$
|
|
2049
|
-
const registry = textOf$
|
|
2050
|
-
const releasedAt = textOf$
|
|
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:
|
|
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.
|
|
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.
|
|
104
|
-
"@dsh-workbench/adapters-default": "1.1.
|
|
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
|
-
{
|
|
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`)拼好源与缓存目录,
|