@workbench-ai/workbench 0.0.86 → 0.0.88
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -61
- package/package.json +6 -7
- package/dist/fanout.d.ts +0 -13
- package/dist/fanout.d.ts.map +0 -1
- package/dist/fanout.js +0 -223
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiEA,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AAuTD,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,GAAE,KAGzD,GAAG,OAAO,CAAC,MAAM,CAAC,CA2MlB"}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,6 @@ import { gzipSync } from "node:zlib";
|
|
|
7
7
|
import { addWorkbenchRemote, addWorkbenchAgent, compareWorkbench, createWorkbenchVersionRuntimeSnapshot, createWorkbenchInspectionSnapshot, createWorkbenchAdapterAuthBundle, createWorkbenchReadOnlyInspectionSnapshot, diffWorkbenchVersions, evalWorkbenchSkill, improveWorkbenchSkill, initWorkbenchSkill, listWorkbenchAgents, listWorkbenchVersions, localWorkbenchAdapterAuthStore, parseWorkbenchAdapterAuthTarget, prepareWorkbenchCloudEvalRequest, prepareWorkbenchCloudImproveRequest, publishWorkbenchVersion, removeWorkbenchAgent, showWorkbenchRef, switchWorkbenchVersion, syncWorkbenchRemote, workbenchJobEvidenceForSnapshot, workbenchStatusSnapshot, WorkbenchCodedError, WorkbenchUserError, } from "@workbench-ai/workbench-core";
|
|
8
8
|
import { normalizeWorkbenchSkillName } from "@workbench-ai/workbench-contract";
|
|
9
9
|
import { emitError, emitResult } from "./output.js";
|
|
10
|
-
import { fanOutSkill, manualFanOutCommand } from "./fanout.js";
|
|
11
10
|
import { installedInventoryToJson, installSnapshotToStore, normalizeInstallSnapshotPath, readInstalledSkillsInventory, } from "./install-targets.js";
|
|
12
11
|
import { startWorkbenchOpenServer } from "./open-server.js";
|
|
13
12
|
const require = createRequire(import.meta.url);
|
|
@@ -449,14 +448,21 @@ export async function runCli(argv, io = {
|
|
|
449
448
|
`next: workbench install ${preview.installHandle}`,
|
|
450
449
|
].join("\n"));
|
|
451
450
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
451
|
+
let remote;
|
|
452
|
+
let result;
|
|
453
|
+
try {
|
|
454
|
+
remote = await ensurePublishRemote(parsed);
|
|
455
|
+
result = await publishWorkbenchVersion({
|
|
456
|
+
...core,
|
|
457
|
+
version: optionalPositional(parsed, 1),
|
|
458
|
+
remote,
|
|
459
|
+
dryRun: parsed.flags["dry-run"] === true,
|
|
460
|
+
visibility: parsePublishVisibilityFlags(parsed),
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
throw await publishErrorWithCliContext(error, parsed, remote);
|
|
465
|
+
}
|
|
460
466
|
return emitResult("workbench.cli.publish.v1", {
|
|
461
467
|
remote: result.remote,
|
|
462
468
|
version: versionSummary(result.version),
|
|
@@ -907,10 +913,6 @@ async function handleInstall(parsed, io) {
|
|
|
907
913
|
baseUrl: workbenchSource.baseUrl,
|
|
908
914
|
},
|
|
909
915
|
});
|
|
910
|
-
const fanout = parsed.flags["dry-run"] === true
|
|
911
|
-
? skippedFanOut(result.directoryName, result.destination, result.result === "unchanged" ? "unchanged" : "dry-run")
|
|
912
|
-
: await fanOutSkill(result.directoryName, { skillDir: result.destination });
|
|
913
|
-
const next = installNextCommand(fanout);
|
|
914
916
|
return emitResult("workbench.cli.install.v1", {
|
|
915
917
|
source: sourceSummary,
|
|
916
918
|
result: result.result,
|
|
@@ -921,14 +923,9 @@ async function handleInstall(parsed, io) {
|
|
|
921
923
|
filesCopied: result.filesCopied,
|
|
922
924
|
contentHash: result.contentHash,
|
|
923
925
|
provenancePath: result.provenancePath,
|
|
924
|
-
|
|
925
|
-
next: next,
|
|
926
|
+
next: null,
|
|
926
927
|
...(parsed.flags["dry-run"] === true ? { dryRun: true } : {}),
|
|
927
|
-
}, parsed, io, () => [
|
|
928
|
-
formatInstallOutcome(result, parsed.flags["dry-run"] === true),
|
|
929
|
-
formatFanOut(fanout),
|
|
930
|
-
...(next ? [`next: ${next}`] : []),
|
|
931
|
-
].join("\n"));
|
|
928
|
+
}, parsed, io, () => formatInstallOutcome(result, parsed.flags["dry-run"] === true));
|
|
932
929
|
}
|
|
933
930
|
async function handleCloudEval(parsed, io) {
|
|
934
931
|
const started = await startCloudExecution("eval", parsed, io);
|
|
@@ -1008,19 +1005,6 @@ async function handleCloudImprove(parsed, io) {
|
|
|
1008
1005
|
...(next ? [`next: ${next}`] : []),
|
|
1009
1006
|
].filter(Boolean).join("\n"));
|
|
1010
1007
|
}
|
|
1011
|
-
function skippedFanOut(name, destination, reason) {
|
|
1012
|
-
return {
|
|
1013
|
-
status: "skipped",
|
|
1014
|
-
command: manualFanOutCommand(destination, name),
|
|
1015
|
-
linkedAgents: [],
|
|
1016
|
-
reason,
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
function installNextCommand(fanout) {
|
|
1020
|
-
return fanout.status === "failed" || (fanout.status === "skipped" && fanout.reason !== "dry-run" && fanout.reason !== "unchanged")
|
|
1021
|
-
? fanout.command
|
|
1022
|
-
: null;
|
|
1023
|
-
}
|
|
1024
1008
|
function formatInstallOutcome(result, dryRun) {
|
|
1025
1009
|
if (dryRun) {
|
|
1026
1010
|
if (result.result === "unchanged") {
|
|
@@ -1048,34 +1032,6 @@ function formatInstallOutcome(result, dryRun) {
|
|
|
1048
1032
|
function formatFileCount(count) {
|
|
1049
1033
|
return `${count} ${count === 1 ? "file" : "files"}`;
|
|
1050
1034
|
}
|
|
1051
|
-
function fanOutToJson(fanout) {
|
|
1052
|
-
return {
|
|
1053
|
-
status: fanout.status,
|
|
1054
|
-
command: fanout.command,
|
|
1055
|
-
linkedAgents: fanout.linkedAgents,
|
|
1056
|
-
...(fanout.additionalAgents ? { additionalAgents: fanout.additionalAgents } : {}),
|
|
1057
|
-
...(fanout.reason ? { reason: fanout.reason } : {}),
|
|
1058
|
-
...(fanout.exitCode !== undefined ? { exitCode: fanout.exitCode } : {}),
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
function formatFanOut(fanout) {
|
|
1062
|
-
if (fanout.status === "skipped") {
|
|
1063
|
-
if (fanout.reason === "dry-run") {
|
|
1064
|
-
return "fanout: planned";
|
|
1065
|
-
}
|
|
1066
|
-
if (fanout.reason === "unchanged") {
|
|
1067
|
-
return "fanout: unchanged";
|
|
1068
|
-
}
|
|
1069
|
-
return `fanout skipped: ${fanout.reason ?? "not available"}`;
|
|
1070
|
-
}
|
|
1071
|
-
if (fanout.status === "failed") {
|
|
1072
|
-
return `fanout failed: ${fanout.reason ?? "unknown failure"}`;
|
|
1073
|
-
}
|
|
1074
|
-
if (fanout.linkedAgents.length === 0) {
|
|
1075
|
-
return "fanout: completed";
|
|
1076
|
-
}
|
|
1077
|
-
return "fanout: completed";
|
|
1078
|
-
}
|
|
1079
1035
|
async function latestInstallVersion(record) {
|
|
1080
1036
|
const handle = normalizedOwnerSkillHandle(record.handle);
|
|
1081
1037
|
if (!handle) {
|
|
@@ -2689,6 +2645,33 @@ function installHandleFromCloudRemote(remote) {
|
|
|
2689
2645
|
}
|
|
2690
2646
|
return `${source.owner}/${source.skill}`;
|
|
2691
2647
|
}
|
|
2648
|
+
async function publishErrorWithCliContext(error, parsed, remoteName) {
|
|
2649
|
+
if (!(error instanceof WorkbenchCodedError) || error.code !== "auth_required") {
|
|
2650
|
+
return error;
|
|
2651
|
+
}
|
|
2652
|
+
if (error.message.startsWith("workbench publish")) {
|
|
2653
|
+
return error;
|
|
2654
|
+
}
|
|
2655
|
+
return new WorkbenchCodedError("auth_required", "workbench publish requires Workbench Cloud auth.", {
|
|
2656
|
+
remediation: await publishAuthRemediation(parsed, remoteName, error.remediation),
|
|
2657
|
+
...(error.subject ? { subject: error.subject } : {}),
|
|
2658
|
+
exitCode: error.exitCode,
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2661
|
+
async function publishAuthRemediation(parsed, remoteName, fallback) {
|
|
2662
|
+
const root = path.resolve(dirFlag(parsed) ?? process.cwd());
|
|
2663
|
+
try {
|
|
2664
|
+
const snapshot = await createWorkbenchReadOnlyInspectionSnapshot({ dir: root });
|
|
2665
|
+
const remote = remoteName
|
|
2666
|
+
? snapshot.remotes.find((entry) => entry.name === remoteName)
|
|
2667
|
+
: cloudRemoteLinkTargetFromRemotes(snapshot.remotes).existing;
|
|
2668
|
+
const source = remote ? parseWorkbenchInstallSource(remote.url) : undefined;
|
|
2669
|
+
return workbenchLoginRemediation(source?.baseUrl);
|
|
2670
|
+
}
|
|
2671
|
+
catch {
|
|
2672
|
+
return fallback ?? "Run workbench login.";
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2692
2675
|
function parseOwnerSkillHandle(input) {
|
|
2693
2676
|
const handle = normalizedOwnerSkillHandle(input);
|
|
2694
2677
|
if (!handle) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workbench-ai/workbench",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.88",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/workbench-ai/workbench.git",
|
|
@@ -20,12 +20,11 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"skills": "1.5.11",
|
|
24
23
|
"yaml": "^2.8.2",
|
|
25
|
-
"@workbench-ai/workbench-
|
|
26
|
-
"@workbench-ai/workbench-
|
|
27
|
-
"@workbench-ai/workbench-
|
|
28
|
-
"@workbench-ai/workbench-
|
|
24
|
+
"@workbench-ai/workbench-contract": "0.0.88",
|
|
25
|
+
"@workbench-ai/workbench-built-in-adapters": "0.0.88",
|
|
26
|
+
"@workbench-ai/workbench-protocol": "0.0.88",
|
|
27
|
+
"@workbench-ai/workbench-core": "0.0.88"
|
|
29
28
|
},
|
|
30
29
|
"devDependencies": {
|
|
31
30
|
"@tailwindcss/postcss": "^4.2.2",
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"react-dom": "^19.2.0",
|
|
37
36
|
"typescript": "^5.9.2",
|
|
38
37
|
"vitest": "^3.2.4",
|
|
39
|
-
"@workbench-ai/workbench-ui": "0.0.
|
|
38
|
+
"@workbench-ai/workbench-ui": "0.0.88"
|
|
40
39
|
},
|
|
41
40
|
"scripts": {
|
|
42
41
|
"build": "rm -rf dist && tsc -p tsconfig.json && chmod 755 dist/workbench.js && node ./scripts/build-dev-open-assets.mjs",
|
package/dist/fanout.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface FanOutResult {
|
|
2
|
-
status: "completed" | "skipped" | "failed";
|
|
3
|
-
command: string;
|
|
4
|
-
linkedAgents: string[];
|
|
5
|
-
additionalAgents?: number;
|
|
6
|
-
reason?: string;
|
|
7
|
-
exitCode?: number | null;
|
|
8
|
-
}
|
|
9
|
-
export declare function fanOutSkill(name: string, options: {
|
|
10
|
-
skillDir: string;
|
|
11
|
-
}): Promise<FanOutResult>;
|
|
12
|
-
export declare function manualFanOutCommand(skillDir: string, name: string): string;
|
|
13
|
-
//# sourceMappingURL=fanout.d.ts.map
|
package/dist/fanout.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fanout.d.ts","sourceRoot":"","sources":["../src/fanout.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAoEpG;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAO1E"}
|
package/dist/fanout.js
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
const require = createRequire(import.meta.url);
|
|
7
|
-
const FANOUT_TIMEOUT_MS = 30_000;
|
|
8
|
-
export async function fanOutSkill(name, options) {
|
|
9
|
-
const resolved = await resolveSkillsBin().catch((error) => ({
|
|
10
|
-
binPath: null,
|
|
11
|
-
reason: error instanceof Error ? error.message : "Unable to resolve skills package.",
|
|
12
|
-
}));
|
|
13
|
-
const command = manualFanOutCommand(options.skillDir, name);
|
|
14
|
-
if (!resolved.binPath) {
|
|
15
|
-
return {
|
|
16
|
-
status: "skipped",
|
|
17
|
-
command,
|
|
18
|
-
linkedAgents: [],
|
|
19
|
-
reason: resolved.reason,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
const stagingRoot = await fs.mkdtemp(path.join(os.tmpdir(), "workbench-skills-fanout-"));
|
|
23
|
-
const stagedSkillDir = path.join(stagingRoot, name);
|
|
24
|
-
try {
|
|
25
|
-
await fs.cp(options.skillDir, stagedSkillDir, {
|
|
26
|
-
recursive: true,
|
|
27
|
-
force: true,
|
|
28
|
-
dereference: true,
|
|
29
|
-
});
|
|
30
|
-
const args = [
|
|
31
|
-
resolved.binPath,
|
|
32
|
-
"add",
|
|
33
|
-
stagedSkillDir,
|
|
34
|
-
"--global",
|
|
35
|
-
"--yes",
|
|
36
|
-
];
|
|
37
|
-
const child = await runNode(args, minimalChildEnv(process.env), FANOUT_TIMEOUT_MS);
|
|
38
|
-
const { linkedAgents, additionalAgents } = parseFanOutAgents(`${child.stdout}\n${child.stderr}`);
|
|
39
|
-
if (child.timedOut) {
|
|
40
|
-
return {
|
|
41
|
-
status: "failed",
|
|
42
|
-
command,
|
|
43
|
-
linkedAgents,
|
|
44
|
-
...(additionalAgents > 0 ? { additionalAgents } : {}),
|
|
45
|
-
reason: `skills fan-out timed out after ${FANOUT_TIMEOUT_MS}ms.`,
|
|
46
|
-
exitCode: child.exitCode,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
if (child.exitCode !== 0) {
|
|
50
|
-
return {
|
|
51
|
-
status: "failed",
|
|
52
|
-
command,
|
|
53
|
-
linkedAgents,
|
|
54
|
-
...(additionalAgents > 0 ? { additionalAgents } : {}),
|
|
55
|
-
reason: conciseFailureReason(`${child.stdout}\n${child.stderr}`) ?? `skills fan-out exited ${child.exitCode}.`,
|
|
56
|
-
exitCode: child.exitCode,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
status: "completed",
|
|
61
|
-
command,
|
|
62
|
-
linkedAgents,
|
|
63
|
-
...(additionalAgents > 0 ? { additionalAgents } : {}),
|
|
64
|
-
exitCode: child.exitCode,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
return {
|
|
69
|
-
status: "failed",
|
|
70
|
-
command,
|
|
71
|
-
linkedAgents: [],
|
|
72
|
-
reason: error instanceof Error ? error.message : "skills fan-out failed.",
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
finally {
|
|
76
|
-
await fs.rm(stagingRoot, { recursive: true, force: true });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
export function manualFanOutCommand(skillDir, name) {
|
|
80
|
-
const quotedName = quoteShellArg(name);
|
|
81
|
-
return [
|
|
82
|
-
"tmpdir=$(mktemp -d)",
|
|
83
|
-
`cp -R ${quoteShellArg(skillDir)} "$tmpdir"/${quotedName}`,
|
|
84
|
-
`skills add "$tmpdir"/${quotedName} --global --yes`,
|
|
85
|
-
].join(" && ");
|
|
86
|
-
}
|
|
87
|
-
function quoteShellArg(value) {
|
|
88
|
-
return /^[A-Za-z0-9_./:=@+-]+$/u.test(value)
|
|
89
|
-
? value
|
|
90
|
-
: `'${value.replace(/'/gu, "'\\''")}'`;
|
|
91
|
-
}
|
|
92
|
-
async function resolveSkillsBin() {
|
|
93
|
-
const packageJsonPath = require.resolve("skills/package.json");
|
|
94
|
-
const packageRoot = path.dirname(packageJsonPath);
|
|
95
|
-
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
96
|
-
const bin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.skills;
|
|
97
|
-
if (!bin) {
|
|
98
|
-
throw new Error("The skills package does not declare a skills bin.");
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
binPath: path.resolve(packageRoot, bin),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
function runNode(args, env, timeoutMs) {
|
|
105
|
-
return new Promise((resolve) => {
|
|
106
|
-
const child = spawn(process.execPath, args, {
|
|
107
|
-
env,
|
|
108
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
109
|
-
windowsHide: true,
|
|
110
|
-
});
|
|
111
|
-
let stdout = "";
|
|
112
|
-
let stderr = "";
|
|
113
|
-
let timedOut = false;
|
|
114
|
-
const timer = setTimeout(() => {
|
|
115
|
-
timedOut = true;
|
|
116
|
-
child.kill("SIGTERM");
|
|
117
|
-
}, timeoutMs);
|
|
118
|
-
child.stdout?.setEncoding("utf8");
|
|
119
|
-
child.stderr?.setEncoding("utf8");
|
|
120
|
-
child.stdout?.on("data", (chunk) => {
|
|
121
|
-
stdout += chunk;
|
|
122
|
-
});
|
|
123
|
-
child.stderr?.on("data", (chunk) => {
|
|
124
|
-
stderr += chunk;
|
|
125
|
-
});
|
|
126
|
-
child.on("error", (error) => {
|
|
127
|
-
clearTimeout(timer);
|
|
128
|
-
resolve({
|
|
129
|
-
exitCode: 1,
|
|
130
|
-
stdout,
|
|
131
|
-
stderr: stderr || error.message,
|
|
132
|
-
timedOut,
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
child.on("close", (exitCode) => {
|
|
136
|
-
clearTimeout(timer);
|
|
137
|
-
resolve({
|
|
138
|
-
exitCode,
|
|
139
|
-
stdout,
|
|
140
|
-
stderr,
|
|
141
|
-
timedOut,
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
// The skills CLI changes behavior when it detects it is running inside an
|
|
147
|
-
// agent (via @vercel/detect-agent reading agent-specific environment
|
|
148
|
-
// variables). Fan-out must always run in machine mode, so the child gets a
|
|
149
|
-
// minimal allowlisted environment instead of a hand-maintained blacklist of
|
|
150
|
-
// every agent's marker variables.
|
|
151
|
-
function minimalChildEnv(env) {
|
|
152
|
-
const allowed = [
|
|
153
|
-
"PATH",
|
|
154
|
-
"HOME",
|
|
155
|
-
"USER",
|
|
156
|
-
"SHELL",
|
|
157
|
-
"TMPDIR",
|
|
158
|
-
"TMP",
|
|
159
|
-
"TEMP",
|
|
160
|
-
"LANG",
|
|
161
|
-
"NODE_EXTRA_CA_CERTS",
|
|
162
|
-
"HTTP_PROXY",
|
|
163
|
-
"HTTPS_PROXY",
|
|
164
|
-
"NO_PROXY",
|
|
165
|
-
"http_proxy",
|
|
166
|
-
"https_proxy",
|
|
167
|
-
"no_proxy",
|
|
168
|
-
"SYSTEMROOT",
|
|
169
|
-
"SystemRoot",
|
|
170
|
-
"COMSPEC",
|
|
171
|
-
"APPDATA",
|
|
172
|
-
"LOCALAPPDATA",
|
|
173
|
-
"USERPROFILE",
|
|
174
|
-
];
|
|
175
|
-
const next = {};
|
|
176
|
-
for (const key of Object.keys(env)) {
|
|
177
|
-
if (allowed.includes(key) || key.startsWith("XDG_") || key.startsWith("LC_")) {
|
|
178
|
-
next[key] = env[key];
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return next;
|
|
182
|
-
}
|
|
183
|
-
function parseFanOutAgents(output) {
|
|
184
|
-
const cleaned = stripAnsi(output);
|
|
185
|
-
const linked = new Set();
|
|
186
|
-
let additionalAgents = 0;
|
|
187
|
-
for (const line of cleaned.split(/\r?\n/u)) {
|
|
188
|
-
const normalized = line.replace(/[\u2502\u25c7\u25cf\u25a0\u2713\u2717]/gu, " ").trim();
|
|
189
|
-
const match = /^(?:universal|symlinked|copied|copy\s*\u2192|symlink\s*\u2192):\s*(.+)$/u.exec(normalized);
|
|
190
|
-
if (!match) {
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
for (const rawAgent of match[1].split(",").map((entry) => entry.trim())) {
|
|
194
|
-
const moreMatch = /\+(\d+) more$/u.exec(rawAgent);
|
|
195
|
-
if (moreMatch) {
|
|
196
|
-
additionalAgents += Number(moreMatch[1]);
|
|
197
|
-
}
|
|
198
|
-
const agent = rawAgent.replace(/\s*\+\d+ more$/u, "").trim();
|
|
199
|
-
if (agent) {
|
|
200
|
-
linked.add(agent);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
linkedAgents: [...linked].sort((left, right) => left.localeCompare(right)),
|
|
206
|
-
additionalAgents,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function conciseFailureReason(output) {
|
|
210
|
-
const lines = stripAnsi(output)
|
|
211
|
-
.split(/\r?\n/u)
|
|
212
|
-
.map((line) => line.replace(/[\u2500-\u257f\u25a0\u25cf\u25c7\u2713\u2717]/gu, " ").trim())
|
|
213
|
-
.filter((line) => line.length > 0 &&
|
|
214
|
-
!/^skills\s*$/iu.test(line) &&
|
|
215
|
-
!/^Tip:/u.test(line) &&
|
|
216
|
-
!/^(Source|Local path|Found \d+ skill|Available skills|Installed \d+ skill|Done!|Review skills)/u.test(line) &&
|
|
217
|
-
!/^- /u.test(line));
|
|
218
|
-
const reason = lines.at(-1);
|
|
219
|
-
return reason ? (reason.length > 200 ? `${reason.slice(0, 197)}...` : reason) : null;
|
|
220
|
-
}
|
|
221
|
-
function stripAnsi(value) {
|
|
222
|
-
return value.replace(/\x1B\[[0-?]*[ -/]*[@-~]/gu, "");
|
|
223
|
-
}
|