@workbench-ai/workbench 0.0.67 → 0.0.69
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/dev-open/client.css +387 -287
- package/dist/dev-open/client.js +202 -202
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-400-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-400-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-500-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-500-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-600-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-600-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-ext-400-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-ext-400-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-ext-500-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-ext-500-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-ext-600-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-cyrillic-ext-600-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-400-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-500-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-600-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-ext-400-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-ext-500-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-ext-600-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-latin-ext-600-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-vietnamese-400-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-vietnamese-400-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-vietnamese-500-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-vietnamese-500-normal.woff2 +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-vietnamese-600-normal.woff +0 -0
- package/dist/dev-open/fonts/ibm-plex-mono-vietnamese-600-normal.woff2 +0 -0
- package/dist/dev-open/fonts/libre-caslon-display-latin-400-normal.woff +0 -0
- package/dist/dev-open/fonts/libre-caslon-display-latin-400-normal.woff2 +0 -0
- package/dist/dev-open/fonts/libre-caslon-display-latin-ext-400-normal.woff +0 -0
- package/dist/dev-open/fonts/libre-caslon-display-latin-ext-400-normal.woff2 +0 -0
- package/dist/index.d.ts +2 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2035 -5100
- package/dist/install-targets.d.ts +35 -0
- package/dist/install-targets.d.ts.map +1 -0
- package/dist/install-targets.js +188 -0
- package/dist/open-server.d.ts +12 -0
- package/dist/open-server.d.ts.map +1 -0
- package/dist/open-server.js +248 -0
- package/dist/output.d.ts +22 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +38 -0
- package/package.json +5 -5
- package/dist/adapter-command-env.d.ts +0 -8
- package/dist/adapter-command-env.d.ts.map +0 -1
- package/dist/adapter-command-env.js +0 -80
- package/dist/adapter-project.d.ts +0 -29
- package/dist/adapter-project.d.ts.map +0 -1
- package/dist/adapter-project.js +0 -332
- package/dist/benchmark-fingerprint.d.ts +0 -6
- package/dist/benchmark-fingerprint.d.ts.map +0 -1
- package/dist/benchmark-fingerprint.js +0 -42
- package/dist/command-model.d.ts +0 -5
- package/dist/command-model.d.ts.map +0 -1
- package/dist/command-model.js +0 -537
- package/dist/dev-open-server.d.ts +0 -18
- package/dist/dev-open-server.d.ts.map +0 -1
- package/dist/dev-open-server.js +0 -297
- package/dist/init-scaffold.d.ts +0 -22
- package/dist/init-scaffold.d.ts.map +0 -1
- package/dist/init-scaffold.js +0 -30
- package/dist/init-template-pack.d.ts +0 -19
- package/dist/init-template-pack.d.ts.map +0 -1
- package/dist/init-template-pack.js +0 -262
- package/dist/local-archive.d.ts +0 -48
- package/dist/local-archive.d.ts.map +0 -1
- package/dist/local-archive.js +0 -838
- package/dist/local-inspection.d.ts +0 -9
- package/dist/local-inspection.d.ts.map +0 -1
- package/dist/local-inspection.js +0 -354
- package/dist/project-source.d.ts +0 -63
- package/dist/project-source.d.ts.map +0 -1
- package/dist/project-source.js +0 -682
- package/dist/workspace-snapshot.d.ts +0 -10
- package/dist/workspace-snapshot.d.ts.map +0 -1
- package/dist/workspace-snapshot.js +0 -81
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Json, type SurfaceSnapshotFile } from "@workbench-ai/workbench-core";
|
|
2
|
+
export type WorkbenchInstallAgentTarget = "codex" | "claude";
|
|
3
|
+
export type WorkbenchInstallTargetName = WorkbenchInstallAgentTarget | "local";
|
|
4
|
+
export interface WorkbenchInstallSnapshot {
|
|
5
|
+
name: string;
|
|
6
|
+
files: SurfaceSnapshotFile[];
|
|
7
|
+
}
|
|
8
|
+
export interface WorkbenchInstallTarget {
|
|
9
|
+
agent: WorkbenchInstallTargetName;
|
|
10
|
+
mode: "copy";
|
|
11
|
+
destination: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WorkbenchInstallTargetResult extends WorkbenchInstallTarget {
|
|
14
|
+
previous: "none" | "overwritten" | "unchanged";
|
|
15
|
+
}
|
|
16
|
+
export interface WorkbenchInstallResult {
|
|
17
|
+
result: "installed" | "unchanged";
|
|
18
|
+
targets: WorkbenchInstallTargetResult[];
|
|
19
|
+
filesCopied: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function supportedInstallTargets(): WorkbenchInstallTarget[];
|
|
22
|
+
export declare function resolveInstallTargets(options: {
|
|
23
|
+
agents: readonly string[];
|
|
24
|
+
local: boolean;
|
|
25
|
+
skillName: string;
|
|
26
|
+
}): WorkbenchInstallTarget[];
|
|
27
|
+
export declare function installSnapshotToTargets(options: {
|
|
28
|
+
snapshot: WorkbenchInstallSnapshot;
|
|
29
|
+
targets: readonly WorkbenchInstallTarget[];
|
|
30
|
+
overwrite: boolean;
|
|
31
|
+
dryRun: boolean;
|
|
32
|
+
}): Promise<WorkbenchInstallResult>;
|
|
33
|
+
export declare function normalizeInstallSnapshotPath(filePath: string): string;
|
|
34
|
+
export declare function installTargetsToJson(targets: readonly WorkbenchInstallTarget[]): Json[];
|
|
35
|
+
//# sourceMappingURL=install-targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-targets.d.ts","sourceRoot":"","sources":["../src/install-targets.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,IAAI,EAAE,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAExG,MAAM,MAAM,2BAA2B,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC7D,MAAM,MAAM,0BAA0B,GAAG,2BAA2B,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,0BAA0B,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,4BAA6B,SAAQ,sBAAsB;IAC1E,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,WAAW,CAAC;CAChD;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,WAAW,GAAG,WAAW,CAAC;IAClC,OAAO,EAAE,4BAA4B,EAAE,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,uBAAuB,IAAI,sBAAsB,EAAE,CAMlE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAC7C,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,sBAAsB,EAAE,CA8B3B;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACtD,QAAQ,EAAE,wBAAwB,CAAC;IACnC,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAyDlC;AAiBD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBrE;AAyDD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,GAAG,IAAI,EAAE,CAMvF"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { WorkbenchCodedError } from "@workbench-ai/workbench-core";
|
|
5
|
+
export function supportedInstallTargets() {
|
|
6
|
+
return [
|
|
7
|
+
{ agent: "codex", mode: "copy", destination: path.join(workbenchAgentHome("codex"), "skills", "<skill>") },
|
|
8
|
+
{ agent: "claude", mode: "copy", destination: path.join(workbenchAgentHome("claude"), "skills", "<skill>") },
|
|
9
|
+
{ agent: "local", mode: "copy", destination: path.join(process.cwd(), ".agents", "skills", "<skill>") },
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
export function resolveInstallTargets(options) {
|
|
13
|
+
const targets = [];
|
|
14
|
+
for (const rawAgent of options.agents) {
|
|
15
|
+
const agent = rawAgent.trim().toLowerCase();
|
|
16
|
+
if (agent !== "codex" && agent !== "claude") {
|
|
17
|
+
throw new WorkbenchCodedError("usage", `Unsupported install agent: ${rawAgent}`, {
|
|
18
|
+
remediation: "Use --agent codex, --agent claude, or --local.",
|
|
19
|
+
exitCode: 2,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
targets.push({
|
|
23
|
+
agent,
|
|
24
|
+
mode: "copy",
|
|
25
|
+
destination: path.join(workbenchAgentHome(agent), "skills", options.skillName),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (options.local) {
|
|
29
|
+
targets.push({
|
|
30
|
+
agent: "local",
|
|
31
|
+
mode: "copy",
|
|
32
|
+
destination: path.join(process.cwd(), ".agents", "skills", options.skillName),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (targets.length === 0) {
|
|
36
|
+
throw new WorkbenchCodedError("install_target_required", "workbench install requires an explicit target.", {
|
|
37
|
+
remediation: "Run workbench install --source SOURCE --agent codex, workbench install --source SOURCE --agent claude, or workbench install --source SOURCE --local.",
|
|
38
|
+
exitCode: 2,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return dedupeTargets(targets);
|
|
42
|
+
}
|
|
43
|
+
export async function installSnapshotToTargets(options) {
|
|
44
|
+
const normalizedFiles = options.snapshot.files.map((file) => ({
|
|
45
|
+
...file,
|
|
46
|
+
path: normalizeInstallSnapshotPath(file.path),
|
|
47
|
+
}));
|
|
48
|
+
const next = new Map(normalizedFiles.map((file) => [file.path, installFileContent(file)]));
|
|
49
|
+
// Pre-validate every target before mutating any destination so a conflict
|
|
50
|
+
// on a later target cannot leave an earlier target partially installed.
|
|
51
|
+
const plan = [];
|
|
52
|
+
for (const target of options.targets) {
|
|
53
|
+
const existing = await readExistingTree(target.destination).catch((error) => {
|
|
54
|
+
const code = error.code;
|
|
55
|
+
if (code === "ENOENT") {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (code === "ENOTDIR") {
|
|
59
|
+
const conflictPath = error.path ?? target.destination;
|
|
60
|
+
throw new WorkbenchCodedError("install_failed", `Install target path is blocked by an existing file: ${conflictPath}`, {
|
|
61
|
+
remediation: `Remove the conflicting file ${conflictPath} and rerun the install.`,
|
|
62
|
+
subject: { destination: target.destination, conflictPath },
|
|
63
|
+
exitCode: 1,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
});
|
|
68
|
+
const identical = existing ? fileMapsEqual(existing, next) : false;
|
|
69
|
+
if (identical) {
|
|
70
|
+
plan.push({ target, previous: "unchanged" });
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (existing && !options.overwrite) {
|
|
74
|
+
throw new WorkbenchCodedError("install_failed", `Install target already exists: ${target.destination}`, {
|
|
75
|
+
remediation: "Pass --yes to overwrite the existing target.",
|
|
76
|
+
subject: { destination: target.destination },
|
|
77
|
+
exitCode: 1,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
plan.push({ target, previous: existing ? "overwritten" : "none" });
|
|
81
|
+
}
|
|
82
|
+
let filesCopied = 0;
|
|
83
|
+
const results = [];
|
|
84
|
+
for (const entry of plan) {
|
|
85
|
+
results.push({ ...entry.target, previous: entry.previous });
|
|
86
|
+
if (entry.previous === "unchanged") {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
filesCopied += normalizedFiles.length;
|
|
90
|
+
if (!options.dryRun) {
|
|
91
|
+
await fs.rm(entry.target.destination, { recursive: true, force: true });
|
|
92
|
+
await writeSnapshotFiles(entry.target.destination, normalizedFiles);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
result: plan.some((entry) => entry.previous !== "unchanged") ? "installed" : "unchanged",
|
|
97
|
+
targets: results,
|
|
98
|
+
filesCopied,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function workbenchAgentHome(agent) {
|
|
102
|
+
if (agent === "codex") {
|
|
103
|
+
return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
|
|
104
|
+
}
|
|
105
|
+
return process.env.CLAUDE_HOME?.trim() || path.join(os.homedir(), ".claude");
|
|
106
|
+
}
|
|
107
|
+
function dedupeTargets(targets) {
|
|
108
|
+
const byDestination = new Map();
|
|
109
|
+
for (const target of targets) {
|
|
110
|
+
byDestination.set(target.destination, target);
|
|
111
|
+
}
|
|
112
|
+
return [...byDestination.values()];
|
|
113
|
+
}
|
|
114
|
+
export function normalizeInstallSnapshotPath(filePath) {
|
|
115
|
+
const normalized = filePath.replace(/\\/gu, "/");
|
|
116
|
+
if (!normalized || normalized.includes("\0") || normalized.startsWith("/")) {
|
|
117
|
+
throw new WorkbenchCodedError("install_failed", `Invalid source file path: ${filePath}`, {
|
|
118
|
+
subject: { path: filePath },
|
|
119
|
+
exitCode: 1,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const parts = normalized.split("/");
|
|
123
|
+
if (parts.some((part) => part === "" || part === "." || part === "..")) {
|
|
124
|
+
throw new WorkbenchCodedError("install_failed", `Invalid source file path: ${filePath}`, {
|
|
125
|
+
subject: { path: filePath },
|
|
126
|
+
exitCode: 1,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return normalized;
|
|
130
|
+
}
|
|
131
|
+
function installFileContent(file) {
|
|
132
|
+
if (file.encoding === "base64") {
|
|
133
|
+
return Buffer.from(file.content, "base64");
|
|
134
|
+
}
|
|
135
|
+
return file.content;
|
|
136
|
+
}
|
|
137
|
+
async function writeSnapshotFiles(root, files) {
|
|
138
|
+
for (const file of files) {
|
|
139
|
+
const filePath = path.join(root, file.path);
|
|
140
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
141
|
+
await fs.writeFile(filePath, installFileContent(file));
|
|
142
|
+
if (file.executable) {
|
|
143
|
+
await fs.chmod(filePath, 0o755);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function readExistingTree(root) {
|
|
148
|
+
const result = new Map();
|
|
149
|
+
await readExistingTreeInto(root, "", result);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
async function readExistingTreeInto(root, relativeDir, result) {
|
|
153
|
+
const dir = path.join(root, relativeDir);
|
|
154
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
155
|
+
for (const entry of entries) {
|
|
156
|
+
const relativePath = relativeDir ? path.join(relativeDir, entry.name) : entry.name;
|
|
157
|
+
const fullPath = path.join(root, relativePath);
|
|
158
|
+
if (entry.isDirectory()) {
|
|
159
|
+
await readExistingTreeInto(root, relativePath, result);
|
|
160
|
+
}
|
|
161
|
+
else if (entry.isFile()) {
|
|
162
|
+
result.set(relativePath.replace(/\\/gu, "/"), await fs.readFile(fullPath));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function fileMapsEqual(left, right) {
|
|
167
|
+
if (left.size !== right.size) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
for (const [key, rightValue] of right) {
|
|
171
|
+
const leftValue = left.get(key);
|
|
172
|
+
if (!leftValue) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
const rightBuffer = Buffer.isBuffer(rightValue) ? rightValue : Buffer.from(rightValue);
|
|
176
|
+
if (!leftValue.equals(rightBuffer)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
export function installTargetsToJson(targets) {
|
|
183
|
+
return targets.map((target) => ({
|
|
184
|
+
agent: target.agent,
|
|
185
|
+
mode: target.mode,
|
|
186
|
+
destination: target.destination,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface StartWorkbenchOpenServerOptions {
|
|
2
|
+
dir?: string;
|
|
3
|
+
authToken?: string;
|
|
4
|
+
host?: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface StartedWorkbenchOpenServer {
|
|
8
|
+
url: string;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function startWorkbenchOpenServer(options?: StartWorkbenchOpenServerOptions): Promise<StartedWorkbenchOpenServer>;
|
|
12
|
+
//# sourceMappingURL=open-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-server.d.ts","sourceRoot":"","sources":["../src/open-server.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,+BAA+B;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,0BAA0B,CAAC,CA+BrC"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createWorkbenchReadOnlyInspectionSnapshot, workbenchJobEvidenceForSnapshot, workbenchInspectionFileContent, workbenchInspectionFileManifest, WorkbenchUserError, } from "@workbench-ai/workbench-core";
|
|
6
|
+
export async function startWorkbenchOpenServer(options = {}) {
|
|
7
|
+
const host = options.host ?? "127.0.0.1";
|
|
8
|
+
const port = options.port ?? 0;
|
|
9
|
+
const assetRoot = await resolveDevOpenAssetRoot();
|
|
10
|
+
const server = createServer((request, response) => {
|
|
11
|
+
void handleRequest({ request, response, assetRoot, dir: options.dir, authToken: options.authToken });
|
|
12
|
+
});
|
|
13
|
+
await new Promise((resolve, reject) => {
|
|
14
|
+
server.once("error", reject);
|
|
15
|
+
server.listen(port, host, () => {
|
|
16
|
+
server.off("error", reject);
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
const address = server.address();
|
|
21
|
+
if (!address || typeof address === "string") {
|
|
22
|
+
throw new WorkbenchUserError("Could not determine Workbench open server address.");
|
|
23
|
+
}
|
|
24
|
+
const display = displayHost(host);
|
|
25
|
+
return {
|
|
26
|
+
url: `http://${display}:${address.port}/`,
|
|
27
|
+
close: () => new Promise((resolve, reject) => {
|
|
28
|
+
server.close((error) => {
|
|
29
|
+
if (error) {
|
|
30
|
+
reject(error);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
resolve();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function resolveDevOpenAssetRoot() {
|
|
40
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
41
|
+
const candidates = [
|
|
42
|
+
path.join(moduleDir, "dev-open"),
|
|
43
|
+
path.join(moduleDir, "..", "dist", "dev-open"),
|
|
44
|
+
];
|
|
45
|
+
for (const candidate of candidates) {
|
|
46
|
+
try {
|
|
47
|
+
await fs.access(path.join(candidate, "client.js"));
|
|
48
|
+
return candidate;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Try the next source/build layout.
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return candidates[0];
|
|
55
|
+
}
|
|
56
|
+
async function handleRequest({ request, response, assetRoot, dir, authToken, }) {
|
|
57
|
+
try {
|
|
58
|
+
const url = new URL(request.url ?? "/", "http://workbench.local");
|
|
59
|
+
if (url.pathname === "/api/snapshot") {
|
|
60
|
+
const snapshot = await createWorkbenchReadOnlyInspectionSnapshot({ dir, authToken });
|
|
61
|
+
sendText(response, 200, `${JSON.stringify(inspectionSnapshotManifest(snapshot), null, 2)}\n`, "application/json; charset=utf-8");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const jobEvidenceRoute = parseJobEvidenceApiPath(url.pathname);
|
|
65
|
+
if (jobEvidenceRoute) {
|
|
66
|
+
const runId = url.searchParams.get("run")?.trim();
|
|
67
|
+
if (!runId) {
|
|
68
|
+
sendText(response, 400, `${JSON.stringify({ message: "run is required" })}\n`, "application/json; charset=utf-8");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const snapshot = await createWorkbenchReadOnlyInspectionSnapshot({ dir, authToken });
|
|
72
|
+
const detail = workbenchJobEvidenceForSnapshot(snapshot, {
|
|
73
|
+
runId,
|
|
74
|
+
jobId: jobEvidenceRoute.jobId,
|
|
75
|
+
});
|
|
76
|
+
if (!detail) {
|
|
77
|
+
sendText(response, 404, `${JSON.stringify({ message: "Job evidence not found" })}\n`, "application/json; charset=utf-8");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
sendText(response, 200, `${JSON.stringify(detail, null, 2)}\n`, "application/json; charset=utf-8");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const fileRoute = parseInspectionFileApiPath(url.pathname);
|
|
84
|
+
if (fileRoute) {
|
|
85
|
+
const snapshot = await createWorkbenchReadOnlyInspectionSnapshot({ dir, authToken });
|
|
86
|
+
const content = inspectionFileContentForSnapshot(snapshot, fileRoute);
|
|
87
|
+
if (!content) {
|
|
88
|
+
sendText(response, 404, `${JSON.stringify({ message: "File not found" })}\n`, "application/json; charset=utf-8");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
sendText(response, 200, `${JSON.stringify(content, null, 2)}\n`, "application/json; charset=utf-8");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (url.pathname.startsWith("/api/")) {
|
|
95
|
+
sendText(response, 404, `${JSON.stringify({ message: "Not found" })}\n`, "application/json; charset=utf-8");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
99
|
+
sendText(response, 200, html(), "text/html; charset=utf-8");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (url.pathname === "/client.js" || url.pathname === "/client.css" || url.pathname.startsWith("/fonts/")) {
|
|
103
|
+
await sendAsset(response, assetRoot, url.pathname.slice(1));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
sendText(response, 200, html(), "text/html; charset=utf-8");
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
sendText(response, 500, `${error instanceof Error ? error.message : String(error)}\n`, "text/plain; charset=utf-8");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function inspectionSnapshotManifest(snapshot) {
|
|
113
|
+
return {
|
|
114
|
+
...snapshot,
|
|
115
|
+
versions: snapshot.versions.map((version) => ({
|
|
116
|
+
...version,
|
|
117
|
+
files: inspectionFileManifests(version.files),
|
|
118
|
+
})),
|
|
119
|
+
skillBundles: snapshot.skillBundles.map((bundle) => ({
|
|
120
|
+
...bundle,
|
|
121
|
+
files: inspectionFileManifests(bundle.files),
|
|
122
|
+
})),
|
|
123
|
+
evals: snapshot.evals.map((evalSnapshot) => ({
|
|
124
|
+
...evalSnapshot,
|
|
125
|
+
files: inspectionFileManifests(evalSnapshot.files),
|
|
126
|
+
})),
|
|
127
|
+
...(snapshot.comparison ? {
|
|
128
|
+
comparison: {
|
|
129
|
+
...snapshot.comparison,
|
|
130
|
+
versions: snapshot.comparison.versions.map((version) => ({
|
|
131
|
+
...version,
|
|
132
|
+
files: inspectionFileManifests(version.files),
|
|
133
|
+
})),
|
|
134
|
+
skills: snapshot.comparison.skills.map((bundle) => ({
|
|
135
|
+
...bundle,
|
|
136
|
+
files: inspectionFileManifests(bundle.files),
|
|
137
|
+
})),
|
|
138
|
+
},
|
|
139
|
+
} : {}),
|
|
140
|
+
traces: snapshot.traces.map((trace) => ({
|
|
141
|
+
...trace,
|
|
142
|
+
files: inspectionFileManifests(trace.files),
|
|
143
|
+
})),
|
|
144
|
+
artifacts: snapshot.artifacts.map((artifact) => ({
|
|
145
|
+
...artifact,
|
|
146
|
+
files: inspectionFileManifests(artifact.files),
|
|
147
|
+
})),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function inspectionFileManifests(files) {
|
|
151
|
+
return files.map(workbenchInspectionFileManifest);
|
|
152
|
+
}
|
|
153
|
+
function parseJobEvidenceApiPath(pathname) {
|
|
154
|
+
const segments = pathname.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
|
|
155
|
+
const [api, jobs, jobId, evidence] = segments;
|
|
156
|
+
if (api !== "api" || jobs !== "jobs" || evidence !== "evidence" || !jobId || segments.length !== 4) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
return { jobId };
|
|
160
|
+
}
|
|
161
|
+
function parseInspectionFileApiPath(pathname) {
|
|
162
|
+
const segments = pathname.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
|
|
163
|
+
const [api, ownerKind, ownerId, files, ...filePath] = segments;
|
|
164
|
+
if (api !== "api" || files !== "files" || !ownerId || filePath.length === 0) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (ownerKind !== "versions" && ownerKind !== "traces" && ownerKind !== "artifacts") {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
ownerKind: ownerKind.slice(0, -1),
|
|
172
|
+
ownerId,
|
|
173
|
+
path: filePath.join("/"),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function inspectionFileContentForSnapshot(snapshot, route) {
|
|
177
|
+
const owner = findInspectionFileOwner(snapshot, route.ownerKind, route.ownerId);
|
|
178
|
+
const file = owner?.files.find((entry) => entry.path === route.path);
|
|
179
|
+
return file ? workbenchInspectionFileContent(file) : null;
|
|
180
|
+
}
|
|
181
|
+
function findInspectionFileOwner(snapshot, ownerKind, ownerId) {
|
|
182
|
+
if (ownerKind === "version") {
|
|
183
|
+
return snapshot.versions.find((entry) => entry.id === ownerId);
|
|
184
|
+
}
|
|
185
|
+
if (ownerKind === "trace") {
|
|
186
|
+
return snapshot.traces.find((entry) => entry.id === ownerId);
|
|
187
|
+
}
|
|
188
|
+
return snapshot.artifacts.find((entry) => entry.id === ownerId);
|
|
189
|
+
}
|
|
190
|
+
async function sendAsset(response, assetRoot, relativePath) {
|
|
191
|
+
const normalized = path.normalize(relativePath);
|
|
192
|
+
if (normalized.startsWith("..") || path.isAbsolute(normalized)) {
|
|
193
|
+
sendText(response, 404, "Not found\n", "text/plain; charset=utf-8");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const content = await fs.readFile(path.join(assetRoot, normalized)).catch(() => null);
|
|
197
|
+
if (!content) {
|
|
198
|
+
sendText(response, 404, "Not found\n", "text/plain; charset=utf-8");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
response.statusCode = 200;
|
|
202
|
+
response.setHeader("content-type", contentType(normalized));
|
|
203
|
+
response.end(content);
|
|
204
|
+
}
|
|
205
|
+
function sendText(response, status, content, type) {
|
|
206
|
+
response.statusCode = status;
|
|
207
|
+
response.setHeader("content-type", type);
|
|
208
|
+
response.end(content);
|
|
209
|
+
}
|
|
210
|
+
function html() {
|
|
211
|
+
return [
|
|
212
|
+
"<!doctype html>",
|
|
213
|
+
"<html lang=\"en\">",
|
|
214
|
+
"<head>",
|
|
215
|
+
"<meta charset=\"utf-8\">",
|
|
216
|
+
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
|
|
217
|
+
"<title>Workbench</title>",
|
|
218
|
+
"<link rel=\"stylesheet\" href=\"/client.css\">",
|
|
219
|
+
"</head>",
|
|
220
|
+
"<body>",
|
|
221
|
+
"<div id=\"root\"></div>",
|
|
222
|
+
"<script type=\"module\" src=\"/client.js\"></script>",
|
|
223
|
+
"</body>",
|
|
224
|
+
"</html>",
|
|
225
|
+
"",
|
|
226
|
+
].join("\n");
|
|
227
|
+
}
|
|
228
|
+
function contentType(filePath) {
|
|
229
|
+
if (filePath.endsWith(".js")) {
|
|
230
|
+
return "text/javascript; charset=utf-8";
|
|
231
|
+
}
|
|
232
|
+
if (filePath.endsWith(".css")) {
|
|
233
|
+
return "text/css; charset=utf-8";
|
|
234
|
+
}
|
|
235
|
+
if (filePath.endsWith(".woff2")) {
|
|
236
|
+
return "font/woff2";
|
|
237
|
+
}
|
|
238
|
+
if (filePath.endsWith(".woff")) {
|
|
239
|
+
return "font/woff";
|
|
240
|
+
}
|
|
241
|
+
return "application/octet-stream";
|
|
242
|
+
}
|
|
243
|
+
function displayHost(host) {
|
|
244
|
+
if (host === "0.0.0.0" || host === "::") {
|
|
245
|
+
return "127.0.0.1";
|
|
246
|
+
}
|
|
247
|
+
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
248
|
+
}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Json } from "@workbench-ai/workbench-core";
|
|
2
|
+
import type { CliIo } from "./index.js";
|
|
3
|
+
export interface ParsedCliFlags {
|
|
4
|
+
flags: Record<string, string | boolean | string[]>;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkbenchCliSuccessEnvelope {
|
|
7
|
+
schema: string;
|
|
8
|
+
ok: true;
|
|
9
|
+
[key: string]: Json | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface WorkbenchCliErrorEnvelope {
|
|
12
|
+
schema: "workbench.cli.error.v1";
|
|
13
|
+
ok: false;
|
|
14
|
+
code: string;
|
|
15
|
+
message: string;
|
|
16
|
+
retryable: boolean;
|
|
17
|
+
remediation?: string;
|
|
18
|
+
subject?: Record<string, Json>;
|
|
19
|
+
}
|
|
20
|
+
export declare function emitResult(schema: string, body: Record<string, Json | undefined>, parsed: ParsedCliFlags, io: CliIo, text: () => string): number;
|
|
21
|
+
export declare function emitError(error: unknown, parsed: ParsedCliFlags, io: CliIo): number;
|
|
22
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAEhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,IAAI,CAAC;IACT,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,wBAAwB,CAAC;IACjC,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAChC;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,CAAC,EACtC,MAAM,EAAE,cAAc,EACtB,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,MAAM,MAAM,GACjB,MAAM,CAaR;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,CAoBnF"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { codedErrorFromUnknown } from "@workbench-ai/workbench-core";
|
|
2
|
+
export function emitResult(schema, body, parsed, io, text) {
|
|
3
|
+
if (parsed.flags.json === true) {
|
|
4
|
+
const envelope = { schema, ok: true };
|
|
5
|
+
for (const [key, value] of Object.entries(body)) {
|
|
6
|
+
if (value !== undefined) {
|
|
7
|
+
envelope[key] = value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
io.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
io.stdout.write(`${text()}\n`);
|
|
14
|
+
}
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
export function emitError(error, parsed, io) {
|
|
18
|
+
const coded = codedErrorFromUnknown(error);
|
|
19
|
+
const envelope = {
|
|
20
|
+
schema: "workbench.cli.error.v1",
|
|
21
|
+
ok: false,
|
|
22
|
+
code: coded.code,
|
|
23
|
+
message: coded.message,
|
|
24
|
+
retryable: coded.retryable,
|
|
25
|
+
...(coded.remediation ? { remediation: coded.remediation } : {}),
|
|
26
|
+
...(coded.subject ? { subject: coded.subject } : {}),
|
|
27
|
+
};
|
|
28
|
+
if (parsed.flags.json === true) {
|
|
29
|
+
io.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
io.stderr.write(`error[${envelope.code}]: ${envelope.message}\n`);
|
|
33
|
+
if (envelope.remediation) {
|
|
34
|
+
io.stderr.write(`next: ${envelope.remediation}\n`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return coded.exitCode;
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workbench-ai/workbench",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.69",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/workbench-ai/workbench.git",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"yaml": "^2.8.2",
|
|
24
|
-
"@workbench-ai/workbench-built-in-adapters": "0.0.
|
|
25
|
-
"@workbench-ai/workbench-
|
|
26
|
-
"@workbench-ai/workbench-
|
|
24
|
+
"@workbench-ai/workbench-built-in-adapters": "0.0.69",
|
|
25
|
+
"@workbench-ai/workbench-protocol": "0.0.69",
|
|
26
|
+
"@workbench-ai/workbench-core": "0.0.69"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@tailwindcss/postcss": "^4.2.2",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"build": "rm -rf dist && tsc -p tsconfig.json && chmod 755 dist/workbench.js && node ./scripts/build-dev-open-assets.mjs",
|
|
41
41
|
"lint": "tsc -p tsconfig.lint.json",
|
|
42
42
|
"test": "vitest run --config vitest.config.ts",
|
|
43
|
-
"test:e2e": "
|
|
43
|
+
"test:e2e": "vitest run --config vitest.config.ts tests/cli.test.ts"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface AdapterCommandEnvOptions {
|
|
2
|
-
workspaceRoot?: string;
|
|
3
|
-
adapterRoot?: string;
|
|
4
|
-
env?: NodeJS.ProcessEnv;
|
|
5
|
-
extraEnv?: Record<string, string | undefined>;
|
|
6
|
-
}
|
|
7
|
-
export declare function createAdapterCommandEnv(options?: AdapterCommandEnvOptions): NodeJS.ProcessEnv;
|
|
8
|
-
//# sourceMappingURL=adapter-command-env.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-command-env.d.ts","sourceRoot":"","sources":["../src/adapter-command-env.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,wBAAwB;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC/C;AAED,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,MAAM,CAAC,UAAU,CAmBjG"}
|