@workbench-ai/workbench 0.0.85 → 0.0.87

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkEA,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,CAqMlB"}
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,CAqMlB"}
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);
@@ -907,10 +906,6 @@ async function handleInstall(parsed, io) {
907
906
  baseUrl: workbenchSource.baseUrl,
908
907
  },
909
908
  });
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
909
  return emitResult("workbench.cli.install.v1", {
915
910
  source: sourceSummary,
916
911
  result: result.result,
@@ -921,14 +916,9 @@ async function handleInstall(parsed, io) {
921
916
  filesCopied: result.filesCopied,
922
917
  contentHash: result.contentHash,
923
918
  provenancePath: result.provenancePath,
924
- fanout: fanOutToJson(fanout),
925
- next: next,
919
+ next: null,
926
920
  ...(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"));
921
+ }, parsed, io, () => formatInstallOutcome(result, parsed.flags["dry-run"] === true));
932
922
  }
933
923
  async function handleCloudEval(parsed, io) {
934
924
  const started = await startCloudExecution("eval", parsed, io);
@@ -1008,19 +998,6 @@ async function handleCloudImprove(parsed, io) {
1008
998
  ...(next ? [`next: ${next}`] : []),
1009
999
  ].filter(Boolean).join("\n"));
1010
1000
  }
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
1001
  function formatInstallOutcome(result, dryRun) {
1025
1002
  if (dryRun) {
1026
1003
  if (result.result === "unchanged") {
@@ -1048,34 +1025,6 @@ function formatInstallOutcome(result, dryRun) {
1048
1025
  function formatFileCount(count) {
1049
1026
  return `${count} ${count === 1 ? "file" : "files"}`;
1050
1027
  }
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
1028
  async function latestInstallVersion(record) {
1080
1029
  const handle = normalizedOwnerSkillHandle(record.handle);
1081
1030
  if (!handle) {
@@ -1118,7 +1067,7 @@ async function startCloudExecution(command, parsed, io) {
1118
1067
  const token = await workbenchCloudToken({ baseUrl: source.baseUrl });
1119
1068
  if (!token) {
1120
1069
  throw new WorkbenchCodedError("auth_required", `workbench ${command} --cloud requires Workbench Cloud auth.`, {
1121
- remediation: `Run workbench login --base-url ${source.baseUrl}.`,
1070
+ remediation: workbenchLoginRemediation(source.baseUrl),
1122
1071
  exitCode: 1,
1123
1072
  });
1124
1073
  }
@@ -1444,7 +1393,7 @@ async function ensureCloudRemoteForExecution(root, parsed) {
1444
1393
  const token = await workbenchCloudToken({ baseUrl: source.baseUrl });
1445
1394
  if (!token) {
1446
1395
  throw new WorkbenchCodedError("auth_required", "workbench --cloud requires Workbench Cloud auth.", {
1447
- remediation: `Run workbench login --base-url ${source.baseUrl}.`,
1396
+ remediation: workbenchLoginRemediation(source.baseUrl),
1448
1397
  exitCode: 1,
1449
1398
  });
1450
1399
  }
@@ -1799,6 +1748,13 @@ function optionalWorkbenchBaseUrl(input = {}) {
1799
1748
  DEFAULT_WORKBENCH_CLOUD_BASE_URL;
1800
1749
  return normalizeBaseUrl(value);
1801
1750
  }
1751
+ function workbenchLoginRemediation(baseUrl) {
1752
+ const normalized = baseUrl ? normalizeBaseUrl(baseUrl) : DEFAULT_WORKBENCH_CLOUD_BASE_URL;
1753
+ if (normalized === DEFAULT_WORKBENCH_CLOUD_BASE_URL) {
1754
+ return "Run workbench login.";
1755
+ }
1756
+ return `Run workbench login --base-url ${normalized}.`;
1757
+ }
1802
1758
  function normalizeBaseUrl(value) {
1803
1759
  return value.trim().replace(/\/+$/u, "");
1804
1760
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-ai/workbench",
3
- "version": "0.0.85",
3
+ "version": "0.0.87",
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-built-in-adapters": "0.0.85",
26
- "@workbench-ai/workbench-protocol": "0.0.85",
27
- "@workbench-ai/workbench-core": "0.0.85",
28
- "@workbench-ai/workbench-contract": "0.0.85"
24
+ "@workbench-ai/workbench-built-in-adapters": "0.0.87",
25
+ "@workbench-ai/workbench-contract": "0.0.87",
26
+ "@workbench-ai/workbench-protocol": "0.0.87",
27
+ "@workbench-ai/workbench-core": "0.0.87"
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.85"
38
+ "@workbench-ai/workbench-ui": "0.0.87"
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
@@ -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
- }