@workbench-ai/workbench 0.0.72 → 0.0.74
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/fanout.d.ts +13 -0
- package/dist/fanout.d.ts.map +1 -0
- package/dist/fanout.js +212 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +326 -184
- package/dist/install-targets.d.ts +54 -21
- package/dist/install-targets.d.ts.map +1 -1
- package/dist/install-targets.js +333 -118
- package/package.json +7 -6
package/dist/fanout.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
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, scrubAgentRuntimeEnv(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
|
+
function scrubAgentRuntimeEnv(env) {
|
|
147
|
+
const next = { ...env };
|
|
148
|
+
for (const key of [
|
|
149
|
+
"AI_AGENT",
|
|
150
|
+
"CODEX_SANDBOX",
|
|
151
|
+
"CODEX_CI",
|
|
152
|
+
"CODEX_THREAD_ID",
|
|
153
|
+
"CURSOR_TRACE_ID",
|
|
154
|
+
"CURSOR_AGENT",
|
|
155
|
+
"CURSOR_EXTENSION_HOST_ROLE",
|
|
156
|
+
"GEMINI_CLI",
|
|
157
|
+
"ANTIGRAVITY_AGENT",
|
|
158
|
+
"AUGMENT_AGENT",
|
|
159
|
+
"OPENCODE_CLIENT",
|
|
160
|
+
"CLAUDECODE",
|
|
161
|
+
"CLAUDE_CODE",
|
|
162
|
+
"CLAUDE_CODE_IS_COWORK",
|
|
163
|
+
"REPL_ID",
|
|
164
|
+
"COPILOT_MODEL",
|
|
165
|
+
"COPILOT_ALLOW_ALL",
|
|
166
|
+
"COPILOT_GITHUB_TOKEN",
|
|
167
|
+
]) {
|
|
168
|
+
delete next[key];
|
|
169
|
+
}
|
|
170
|
+
return next;
|
|
171
|
+
}
|
|
172
|
+
function parseFanOutAgents(output) {
|
|
173
|
+
const cleaned = stripAnsi(output);
|
|
174
|
+
const linked = new Set();
|
|
175
|
+
let additionalAgents = 0;
|
|
176
|
+
for (const line of cleaned.split(/\r?\n/u)) {
|
|
177
|
+
const normalized = line.replace(/[\u2502\u25c7\u25cf\u25a0\u2713\u2717]/gu, " ").trim();
|
|
178
|
+
const match = /^(?:universal|symlinked|copied|copy\s*\u2192|symlink\s*\u2192):\s*(.+)$/u.exec(normalized);
|
|
179
|
+
if (!match) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
for (const rawAgent of match[1].split(",").map((entry) => entry.trim())) {
|
|
183
|
+
const moreMatch = /\+(\d+) more$/u.exec(rawAgent);
|
|
184
|
+
if (moreMatch) {
|
|
185
|
+
additionalAgents += Number(moreMatch[1]);
|
|
186
|
+
}
|
|
187
|
+
const agent = rawAgent.replace(/\s*\+\d+ more$/u, "").trim();
|
|
188
|
+
if (agent) {
|
|
189
|
+
linked.add(agent);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
linkedAgents: [...linked].sort((left, right) => left.localeCompare(right)),
|
|
195
|
+
additionalAgents,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function conciseFailureReason(output) {
|
|
199
|
+
const lines = stripAnsi(output)
|
|
200
|
+
.split(/\r?\n/u)
|
|
201
|
+
.map((line) => line.replace(/[\u2500-\u257f\u25a0\u25cf\u25c7\u2713\u2717]/gu, " ").trim())
|
|
202
|
+
.filter((line) => line.length > 0 &&
|
|
203
|
+
!/^skills\s*$/iu.test(line) &&
|
|
204
|
+
!/^Tip:/u.test(line) &&
|
|
205
|
+
!/^(Source|Local path|Found \d+ skill|Available skills|Installed \d+ skill|Done!|Review skills)/u.test(line) &&
|
|
206
|
+
!/^- /u.test(line));
|
|
207
|
+
const reason = lines.at(-1);
|
|
208
|
+
return reason ? (reason.length > 200 ? `${reason.slice(0, 197)}...` : reason) : null;
|
|
209
|
+
}
|
|
210
|
+
function stripAnsi(value) {
|
|
211
|
+
return value.replace(/\x1B\[[0-?]*[ -/]*[@-~]/gu, "");
|
|
212
|
+
}
|
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,CAsMlB"}
|