@workbench-ai/workbench-core 0.0.46

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.
Files changed (72) hide show
  1. package/dist/adapter-auth.d.ts +63 -0
  2. package/dist/adapter-auth.d.ts.map +1 -0
  3. package/dist/adapter-auth.js +244 -0
  4. package/dist/execution-events.d.ts +53 -0
  5. package/dist/execution-events.d.ts.map +1 -0
  6. package/dist/execution-events.js +195 -0
  7. package/dist/execution-graph.d.ts +27 -0
  8. package/dist/execution-graph.d.ts.map +1 -0
  9. package/dist/execution-graph.js +126 -0
  10. package/dist/execution-jobs.d.ts +70 -0
  11. package/dist/execution-jobs.d.ts.map +1 -0
  12. package/dist/execution-jobs.js +229 -0
  13. package/dist/execution-outputs.d.ts +9 -0
  14. package/dist/execution-outputs.d.ts.map +1 -0
  15. package/dist/execution-outputs.js +393 -0
  16. package/dist/execution-phases.d.ts +21 -0
  17. package/dist/execution-phases.d.ts.map +1 -0
  18. package/dist/execution-phases.js +262 -0
  19. package/dist/execution-runtime-types.d.ts +35 -0
  20. package/dist/execution-runtime-types.d.ts.map +1 -0
  21. package/dist/execution-runtime-types.js +1 -0
  22. package/dist/execution-scheduler.d.ts +31 -0
  23. package/dist/execution-scheduler.d.ts.map +1 -0
  24. package/dist/execution-scheduler.js +241 -0
  25. package/dist/execution-traces.d.ts +16 -0
  26. package/dist/execution-traces.d.ts.map +1 -0
  27. package/dist/execution-traces.js +164 -0
  28. package/dist/execution-usage.d.ts +12 -0
  29. package/dist/execution-usage.d.ts.map +1 -0
  30. package/dist/execution-usage.js +433 -0
  31. package/dist/generic-spec.d.ts +113 -0
  32. package/dist/generic-spec.d.ts.map +1 -0
  33. package/dist/generic-spec.js +656 -0
  34. package/dist/index.d.ts +160 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +2858 -0
  37. package/dist/model-prices-litellm.d.ts +9674 -0
  38. package/dist/model-prices-litellm.d.ts.map +1 -0
  39. package/dist/model-prices-litellm.js +9668 -0
  40. package/dist/runtime-utils.d.ts +18 -0
  41. package/dist/runtime-utils.d.ts.map +1 -0
  42. package/dist/runtime-utils.js +108 -0
  43. package/dist/sandbox-backends/docker.d.ts +5 -0
  44. package/dist/sandbox-backends/docker.d.ts.map +1 -0
  45. package/dist/sandbox-backends/docker.js +568 -0
  46. package/dist/sandbox-backends/index.d.ts +37 -0
  47. package/dist/sandbox-backends/index.d.ts.map +1 -0
  48. package/dist/sandbox-backends/index.js +79 -0
  49. package/dist/sandbox-backends/names.d.ts +6 -0
  50. package/dist/sandbox-backends/names.d.ts.map +1 -0
  51. package/dist/sandbox-backends/names.js +14 -0
  52. package/dist/sandbox-backends/template-images.d.ts +4 -0
  53. package/dist/sandbox-backends/template-images.d.ts.map +1 -0
  54. package/dist/sandbox-backends/template-images.js +48 -0
  55. package/dist/sandbox-inputs.d.ts +27 -0
  56. package/dist/sandbox-inputs.d.ts.map +1 -0
  57. package/dist/sandbox-inputs.js +220 -0
  58. package/dist/sandbox-plane.d.ts +89 -0
  59. package/dist/sandbox-plane.d.ts.map +1 -0
  60. package/dist/sandbox-plane.js +327 -0
  61. package/dist/subject-patch.d.ts +8 -0
  62. package/dist/subject-patch.d.ts.map +1 -0
  63. package/dist/subject-patch.js +63 -0
  64. package/dist/trace-files.d.ts +18 -0
  65. package/dist/trace-files.d.ts.map +1 -0
  66. package/dist/trace-files.js +94 -0
  67. package/environments/libreoffice-agent/Dockerfile +13 -0
  68. package/environments/libreoffice-python/Dockerfile +11 -0
  69. package/environments/node-22/Dockerfile +3 -0
  70. package/environments/python-3.12/Dockerfile +8 -0
  71. package/package.json +42 -0
  72. package/worker/sandbox-adapter-runner.cjs +275 -0
@@ -0,0 +1,63 @@
1
+ export type WorkbenchAdapterAuthStatus = "connected" | "reauth_required" | "disconnected";
2
+ export interface WorkbenchAdapterAuthTarget {
3
+ adapterId: string;
4
+ slot?: string;
5
+ profile: string;
6
+ }
7
+ export interface WorkbenchAdapterAuthFile {
8
+ path: string;
9
+ content: string;
10
+ encoding: "utf8" | "base64";
11
+ mode?: number;
12
+ }
13
+ export interface WorkbenchAdapterAuthEnvVar {
14
+ name: string;
15
+ value: string;
16
+ }
17
+ export interface WorkbenchAdapterAuthBundle {
18
+ adapterId: string;
19
+ slot?: string;
20
+ profile: string;
21
+ method: string;
22
+ status: "connected";
23
+ version: number;
24
+ files: WorkbenchAdapterAuthFile[];
25
+ env?: WorkbenchAdapterAuthEnvVar[];
26
+ updatedAt: string;
27
+ }
28
+ export interface WorkbenchAdapterAuthStatusRecord {
29
+ adapterId: string;
30
+ slot?: string;
31
+ profile: string;
32
+ status: WorkbenchAdapterAuthStatus;
33
+ version: number;
34
+ method?: string;
35
+ updatedAt?: string;
36
+ reason?: string;
37
+ }
38
+ export interface WorkbenchAdapterAuthStore {
39
+ get(target: WorkbenchAdapterAuthTarget): Promise<WorkbenchAdapterAuthBundle | null>;
40
+ put(bundle: WorkbenchAdapterAuthBundle): Promise<WorkbenchAdapterAuthBundle>;
41
+ disconnect(target: WorkbenchAdapterAuthTarget, reason?: string): Promise<void>;
42
+ markReauthRequired(target: WorkbenchAdapterAuthTarget, reason: string): Promise<void>;
43
+ status(target: WorkbenchAdapterAuthTarget): Promise<WorkbenchAdapterAuthStatusRecord>;
44
+ listStatus(): Promise<WorkbenchAdapterAuthStatusRecord[]>;
45
+ }
46
+ export declare function defaultWorkbenchAdapterAuthStoreRoot(): string;
47
+ export declare function localWorkbenchAdapterAuthStore(root?: string): WorkbenchAdapterAuthStore;
48
+ export declare function parseWorkbenchAdapterAuthTarget(value: string, profile?: string): WorkbenchAdapterAuthTarget;
49
+ export declare function normalizeWorkbenchAdapterAuthTarget(target: {
50
+ adapterId: string;
51
+ slot?: string;
52
+ profile?: string;
53
+ }): WorkbenchAdapterAuthTarget;
54
+ export declare function createWorkbenchAdapterAuthBundle(args: {
55
+ target: WorkbenchAdapterAuthTarget;
56
+ method: string;
57
+ files?: WorkbenchAdapterAuthFile[];
58
+ env?: Record<string, string> | WorkbenchAdapterAuthEnvVar[];
59
+ now?: string;
60
+ }): WorkbenchAdapterAuthBundle;
61
+ export declare function adapterAuthEnv(bundle: WorkbenchAdapterAuthBundle): Record<string, string>;
62
+ export declare function sanitizeWorkbenchAdapterAuthBundle(value: unknown): WorkbenchAdapterAuthBundle;
63
+ //# sourceMappingURL=adapter-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-auth.d.ts","sourceRoot":"","sources":["../src/adapter-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,244 @@
1
+ import { promises as fs } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const ADAPTER_AUTH_STORE_VERSION = 1;
5
+ const DEFAULT_AUTH_PROFILE = "default";
6
+ export function defaultWorkbenchAdapterAuthStoreRoot() {
7
+ return path.join(os.homedir(), ".workbench", "adapter-auth");
8
+ }
9
+ export function localWorkbenchAdapterAuthStore(root = defaultWorkbenchAdapterAuthStoreRoot()) {
10
+ return new FileWorkbenchAdapterAuthStore(root);
11
+ }
12
+ export function parseWorkbenchAdapterAuthTarget(value, profile = DEFAULT_AUTH_PROFILE) {
13
+ const [adapterId, slot, ...rest] = value.split("/");
14
+ if (!adapterId || rest.length > 0) {
15
+ throw new Error("Adapter auth target must be adapter or adapter/slot.");
16
+ }
17
+ return normalizeWorkbenchAdapterAuthTarget({
18
+ adapterId,
19
+ ...(slot ? { slot } : {}),
20
+ profile,
21
+ });
22
+ }
23
+ export function normalizeWorkbenchAdapterAuthTarget(target) {
24
+ const adapterId = readAdapterAuthSegment(target.adapterId, "adapter id");
25
+ const slot = target.slot === undefined
26
+ ? undefined
27
+ : readAdapterAuthSegment(target.slot, "auth slot");
28
+ const profile = target.profile === undefined || target.profile === ""
29
+ ? DEFAULT_AUTH_PROFILE
30
+ : readAdapterAuthSegment(target.profile, "auth profile");
31
+ return {
32
+ adapterId,
33
+ ...(slot ? { slot } : {}),
34
+ profile,
35
+ };
36
+ }
37
+ export function createWorkbenchAdapterAuthBundle(args) {
38
+ const files = args.files ?? [];
39
+ const env = Array.isArray(args.env)
40
+ ? args.env
41
+ : Object.entries(args.env ?? {}).map(([name, value]) => ({ name, value }));
42
+ if (files.length === 0 && env.length === 0) {
43
+ throw new Error("Adapter auth requires at least one file or env var.");
44
+ }
45
+ return sanitizeWorkbenchAdapterAuthBundle({
46
+ ...args.target,
47
+ method: args.method,
48
+ status: "connected",
49
+ version: ADAPTER_AUTH_STORE_VERSION,
50
+ files,
51
+ ...(env.length > 0 ? { env } : {}),
52
+ updatedAt: args.now ?? new Date().toISOString(),
53
+ });
54
+ }
55
+ export function adapterAuthEnv(bundle) {
56
+ return Object.fromEntries((bundle.env ?? []).map((entry) => [entry.name, entry.value]));
57
+ }
58
+ export function sanitizeWorkbenchAdapterAuthBundle(value) {
59
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
60
+ throw new Error("Adapter auth bundle must be an object.");
61
+ }
62
+ const record = value;
63
+ const target = normalizeWorkbenchAdapterAuthTarget({
64
+ adapterId: readString(record.adapterId, "adapterId"),
65
+ ...(record.slot !== undefined ? { slot: readString(record.slot, "slot") } : {}),
66
+ profile: typeof record.profile === "string" ? record.profile : DEFAULT_AUTH_PROFILE,
67
+ });
68
+ if (record.status !== "connected") {
69
+ throw new Error("Adapter auth bundle must be connected.");
70
+ }
71
+ const method = readAdapterAuthSegment(readString(record.method, "method"), "auth method");
72
+ const files = Array.isArray(record.files)
73
+ ? record.files.map(sanitizeWorkbenchAdapterAuthFile)
74
+ : [];
75
+ const env = Array.isArray(record.env)
76
+ ? record.env.map(sanitizeWorkbenchAdapterAuthEnvVar)
77
+ : [];
78
+ return {
79
+ ...target,
80
+ method,
81
+ status: "connected",
82
+ version: ADAPTER_AUTH_STORE_VERSION,
83
+ files,
84
+ ...(env.length > 0 ? { env } : {}),
85
+ updatedAt: typeof record.updatedAt === "string"
86
+ ? record.updatedAt
87
+ : new Date().toISOString(),
88
+ };
89
+ }
90
+ class FileWorkbenchAdapterAuthStore {
91
+ root;
92
+ constructor(root) {
93
+ this.root = root;
94
+ }
95
+ async get(target) {
96
+ const record = await this.readRecord(target);
97
+ return record?.status === "connected" && record.bundle
98
+ ? sanitizeWorkbenchAdapterAuthBundle(record.bundle)
99
+ : null;
100
+ }
101
+ async put(bundle) {
102
+ const sanitized = sanitizeWorkbenchAdapterAuthBundle(bundle);
103
+ const existing = await this.readRecord(sanitized).catch(() => null);
104
+ const saved = {
105
+ ...sanitized,
106
+ version: Math.max(0, existing?.version ?? 0) + 1,
107
+ updatedAt: new Date().toISOString(),
108
+ };
109
+ await this.writeRecord({
110
+ target: normalizeWorkbenchAdapterAuthTarget(saved),
111
+ status: "connected",
112
+ version: saved.version,
113
+ updatedAt: saved.updatedAt,
114
+ bundle: saved,
115
+ });
116
+ return saved;
117
+ }
118
+ async disconnect(target, reason = "disconnected") {
119
+ const normalized = normalizeWorkbenchAdapterAuthTarget(target);
120
+ const existing = await this.readRecord(normalized).catch(() => null);
121
+ await this.writeRecord({
122
+ target: normalized,
123
+ status: "disconnected",
124
+ version: Math.max(0, existing?.version ?? 0) + 1,
125
+ updatedAt: new Date().toISOString(),
126
+ reason,
127
+ });
128
+ }
129
+ async markReauthRequired(target, reason) {
130
+ const normalized = normalizeWorkbenchAdapterAuthTarget(target);
131
+ const existing = await this.readRecord(normalized).catch(() => null);
132
+ await this.writeRecord({
133
+ target: normalized,
134
+ status: "reauth_required",
135
+ version: Math.max(0, existing?.version ?? 0),
136
+ updatedAt: new Date().toISOString(),
137
+ reason,
138
+ bundle: existing?.bundle,
139
+ });
140
+ }
141
+ async status(target) {
142
+ const normalized = normalizeWorkbenchAdapterAuthTarget(target);
143
+ const record = await this.readRecord(normalized);
144
+ if (!record) {
145
+ return { ...normalized, status: "disconnected", version: 0 };
146
+ }
147
+ return statusFromRecord(record);
148
+ }
149
+ async listStatus() {
150
+ const files = await fs.readdir(this.root).catch(() => []);
151
+ const records = await Promise.all(files
152
+ .filter((file) => file.endsWith(".json"))
153
+ .map(async (file) => await this.readRecordFile(path.join(this.root, file))));
154
+ return records
155
+ .filter((record) => Boolean(record))
156
+ .map(statusFromRecord)
157
+ .sort((left, right) => adapterAuthTargetKey(left).localeCompare(adapterAuthTargetKey(right)));
158
+ }
159
+ async readRecord(target) {
160
+ return await this.readRecordFile(this.recordPath(target));
161
+ }
162
+ async readRecordFile(filePath) {
163
+ const source = await fs.readFile(filePath, "utf8").catch((error) => {
164
+ if (error.code === "ENOENT") {
165
+ return null;
166
+ }
167
+ throw error;
168
+ });
169
+ if (source === null) {
170
+ return null;
171
+ }
172
+ const parsed = JSON.parse(source);
173
+ return parsed?.target ? parsed : null;
174
+ }
175
+ async writeRecord(record) {
176
+ await fs.mkdir(this.root, { recursive: true, mode: 0o700 });
177
+ await fs.writeFile(this.recordPath(record.target), `${JSON.stringify(record, null, 2)}\n`, {
178
+ mode: 0o600,
179
+ });
180
+ }
181
+ recordPath(target) {
182
+ return path.join(this.root, `${adapterAuthTargetKey(target)}.json`);
183
+ }
184
+ }
185
+ function statusFromRecord(record) {
186
+ return {
187
+ ...record.target,
188
+ status: record.status,
189
+ version: record.version,
190
+ ...(record.bundle?.method ? { method: record.bundle.method } : {}),
191
+ ...(record.updatedAt ? { updatedAt: record.updatedAt } : {}),
192
+ ...(record.reason ? { reason: record.reason } : {}),
193
+ };
194
+ }
195
+ function adapterAuthTargetKey(target) {
196
+ return [
197
+ target.adapterId,
198
+ target.slot ?? "_",
199
+ target.profile,
200
+ ].join("__");
201
+ }
202
+ function sanitizeWorkbenchAdapterAuthFile(value) {
203
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
204
+ throw new Error("Adapter auth file must be an object.");
205
+ }
206
+ const record = value;
207
+ const filePath = readString(record.path, "file.path").replace(/\\/gu, "/").replace(/^\/+/u, "");
208
+ if (!filePath || filePath.split("/").some((part) => part === "." || part === ".." || part === "")) {
209
+ throw new Error(`Unsafe adapter auth file path: ${filePath}`);
210
+ }
211
+ return {
212
+ path: filePath,
213
+ content: readString(record.content, "file.content"),
214
+ encoding: record.encoding === "base64" ? "base64" : "utf8",
215
+ ...(typeof record.mode === "number" && Number.isInteger(record.mode) ? { mode: record.mode } : {}),
216
+ };
217
+ }
218
+ function sanitizeWorkbenchAdapterAuthEnvVar(value) {
219
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
220
+ throw new Error("Adapter auth env entry must be an object.");
221
+ }
222
+ const record = value;
223
+ const name = readString(record.name, "env.name");
224
+ const envValue = readString(record.value, "env.value");
225
+ if (!/^[A-Z_][A-Z0-9_]*$/u.test(name)) {
226
+ throw new Error(`Adapter auth env var is invalid: ${name}`);
227
+ }
228
+ if (!envValue.trim()) {
229
+ throw new Error(`Adapter auth env var ${name} is empty.`);
230
+ }
231
+ return { name, value: envValue };
232
+ }
233
+ function readString(value, label) {
234
+ if (typeof value !== "string" || value.length === 0) {
235
+ throw new Error(`Adapter auth ${label} must be a non-empty string.`);
236
+ }
237
+ return value;
238
+ }
239
+ function readAdapterAuthSegment(value, label) {
240
+ if (!/^[a-z][a-z0-9-]*$/u.test(value)) {
241
+ throw new Error(`Adapter auth ${label} must be a lowercase identifier.`);
242
+ }
243
+ return value;
244
+ }
@@ -0,0 +1,53 @@
1
+ import type { Json, WorkbenchExecutionEvent, WorkbenchExecutionEventBatch, WorkbenchExecutionEventSource } from "@workbench-ai/workbench-contract";
2
+ export interface WorkbenchExecutionProgressTarget {
3
+ url: string;
4
+ token: string;
5
+ ownerUserId?: string;
6
+ flushWindowMs?: number;
7
+ transport?: "http" | "stdout" | "both";
8
+ }
9
+ export interface WorkbenchExecutionEventPublisherContext {
10
+ projectId: string;
11
+ runId: string;
12
+ jobId: string;
13
+ executionId: string;
14
+ attempt: number;
15
+ target?: WorkbenchExecutionProgressTarget;
16
+ }
17
+ export interface WorkbenchExecutionEventInput {
18
+ at?: string;
19
+ source: WorkbenchExecutionEventSource;
20
+ role?: WorkbenchExecutionEvent["role"];
21
+ schema: WorkbenchExecutionEvent["schema"];
22
+ payload: Json;
23
+ }
24
+ export interface WorkbenchExecutionEventPublisher {
25
+ readonly enabled: boolean;
26
+ readonly flushWindowMs?: number;
27
+ publish(events: readonly WorkbenchExecutionEventInput[]): Promise<void>;
28
+ flush(): Promise<void>;
29
+ }
30
+ export declare const WORKBENCH_PROGRESS_STDOUT_PREFIX = "__WORKBENCH_PROGRESS__";
31
+ export declare function createWorkbenchExecutionEventPublisher(context: WorkbenchExecutionEventPublisherContext): WorkbenchExecutionEventPublisher;
32
+ export declare function publishCommandPhaseEvent(publisher: WorkbenchExecutionEventPublisher | undefined, args: {
33
+ phase: string;
34
+ status: "started" | "succeeded" | "failed";
35
+ role?: WorkbenchExecutionEvent["role"];
36
+ exitCode?: number;
37
+ error?: string;
38
+ }): Promise<void>;
39
+ export interface WorkbenchProgressStdoutEnvelope {
40
+ url: string;
41
+ body: {
42
+ type: "workbench.job.progress";
43
+ ownerUserId?: string;
44
+ progressToken: string;
45
+ batch: WorkbenchExecutionEventBatch;
46
+ };
47
+ }
48
+ export declare function createWorkbenchProgressStdoutParser(onEnvelope: (envelope: WorkbenchProgressStdoutEnvelope) => void): {
49
+ write(chunk: Buffer | string): void;
50
+ flush(): void;
51
+ };
52
+ export declare function publishWorkbenchProgressStdoutEnvelope(envelope: WorkbenchProgressStdoutEnvelope): Promise<void>;
53
+ //# sourceMappingURL=execution-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-events.d.ts","sourceRoot":"","sources":["../src/execution-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,kCAAkC,CAAC;AAE1C,MAAM,WAAW,gCAAgC;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,uCAAuC;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,gCAAgC,CAAC;CAC3C;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,6BAA6B,CAAC;IACtC,IAAI,CAAC,EAAE,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1C,OAAO,EAAE,IAAI,CAAC;CACf;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,MAAM,EAAE,SAAS,4BAA4B,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,eAAO,MAAM,gCAAgC,2BAA2B,CAAC;AAEzE,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,uCAAuC,GAC/C,gCAAgC,CAsClC;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,gCAAgC,GAAG,SAAS,EACvD,IAAI,EAAE;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,EAAE,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACA,OAAO,CAAC,IAAI,CAAC,CAef;AAyCD,MAAM,WAAW,+BAA+B;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE;QACJ,IAAI,EAAE,wBAAwB,CAAC;QAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,4BAA4B,CAAC;KACrC,CAAC;CACH;AAED,wBAAgB,mCAAmC,CACjD,UAAU,EAAE,CAAC,QAAQ,EAAE,+BAA+B,KAAK,IAAI,GAC9D;IACD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,KAAK,IAAI,IAAI,CAAC;CACf,CA0DA;AAED,wBAAsB,sCAAsC,CAC1D,QAAQ,EAAE,+BAA+B,GACxC,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -0,0 +1,195 @@
1
+ export const WORKBENCH_PROGRESS_STDOUT_PREFIX = "__WORKBENCH_PROGRESS__";
2
+ export function createWorkbenchExecutionEventPublisher(context) {
3
+ const target = validProgressTarget(context.target);
4
+ if (!target) {
5
+ return NOOP_EXECUTION_EVENT_PUBLISHER;
6
+ }
7
+ let seq = 0;
8
+ return {
9
+ enabled: true,
10
+ flushWindowMs: target.flushWindowMs,
11
+ async publish(inputs) {
12
+ if (inputs.length === 0) {
13
+ return;
14
+ }
15
+ const events = inputs.map((input) => ({
16
+ seq: ++seq,
17
+ at: input.at ?? new Date().toISOString(),
18
+ source: input.source,
19
+ ...(input.role ? { role: input.role } : {}),
20
+ schema: input.schema,
21
+ payload: input.payload,
22
+ }));
23
+ const batch = {
24
+ projectId: context.projectId,
25
+ runId: context.runId,
26
+ jobId: context.jobId,
27
+ executionId: context.executionId,
28
+ attempt: Math.max(1, Math.floor(context.attempt || 1)),
29
+ seqStart: events[0].seq,
30
+ seqEnd: events[events.length - 1].seq,
31
+ emittedAt: new Date().toISOString(),
32
+ events,
33
+ };
34
+ await publishProgressBatch(target, batch);
35
+ },
36
+ async flush() {
37
+ return;
38
+ },
39
+ };
40
+ }
41
+ export async function publishCommandPhaseEvent(publisher, args) {
42
+ if (!publisher?.enabled) {
43
+ return;
44
+ }
45
+ await publisher.publish([{
46
+ source: "command",
47
+ ...(args.role ? { role: args.role } : {}),
48
+ schema: "workbench.execution.phase.v1",
49
+ payload: {
50
+ phase: args.phase,
51
+ status: args.status,
52
+ ...(typeof args.exitCode === "number" ? { exitCode: args.exitCode } : {}),
53
+ ...(args.error ? { error: args.error } : {}),
54
+ },
55
+ }]).catch(() => undefined);
56
+ }
57
+ const NOOP_EXECUTION_EVENT_PUBLISHER = {
58
+ enabled: false,
59
+ async publish() {
60
+ return;
61
+ },
62
+ async flush() {
63
+ return;
64
+ },
65
+ };
66
+ function validProgressTarget(target) {
67
+ if (!target?.url.trim() || !target.token.trim()) {
68
+ return null;
69
+ }
70
+ return {
71
+ url: target.url.trim(),
72
+ token: target.token,
73
+ ...(typeof target.ownerUserId === "string" && target.ownerUserId.trim() ? { ownerUserId: target.ownerUserId.trim() } : {}),
74
+ ...(typeof target.flushWindowMs === "number" && Number.isFinite(target.flushWindowMs) && target.flushWindowMs >= 0
75
+ ? { flushWindowMs: target.flushWindowMs }
76
+ : {}),
77
+ ...(target.transport === "stdout" || target.transport === "both" ? { transport: target.transport } : { transport: "http" }),
78
+ };
79
+ }
80
+ async function publishProgressBatch(target, batch) {
81
+ const envelope = progressStdoutEnvelope(target, batch);
82
+ if (target.transport === "stdout" || target.transport === "both") {
83
+ process.stdout.write(`${WORKBENCH_PROGRESS_STDOUT_PREFIX}${JSON.stringify(envelope)}\n`);
84
+ }
85
+ if (target.transport === "stdout") {
86
+ return;
87
+ }
88
+ await postProgressBody(envelope);
89
+ }
90
+ export function createWorkbenchProgressStdoutParser(onEnvelope) {
91
+ let buffer = "";
92
+ const consumeRawEnvelope = (rawEnvelope) => {
93
+ const raw = rawEnvelope.trim();
94
+ if (!raw) {
95
+ return;
96
+ }
97
+ try {
98
+ const parsed = JSON.parse(raw);
99
+ if (isProgressStdoutEnvelope(parsed)) {
100
+ onEnvelope(parsed);
101
+ }
102
+ }
103
+ catch {
104
+ // Progress telemetry is best-effort and must not crash the sandbox host.
105
+ }
106
+ };
107
+ const drain = (final) => {
108
+ for (;;) {
109
+ const prefixIndex = buffer.indexOf(WORKBENCH_PROGRESS_STDOUT_PREFIX);
110
+ if (prefixIndex < 0) {
111
+ buffer = buffer.length > WORKBENCH_PROGRESS_STDOUT_PREFIX.length
112
+ ? buffer.slice(-WORKBENCH_PROGRESS_STDOUT_PREFIX.length)
113
+ : buffer;
114
+ return;
115
+ }
116
+ if (prefixIndex > 0) {
117
+ buffer = buffer.slice(prefixIndex);
118
+ }
119
+ const payloadStart = WORKBENCH_PROGRESS_STDOUT_PREFIX.length;
120
+ const delimiterIndex = firstExistingIndex([
121
+ buffer.indexOf("\n", payloadStart),
122
+ buffer.indexOf("\r", payloadStart),
123
+ buffer.indexOf(WORKBENCH_PROGRESS_STDOUT_PREFIX, payloadStart),
124
+ ]);
125
+ if (delimiterIndex < 0) {
126
+ if (!final) {
127
+ return;
128
+ }
129
+ consumeRawEnvelope(buffer.slice(payloadStart));
130
+ buffer = "";
131
+ return;
132
+ }
133
+ consumeRawEnvelope(buffer.slice(payloadStart, delimiterIndex));
134
+ buffer = buffer.slice(delimiterIndex);
135
+ while (buffer.startsWith("\n") || buffer.startsWith("\r")) {
136
+ buffer = buffer.slice(1);
137
+ }
138
+ }
139
+ };
140
+ return {
141
+ write(chunk) {
142
+ buffer += Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk;
143
+ drain(false);
144
+ },
145
+ flush() {
146
+ drain(true);
147
+ },
148
+ };
149
+ }
150
+ export async function publishWorkbenchProgressStdoutEnvelope(envelope) {
151
+ await postProgressBody(envelope);
152
+ }
153
+ function firstExistingIndex(indexes) {
154
+ return indexes.filter((index) => index >= 0).sort((a, b) => a - b)[0] ?? -1;
155
+ }
156
+ function progressStdoutEnvelope(target, batch) {
157
+ return {
158
+ url: target.url,
159
+ body: {
160
+ type: "workbench.job.progress",
161
+ ...(target.ownerUserId ? { ownerUserId: target.ownerUserId } : {}),
162
+ progressToken: target.token,
163
+ batch,
164
+ },
165
+ };
166
+ }
167
+ async function postProgressBody(envelope) {
168
+ const response = await fetch(envelope.url, {
169
+ method: "POST",
170
+ headers: {
171
+ "content-type": "application/json",
172
+ },
173
+ body: JSON.stringify(envelope.body),
174
+ });
175
+ if (!response.ok) {
176
+ throw new Error(`Workbench progress publish failed with HTTP ${response.status}.`);
177
+ }
178
+ }
179
+ function isProgressStdoutEnvelope(value) {
180
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
181
+ return false;
182
+ }
183
+ const record = value;
184
+ if (typeof record.url !== "string" || record.url.length === 0) {
185
+ return false;
186
+ }
187
+ const body = record.body;
188
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
189
+ return false;
190
+ }
191
+ const bodyRecord = body;
192
+ return bodyRecord.type === "workbench.job.progress"
193
+ && typeof bodyRecord.progressToken === "string"
194
+ && Boolean(bodyRecord.batch);
195
+ }
@@ -0,0 +1,27 @@
1
+ import type { WorkbenchExecutionSpec } from "@workbench-ai/workbench-contract";
2
+ import { type GenericRunSpec, type GenericEngineCaseSpec } from "./generic-spec.ts";
3
+ export interface CompileExecutionGraphInput {
4
+ ownerUserId: string;
5
+ projectId: string;
6
+ runId: string;
7
+ subjectId: string;
8
+ attemptIndex: number;
9
+ sampleIndex?: number;
10
+ caseId?: string;
11
+ engineCase?: GenericEngineCaseSpec;
12
+ spec: GenericRunSpec;
13
+ workflow?: "eval" | "improve";
14
+ subjectRef?: string;
15
+ caseRef?: string;
16
+ environmentRef?: string;
17
+ }
18
+ export interface WorkbenchExecutionGraph {
19
+ nodes: WorkbenchExecutionGraphNode[];
20
+ executions: WorkbenchExecutionSpec[];
21
+ }
22
+ export interface WorkbenchExecutionGraphNode {
23
+ execution: WorkbenchExecutionSpec;
24
+ dependsOn: string[];
25
+ }
26
+ export declare function compileWorkbenchExecutionGraph(input: CompileExecutionGraphInput): WorkbenchExecutionGraph;
27
+ //# sourceMappingURL=execution-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-graph.d.ts","sourceRoot":"","sources":["../src/execution-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,sBAAsB,EAEvB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAE3B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,qBAAqB,CAAC;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,2BAA2B,EAAE,CAAC;IACrC,UAAU,EAAE,sBAAsB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,sBAAsB,CAAC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,0BAA0B,GAAG,uBAAuB,CAoEzG"}