@workbench-ai/workbench 0.0.63 → 0.0.65

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,5 +1,4 @@
1
- import { type RunSummary, type SurfaceSnapshotFile } from "@workbench-ai/workbench-core";
2
- import { type LocalProjectSource } from "./project-source.js";
1
+ import type { WorkbenchInspection } from "@workbench-ai/workbench-core";
3
2
  export interface LocalWorkbenchDevServer {
4
3
  url: string;
5
4
  close: () => Promise<void>;
@@ -13,41 +12,7 @@ export interface LocalWorkbenchDevServerOptions {
13
12
  export interface LocalWorkbenchRequestContext {
14
13
  workspace: string;
15
14
  assetsRoot: string;
16
- readProjectSource: () => Promise<LocalProjectSource>;
15
+ inspection: WorkbenchInspection;
17
16
  }
18
17
  export declare function startLocalWorkbenchDevServer(options: LocalWorkbenchDevServerOptions): Promise<LocalWorkbenchDevServer>;
19
- export declare function localBenchmarkSnapshot(context: LocalWorkbenchRequestContext): Promise<{
20
- workspaceRoot: string;
21
- activeId: string | null;
22
- currentBenchmarkFingerprint: string | null;
23
- summaries: import("@workbench-ai/workbench-contract").CandidateSummary[];
24
- evaluations: {
25
- id: string;
26
- runId: string;
27
- benchmarkFingerprint: string;
28
- candidateFingerprint: string;
29
- candidateId: string;
30
- candidateName?: string;
31
- candidateVersion: number;
32
- candidateRunId?: string;
33
- candidateRunName?: string;
34
- createdAt: string;
35
- updatedAt: string;
36
- status: import("@workbench-ai/workbench-contract").EvaluationStatus;
37
- sampleCount: number;
38
- completedSampleCount: number;
39
- errorSampleCount: number;
40
- metrics?: Record<string, import("@workbench-ai/workbench-contract").MetricStats>;
41
- selectionMetric?: string;
42
- selectionLabel?: string;
43
- selectionScore?: import("@workbench-ai/workbench-contract").MetricStats;
44
- durationMs?: import("@workbench-ai/workbench-contract").MetricStats;
45
- usage?: import("@workbench-ai/workbench-contract").EvaluationUsageStats;
46
- error?: string;
47
- }[];
48
- runs: RunSummary[];
49
- }>;
50
- export declare function localSpecDocument(context: LocalWorkbenchRequestContext, benchmarkFingerprint?: string | null): Promise<import("@workbench-ai/workbench-contract").AuthoredWorkbenchSourceDocument>;
51
- export declare function localSourceFiles(workspace: string): Promise<SurfaceSnapshotFile[]>;
52
- export declare function localBenchmarkMountedFiles(context: LocalWorkbenchRequestContext, benchmarkFingerprint?: string | null): Promise<SurfaceSnapshotFile[]>;
53
18
  //# sourceMappingURL=dev-open-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev-open-server.d.ts","sourceRoot":"","sources":["../src/dev-open-server.ts"],"names":[],"mappings":"AAKA,OAAO,EAYL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAIzB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkBD,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACtD;AAKD,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC,CAwClC;AAoQD,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiBjF;AAeD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,4BAA4B,EACrC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,uFAiCrC;AAwBD,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAExF;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,4BAA4B,EACrC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,GACnC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAchC"}
1
+ {"version":3,"file":"dev-open-server.d.ts","sourceRoot":"","sources":["../src/dev-open-server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAOxE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAWD,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAID,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC,CA4ClC"}
@@ -2,10 +2,7 @@ import { promises as fs } from "node:fs";
2
2
  import http from "node:http";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { buildCandidateCaseExecutionRefs, buildWorkbenchExecutionEvidence, candidateRecordWithoutDerivedFields, candidateSummaryFromRecord, createCandidateFilePreview, createCaseReview, loadAuthoredWorkbenchSourceDocument, summarizeCandidateFiles, traceSessionLabel, } from "@workbench-ai/workbench-core";
6
- import { readLocalExecutionFiles, loadLocalArchiveIndex, readLocalEvaluationRecord, readLocalJobInRun, readLocalRunJobs, readLocalCandidateFilesForId, readLocalCandidateRecord, } from "./local-archive.js";
7
- import { readLocalAuthoredProjectSource, readLocalProjectSource, WORKBENCH_BENCHMARK_FILE, } from "./project-source.js";
8
- import { localBenchmarkFingerprint } from "./benchmark-fingerprint.js";
5
+ import { createLocalProjectSourceReader, createLocalWorkbenchInspection, } from "./local-inspection.js";
9
6
  class LocalApiError extends Error {
10
7
  status;
11
8
  constructor(message, status = 400) {
@@ -14,15 +11,18 @@ class LocalApiError extends Error {
14
11
  }
15
12
  }
16
13
  const DEV_OPEN_ASSET_DIR = "dev-open";
17
- const PROJECT_SOURCE_CACHE_TTL_MS = 1000;
18
14
  export async function startLocalWorkbenchDevServer(options) {
19
15
  const workspace = path.resolve(options.workspace);
20
16
  const assetsRoot = options.assetsRoot ?? defaultDevOpenAssetsRoot();
21
17
  await assertDevOpenAssets(assetsRoot);
18
+ const readProjectSource = createLocalProjectSourceReader(workspace);
22
19
  const context = {
23
20
  workspace,
24
21
  assetsRoot,
25
- readProjectSource: createProjectSourceReader(workspace),
22
+ inspection: createLocalWorkbenchInspection({
23
+ workspace,
24
+ readProjectSource,
25
+ }),
26
26
  };
27
27
  const server = http.createServer((request, response) => {
28
28
  void handleLocalWorkbenchRequest({
@@ -64,23 +64,6 @@ async function assertDevOpenAssets(assetsRoot) {
64
64
  throw new Error(`Workbench local browser assets are missing from ${assetsRoot}. Run pnpm --dir products/workbench/packages/cli build.`);
65
65
  });
66
66
  }
67
- function createProjectSourceReader(workspace) {
68
- let cached = null;
69
- return () => {
70
- const now = Date.now();
71
- if (cached && now - cached.loadedAt < PROJECT_SOURCE_CACHE_TTL_MS) {
72
- return cached.promise;
73
- }
74
- const promise = readLocalProjectSource(workspace);
75
- cached = { loadedAt: now, promise };
76
- promise.catch(() => {
77
- if (cached?.promise === promise) {
78
- cached = null;
79
- }
80
- });
81
- return promise;
82
- };
83
- }
84
67
  async function closeServer(server) {
85
68
  await new Promise((resolve, reject) => {
86
69
  server.close((error) => error ? reject(error) : resolve());
@@ -138,40 +121,43 @@ function decodeDocumentPathSegment(segment) {
138
121
  }
139
122
  }
140
123
  async function handleApiRequest(request, response, context, url) {
141
- const { workspace } = context;
124
+ const { inspection } = context;
142
125
  switch (url.pathname) {
143
126
  case "/api/snapshot":
144
- sendJson(response, await localBenchmarkSnapshot(context), 200, request.method);
127
+ sendJson(response, await inspection.snapshot(), 200, request.method);
145
128
  return;
146
129
  case "/api/spec":
147
- sendJson(response, await localSpecDocument(context, readOptionalSearchString(url.searchParams, "fingerprint")), 200, request.method);
130
+ sendJson(response, await inspection.spec({
131
+ fingerprint: readOptionalSearchString(url.searchParams, "fingerprint"),
132
+ }), 200, request.method);
148
133
  return;
149
134
  case "/api/source/files":
150
- sendJson(response, summarizeCandidateFiles(await localBenchmarkMountedFiles(context, readOptionalSearchString(url.searchParams, "fingerprint")), []), 200, request.method);
135
+ sendJson(response, await inspection.sourceFiles({
136
+ fingerprint: readOptionalSearchString(url.searchParams, "fingerprint"),
137
+ }), 200, request.method);
151
138
  return;
152
139
  case "/api/source/preview":
153
- sendJson(response, createCandidateFilePreview({
154
- files: await localBenchmarkMountedFiles(context, readOptionalSearchString(url.searchParams, "fingerprint")),
140
+ sendJson(response, await inspection.sourcePreview({
141
+ fingerprint: readOptionalSearchString(url.searchParams, "fingerprint"),
155
142
  path: readSearchString(url.searchParams, "path"),
156
143
  view: readPreviewMode(url.searchParams),
157
144
  }), 200, request.method);
158
145
  return;
159
146
  case "/api/record":
160
- sendJson(response, await readCandidateForApi(workspace, readSearchString(url.searchParams, "id")), 200, request.method);
147
+ sendJson(response, await inspection.candidate({ id: readSearchString(url.searchParams, "id") }), 200, request.method);
161
148
  return;
162
149
  case "/api/evaluation":
163
- sendJson(response, await readEvaluationForApi(workspace, readSearchString(url.searchParams, "id")), 200, request.method);
150
+ sendJson(response, await inspection.evaluation({ id: readSearchString(url.searchParams, "id") }), 200, request.method);
164
151
  return;
165
152
  case "/api/candidate/files": {
166
153
  const candidateId = readSearchString(url.searchParams, "id");
167
- const candidate = await readCandidateForApi(workspace, candidateId);
168
- sendJson(response, summarizeCandidateFiles(await readCandidateFilesForApi(workspace, candidateId), candidate.fileChanges), 200, request.method);
154
+ sendJson(response, await inspection.candidateFiles({ id: candidateId }), 200, request.method);
169
155
  return;
170
156
  }
171
157
  case "/api/candidate/preview": {
172
158
  const candidateId = readSearchString(url.searchParams, "id");
173
- sendJson(response, createCandidateFilePreview({
174
- files: await readCandidateFilesForApi(workspace, candidateId),
159
+ sendJson(response, await inspection.candidatePreview({
160
+ id: candidateId,
175
161
  path: readSearchString(url.searchParams, "path"),
176
162
  view: readPreviewMode(url.searchParams),
177
163
  }), 200, request.method);
@@ -181,44 +167,38 @@ async function handleApiRequest(request, response, context, url) {
181
167
  const candidateId = readSearchString(url.searchParams, "id");
182
168
  const caseId = readSearchString(url.searchParams, "case");
183
169
  const runId = readSearchString(url.searchParams, "run");
184
- const jobs = await readLocalRunJobs(workspace, runId);
185
- sendJson(response, createCaseReview({
186
- candidate: await readCandidateForApi(workspace, candidateId),
170
+ sendJson(response, await inspection.caseReview({
171
+ candidateId,
187
172
  caseId,
188
- executions: buildCandidateCaseExecutionRefs({ jobs, candidateId, caseId }),
173
+ runId,
189
174
  }), 200, request.method);
190
175
  return;
191
176
  }
192
177
  case "/api/traces": {
193
178
  const traceRunId = readSearchString(url.searchParams, "run");
194
179
  const traceJobId = readSearchString(url.searchParams, "job");
195
- const traceJobs = [await readExecutionJobForRun(workspace, traceRunId, traceJobId)];
196
- sendJson(response, {
197
- projectId: "local",
180
+ sendJson(response, await inspection.executionTrace({
198
181
  runId: traceRunId,
199
- executions: buildWorkbenchExecutionEvidence({
200
- jobs: traceJobs,
201
- traceIdPrefix: "local-execution",
202
- traceForJob: readLocalAggregateTrace,
203
- traceSessionsForJob: readLocalTraceSessions,
204
- }),
205
- }, 200, request.method);
182
+ jobId: traceJobId,
183
+ }), 200, request.method);
206
184
  return;
207
185
  }
208
186
  case "/api/execution/files": {
209
187
  const execRunId = readSearchString(url.searchParams, "run");
210
188
  const execJobId = readSearchString(url.searchParams, "id");
211
- const execFiles = await loadExecutionFiles(workspace, execRunId, execJobId);
212
- sendJson(response, execFiles, 200, request.method);
189
+ sendJson(response, await inspection.executionFiles({
190
+ runId: execRunId,
191
+ jobId: execJobId,
192
+ }), 200, request.method);
213
193
  return;
214
194
  }
215
195
  case "/api/execution/preview": {
216
196
  const previewRunId = readSearchString(url.searchParams, "run");
217
197
  const previewJobId = readSearchString(url.searchParams, "id");
218
198
  const previewFilePath = readSearchString(url.searchParams, "path");
219
- const previewFiles = await readExecutionFilesForRun(workspace, previewRunId, previewJobId);
220
- sendJson(response, createCandidateFilePreview({
221
- files: previewFiles,
199
+ sendJson(response, await inspection.executionPreview({
200
+ runId: previewRunId,
201
+ jobId: previewJobId,
222
202
  path: previewFilePath,
223
203
  view: readPreviewMode(url.searchParams),
224
204
  }), 200, request.method);
@@ -228,273 +208,6 @@ async function handleApiRequest(request, response, context, url) {
228
208
  throw new LocalApiError(`Unknown Workbench local API route: ${url.pathname}`, 404);
229
209
  }
230
210
  }
231
- export async function localBenchmarkSnapshot(context) {
232
- const { workspace } = context;
233
- const snapshot = await loadLocalArchiveIndex(workspace);
234
- const candidates = snapshot.candidates.filter(isInspectableCandidateRecord);
235
- const summaries = candidates.map(candidateSummary);
236
- const activeId = snapshot.activeId && candidates.some((candidate) => candidate.id === snapshot.activeId)
237
- ? snapshot.activeId
238
- : null;
239
- const currentBenchmarkFingerprint = await readCurrentBenchmarkFingerprint(context);
240
- return {
241
- workspaceRoot: path.resolve(workspace),
242
- activeId,
243
- currentBenchmarkFingerprint,
244
- summaries,
245
- evaluations: snapshot.evaluations.map(evaluationSummary),
246
- runs: snapshot.runs.map(publicLocalRunSummary),
247
- };
248
- }
249
- function publicLocalRunSummary(run) {
250
- const { executionFingerprint: _executionFingerprint, ...summary } = run;
251
- return summary;
252
- }
253
- async function readCurrentBenchmarkFingerprint(context) {
254
- return await context.readProjectSource()
255
- .then(localBenchmarkFingerprint)
256
- .catch(() => null);
257
- }
258
- export async function localSpecDocument(context, benchmarkFingerprint) {
259
- const { workspace } = context;
260
- const projectSource = await context.readProjectSource().catch(() => null);
261
- const authoredSource = projectSource
262
- ? null
263
- : await readLocalAuthoredProjectSource(workspace).catch(() => null);
264
- const requestedFingerprint = normalizeOptionalFingerprint(benchmarkFingerprint);
265
- const currentFingerprint = projectSource
266
- ? localBenchmarkFingerprint(projectSource)
267
- : null;
268
- if (requestedFingerprint &&
269
- currentFingerprint &&
270
- requestedFingerprint !== currentFingerprint) {
271
- const snapshot = await loadLocalArchiveIndex(workspace);
272
- const document = localHistoricalBenchmarkDocument(snapshot, requestedFingerprint);
273
- if (document) {
274
- return document;
275
- }
276
- throw new LocalApiError(`Benchmark version not found: ${requestedFingerprint}`, 404);
277
- }
278
- const sourceYaml = projectSource?.specSource ?? authoredSource?.specSource ?? "";
279
- const cases = projectSource
280
- ? caseSummaryFilesFromEngineCases(projectSource.engineCases, projectSource.engineResolveFiles)
281
- : [];
282
- return loadAuthoredWorkbenchSourceDocument({
283
- sourceYaml,
284
- path: WORKBENCH_BENCHMARK_FILE,
285
- sourceFiles: projectSource?.sourceFiles ?? authoredSource?.sourceFiles,
286
- cases,
287
- });
288
- }
289
- function caseSummaryFilesFromEngineCases(engineCases, files) {
290
- const existingCaseIds = new Set(files.flatMap((file) => {
291
- const normalized = file.path.replace(/\\/gu, "/").replace(/^\/+/u, "");
292
- const slash = normalized.indexOf("/");
293
- return slash > 0 ? [normalized.slice(0, slash)] : [];
294
- }));
295
- return [
296
- ...files.map((file) => ({ ...file })),
297
- ...engineCases
298
- .filter((engineCase) => !existingCaseIds.has(engineCase.id))
299
- .map((engineCase) => ({
300
- path: `${engineCase.id}/.workbench-case.json`,
301
- encoding: "utf8",
302
- content: `${JSON.stringify({ id: engineCase.id })}\n`,
303
- executable: false,
304
- })),
305
- ];
306
- }
307
- export async function localSourceFiles(workspace) {
308
- return (await readLocalProjectSource(workspace)).sourceFiles;
309
- }
310
- export async function localBenchmarkMountedFiles(context, benchmarkFingerprint) {
311
- const requestedFingerprint = normalizeOptionalFingerprint(benchmarkFingerprint);
312
- const { workspace } = context;
313
- const projectSource = await context.readProjectSource();
314
- const currentFingerprint = localBenchmarkFingerprint(projectSource);
315
- if (requestedFingerprint &&
316
- currentFingerprint &&
317
- requestedFingerprint !== currentFingerprint) {
318
- const snapshot = await loadLocalArchiveIndex(workspace);
319
- return localHistoricalBenchmarkFiles(snapshot, requestedFingerprint);
320
- }
321
- return inspectableEngineCaseFiles(projectSource.engineCases);
322
- }
323
- function localHistoricalBenchmarkDocument(snapshot, benchmarkFingerprint) {
324
- const candidate = snapshot.candidates.find((entry) => entry.benchmarkFingerprint === benchmarkFingerprint && readBenchmarkSourceMetadata(entry));
325
- const source = candidate ? readBenchmarkSourceMetadata(candidate) : null;
326
- if (!source?.sourceYaml) {
327
- return null;
328
- }
329
- return loadAuthoredWorkbenchSourceDocument({
330
- sourceYaml: source.sourceYaml,
331
- path: WORKBENCH_BENCHMARK_FILE,
332
- sourceFiles: source.files,
333
- cases: localHistoricalBenchmarkFiles(snapshot, benchmarkFingerprint),
334
- });
335
- }
336
- function localHistoricalBenchmarkFiles(_snapshot, _benchmarkFingerprint) {
337
- return [];
338
- }
339
- function inspectableEngineCaseFiles(engineCases) {
340
- return engineCases.flatMap((bundle) => engineCaseFiles(bundle).map((file) => ({
341
- ...file,
342
- path: `${bundle.id}/${file.path}`,
343
- }))).sort((left, right) => left.path.localeCompare(right.path));
344
- }
345
- function engineCaseFiles(bundle) {
346
- const buckets = bundle.files;
347
- return buckets.source?.length
348
- ? buckets.source
349
- : [...(buckets.public ?? []), ...(buckets.private ?? [])];
350
- }
351
- function candidateSummary(candidate) {
352
- return candidateSummaryFromRecord(candidate);
353
- }
354
- function isInspectableCandidateRecord(candidate) {
355
- return Boolean(candidate.eval || asRecord(asRecord(candidate.meta)?.source));
356
- }
357
- function evaluationSummary(evaluation) {
358
- const { evaluation: _evaluation, ...summary } = evaluation;
359
- return summary;
360
- }
361
- async function readCandidateForApi(workspace, candidateId) {
362
- const candidate = await readArchiveRecord("Candidate", candidateId, () => readLocalCandidateRecord(workspace, candidateId));
363
- return candidateRecordWithoutDerivedFields(candidate);
364
- }
365
- async function readEvaluationForApi(workspace, evaluationId) {
366
- return await readArchiveRecord("Evaluation", evaluationId, () => readLocalEvaluationRecord(workspace, evaluationId));
367
- }
368
- async function readCandidateFilesForApi(workspace, candidateId) {
369
- return await readArchiveRecord("Candidate", candidateId, () => readLocalCandidateFilesForId(workspace, candidateId));
370
- }
371
- function readBenchmarkSourceMetadata(candidate) {
372
- const benchmark = asRecord(asRecord(candidate.meta)?.benchmark);
373
- const files = Array.isArray(benchmark?.files)
374
- ? benchmark.files
375
- .map(readSurfaceSnapshotFile)
376
- .filter((file) => file !== null)
377
- : [];
378
- const sourceYaml = files.find((file) => file.path === WORKBENCH_BENCHMARK_FILE)?.content ?? null;
379
- if (!sourceYaml) {
380
- return null;
381
- }
382
- return { sourceYaml, files };
383
- }
384
- function readSurfaceSnapshotFile(value) {
385
- const record = asRecord(value);
386
- if (!record) {
387
- return null;
388
- }
389
- const filePath = typeof record?.path === "string" ? record.path : "";
390
- const content = typeof record?.content === "string" ? record.content : null;
391
- if (!filePath || content === null) {
392
- return null;
393
- }
394
- return {
395
- path: filePath,
396
- kind: record.kind === "binary" ? "binary" : "text",
397
- encoding: record.encoding === "base64" ? "base64" : "utf8",
398
- content,
399
- executable: record.executable === true,
400
- };
401
- }
402
- function asRecord(value) {
403
- return value && typeof value === "object" && !Array.isArray(value)
404
- ? value
405
- : null;
406
- }
407
- function normalizeOptionalFingerprint(value) {
408
- const normalized = value?.trim();
409
- return normalized ? normalized : null;
410
- }
411
- async function readArchiveRecord(kind, id, read) {
412
- try {
413
- return await read();
414
- }
415
- catch (error) {
416
- if (error instanceof Error && error.message === `${kind} not found: ${id}`) {
417
- throw new LocalApiError(error.message, 404);
418
- }
419
- throw error;
420
- }
421
- }
422
- async function loadExecutionFiles(workspace, runId, jobId) {
423
- const files = await readExecutionFilesForRun(workspace, runId, jobId);
424
- return summarizeCandidateFiles(files);
425
- }
426
- async function readExecutionFilesForRun(workspace, runId, jobId) {
427
- await assertExecutionJobInRun(workspace, runId, jobId);
428
- return await readLocalExecutionFiles(workspace, jobId);
429
- }
430
- async function assertExecutionJobInRun(workspace, runId, jobId) {
431
- await readExecutionJobForRun(workspace, runId, jobId);
432
- }
433
- async function readExecutionJobForRun(workspace, runId, jobId) {
434
- const job = await readLocalJobInRun(workspace, runId, jobId);
435
- if (!job) {
436
- throw new LocalApiError(`Execution job not found: ${jobId}`, 404);
437
- }
438
- return job;
439
- }
440
- function readLocalAggregateTrace(job) {
441
- const trace = job.trace;
442
- if (!trace || typeof trace !== "object" || Array.isArray(trace)) {
443
- return { trace_id: job.id, spans: [], events: [], summaries: [] };
444
- }
445
- const record = trace;
446
- return {
447
- trace_id: typeof record.trace_id === "string" ? record.trace_id : job.id,
448
- spans: Array.isArray(record.spans) ? record.spans : [],
449
- events: Array.isArray(record.events) ? record.events : [],
450
- summaries: Array.isArray(record.summaries) ? record.summaries : [],
451
- };
452
- }
453
- function readLocalTraceSessions(job, role) {
454
- if (!Array.isArray(job.traceSessions)) {
455
- return [];
456
- }
457
- return job.traceSessions.map((session) => ({
458
- id: typeof session.id === "string" && session.id.length > 0
459
- ? session.id
460
- : `${job.id}:trace`,
461
- jobId: typeof session.jobId === "string" && session.jobId.length > 0
462
- ? session.jobId
463
- : job.id,
464
- role: session.role === "improver" || session.role === "runner" || session.role === "engine"
465
- ? session.role
466
- : role,
467
- kind: typeof session.kind === "string" && session.kind.length > 0 ? session.kind : "trace",
468
- label: traceSessionDisplayLabel(session, role),
469
- sourcePath: typeof session.sourcePath === "string" ? session.sourcePath : null,
470
- trace: sanitizeLocalTrace(session.trace, session.id),
471
- ...(session.metadata && typeof session.metadata === "object" && !Array.isArray(session.metadata)
472
- ? { metadata: session.metadata }
473
- : {}),
474
- }));
475
- }
476
- function traceSessionDisplayLabel(session, fallbackRole) {
477
- const role = session.role === "improver" || session.role === "runner" || session.role === "engine"
478
- ? session.role
479
- : fallbackRole;
480
- return typeof session.label === "string" && session.label.length > 0
481
- ? session.label
482
- : typeof session.sourcePath === "string" && session.sourcePath.length > 0
483
- ? traceSessionLabel(session.sourcePath, role)
484
- : "Trace";
485
- }
486
- function sanitizeLocalTrace(trace, fallbackId) {
487
- if (!trace || typeof trace !== "object" || Array.isArray(trace)) {
488
- return { trace_id: fallbackId, spans: [], events: [], summaries: [] };
489
- }
490
- const record = trace;
491
- return {
492
- trace_id: typeof record.trace_id === "string" ? record.trace_id : fallbackId,
493
- spans: Array.isArray(record.spans) ? record.spans : [],
494
- events: Array.isArray(record.events) ? record.events : [],
495
- summaries: Array.isArray(record.summaries) ? record.summaries : [],
496
- };
497
- }
498
211
  function readSearchString(params, key) {
499
212
  const value = params.get(key);
500
213
  if (!value) {
@@ -576,7 +289,11 @@ function sendJson(response, value, status = 200, method = "GET") {
576
289
  }
577
290
  function sendError(response, error, method = "GET") {
578
291
  const message = error instanceof Error ? error.message : String(error);
579
- const status = error instanceof LocalApiError ? error.status : 500;
292
+ const status = error instanceof LocalApiError
293
+ ? error.status
294
+ : typeof error?.statusCode === "number"
295
+ ? error.statusCode
296
+ : 500;
580
297
  sendJson(response, { message }, status, method);
581
298
  }
582
299
  function displayHost(host) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6IA,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AA4BD,UAAU,iBAAiB;CAAG;AA8K9B,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAIH,EACD,cAAc,GAAE,iBAAsB,GACrC,OAAO,CAAC,MAAM,CAAC,CAmHjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8IA,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AA4BD,UAAU,iBAAiB;CAAG;AAkL9B,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAIH,EACD,cAAc,GAAE,iBAAsB,GACrC,OAAO,CAAC,MAAM,CAAC,CA4HjB"}