@workbench-ai/workbench 0.0.65 → 0.0.67
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/command-model.js +2 -2
- package/dist/dev-open/client.css +0 -3
- package/dist/dev-open/client.js +204 -204
- package/dist/dev-open-server.js +21 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +275 -119
- package/dist/local-archive.d.ts.map +1 -1
- package/dist/local-archive.js +15 -96
- package/dist/local-inspection.d.ts.map +1 -1
- package/dist/local-inspection.js +72 -35
- package/package.json +9 -4
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ import { createRequire } from "node:module";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { Writable } from "node:stream";
|
|
8
|
-
import {
|
|
8
|
+
import { gzipSync } from "node:zlib";
|
|
9
|
+
import { createBaselineCandidateJob as createRuntimeBaselineCandidateJob, evaluationScorecardId, evaluationMeanMetrics, executeWorkbenchExecutionJob, engineResolveBindingForSpec, filterOptimizerTraceJobsForCaseIds, filterCandidateSourceFiles, formatWorkbenchCaseSelector, formatWorkbenchSelectionPolicy, workbenchCaseSelectorUsesAllCases, workbenchExecutionPurpose, workbenchRunExecutionFingerprint, createWorkbenchAdapterAuthBundle, createOptimizerTraceInputFiles, DOCKER_SANDBOX_BACKEND, localWorkbenchAdapterAuthStore, materializeWorkbenchRunResult, normalizeRelativePath, normalizeSurfaceFiles, isSurfaceSnapshotFile, jsonRecord, planWorkbenchExecutionJobsForPurpose, runWorkbenchExecutionDag, resolveEngineCaseExecutionConfig, resolveWorkbenchResolvedSourceYaml, runtimeResources, validateWorkbenchRunEnvelope, parseWorkbenchAdapterAuthTarget, workbenchEngineCaseIdsForImproveEvaluation, workbenchEngineCaseIdsForSelector, workbenchImproveOptimizeSelector, workbenchImproveSelectionPolicy, workbenchProjectSourceFingerprint, workbenchRuntimeCandidateIdentityForExchange, workbenchRuntimeBundleFingerprint, workbenchRuntimeProjectedActiveId, } from "@workbench-ai/workbench-core";
|
|
9
10
|
import { assertWorkbenchAdapterOperationResultOk, collectWorkbenchAdapterAuthRequirements, normalizeWorkbenchAdapterOperationRequest, readWorkbenchAdapterOperationResult, workbenchAdapterOperationCommand, workbenchAdapterOperationResultPath, withDefaultWorkbenchAdapterAuthProfiles as applyDefaultWorkbenchAdapterAuthProfiles, } from "@workbench-ai/workbench-protocol";
|
|
10
11
|
import { builtinLocalTraceAdapter, builtinLocalTraceAdapters, sortLocalTraceRefs, } from "@workbench-ai/workbench-built-in-adapters/local-traces";
|
|
11
12
|
import { commandUsage, REMOTE_WATCH_LIFECYCLE_NOTE, LOCAL_DEV_OPEN_LIFECYCLE_NOTE, rootUsage, } from "./command-model.js";
|
|
@@ -34,7 +35,32 @@ class WorkbenchApiRequestError extends Error {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
const API_REQUEST_MAX_ATTEMPTS = 3;
|
|
38
|
+
const API_REQUEST_GZIP_THRESHOLD_BYTES = 1024 * 1024;
|
|
37
39
|
const DEFAULT_BASE_URL = "https://v2.workbench.ai";
|
|
40
|
+
const AUTH_COMMAND_HANDLERS = {
|
|
41
|
+
connect: authConnect,
|
|
42
|
+
disconnect: authDisconnect,
|
|
43
|
+
};
|
|
44
|
+
const ADAPTERS_COMMAND_HANDLERS = {
|
|
45
|
+
create: adaptersCreate,
|
|
46
|
+
inspect: adaptersInspect,
|
|
47
|
+
list: adaptersList,
|
|
48
|
+
test: adaptersTest,
|
|
49
|
+
};
|
|
50
|
+
const TRACES_COMMAND_HANDLERS = {
|
|
51
|
+
collect: localTraceCollect,
|
|
52
|
+
list: localTraceList,
|
|
53
|
+
show: localTraceShow,
|
|
54
|
+
};
|
|
55
|
+
const TWO_SEGMENT_HELP_COMMANDS = {
|
|
56
|
+
adapters: ["create", "list", "inspect", "test"],
|
|
57
|
+
auth: [],
|
|
58
|
+
candidates: ["list", "show", "files", "preview"],
|
|
59
|
+
evaluations: ["list", "show"],
|
|
60
|
+
executions: ["trace"],
|
|
61
|
+
runs: ["list", "show"],
|
|
62
|
+
traces: ["collect", "list", "show"],
|
|
63
|
+
};
|
|
38
64
|
export async function runCli(argv, io = {
|
|
39
65
|
stdin: process.stdin,
|
|
40
66
|
stdout: process.stdout,
|
|
@@ -162,34 +188,12 @@ export async function runCli(argv, io = {
|
|
|
162
188
|
}
|
|
163
189
|
function commandPathForHelp(argv) {
|
|
164
190
|
const positionals = argv.filter((arg) => arg !== "--help" && arg !== "-h" && !arg.startsWith("--"));
|
|
165
|
-
|
|
166
|
-
|
|
191
|
+
const command = positionals[0] ?? "";
|
|
192
|
+
const subcommands = TWO_SEGMENT_HELP_COMMANDS[command];
|
|
193
|
+
if (subcommands && (subcommands.length === 0 || subcommands.includes(positionals[1] ?? ""))) {
|
|
167
194
|
return positionals.slice(0, 2).join(" ");
|
|
168
195
|
}
|
|
169
|
-
|
|
170
|
-
["collect", "list", "show"].includes(positionals[1] ?? "")) {
|
|
171
|
-
return positionals.slice(0, 2).join(" ");
|
|
172
|
-
}
|
|
173
|
-
if (positionals[0] === "auth") {
|
|
174
|
-
return positionals.slice(0, 2).join(" ");
|
|
175
|
-
}
|
|
176
|
-
if (positionals[0] === "runs" &&
|
|
177
|
-
["list", "show"].includes(positionals[1] ?? "")) {
|
|
178
|
-
return positionals.slice(0, 2).join(" ");
|
|
179
|
-
}
|
|
180
|
-
if (positionals[0] === "evaluations" &&
|
|
181
|
-
["list", "show"].includes(positionals[1] ?? "")) {
|
|
182
|
-
return positionals.slice(0, 2).join(" ");
|
|
183
|
-
}
|
|
184
|
-
if (positionals[0] === "executions" &&
|
|
185
|
-
["trace"].includes(positionals[1] ?? "")) {
|
|
186
|
-
return positionals.slice(0, 2).join(" ");
|
|
187
|
-
}
|
|
188
|
-
if (positionals[0] === "candidates" &&
|
|
189
|
-
["list", "show", "files", "preview"].includes(positionals[1] ?? "")) {
|
|
190
|
-
return positionals.slice(0, 2).join(" ");
|
|
191
|
-
}
|
|
192
|
-
return positionals[0] ?? "";
|
|
196
|
+
return command;
|
|
193
197
|
}
|
|
194
198
|
function extractRemoteFlag(argv) {
|
|
195
199
|
let enabled = false;
|
|
@@ -762,6 +766,11 @@ async function localRun(argv, io, runtimeOptions) {
|
|
|
762
766
|
detail: { budget, samples, strategy: "greedy", optimizeOn: optimizeOnLabel, selectBy: selectByLabel },
|
|
763
767
|
}),
|
|
764
768
|
];
|
|
769
|
+
const runInput = localRunInput({
|
|
770
|
+
benchmarkFingerprint,
|
|
771
|
+
sourceYaml: projectSource.specSource,
|
|
772
|
+
engineResolveFiles,
|
|
773
|
+
});
|
|
765
774
|
const runningRun = {
|
|
766
775
|
id: runId,
|
|
767
776
|
workflow: "improve",
|
|
@@ -784,6 +793,7 @@ async function localRun(argv, io, runtimeOptions) {
|
|
|
784
793
|
executionFingerprint,
|
|
785
794
|
activeCandidateId: snapshot.activeId,
|
|
786
795
|
outputCandidateId: null,
|
|
796
|
+
input: runInput,
|
|
787
797
|
};
|
|
788
798
|
snapshot = upsertLocalRun(snapshot, runningRun, events);
|
|
789
799
|
await saveLocalArchive(workspace, snapshot);
|
|
@@ -952,6 +962,7 @@ async function localRun(argv, io, runtimeOptions) {
|
|
|
952
962
|
outcome: failedJobCount > 0 ? "error" : "ok",
|
|
953
963
|
activeCandidateId: snapshot.activeId,
|
|
954
964
|
outputCandidateId: outputCandidateId ?? snapshot.activeId,
|
|
965
|
+
input: runInput,
|
|
955
966
|
};
|
|
956
967
|
events.push(createLocalEvent("run_finished", finishedAt, {
|
|
957
968
|
runId,
|
|
@@ -1243,6 +1254,11 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
|
|
|
1243
1254
|
candidateId: evaluatedCandidateId,
|
|
1244
1255
|
detail: { samples, strategy: "direct" },
|
|
1245
1256
|
});
|
|
1257
|
+
const runInput = localRunInput({
|
|
1258
|
+
benchmarkFingerprint,
|
|
1259
|
+
sourceYaml: projectSource.specSource,
|
|
1260
|
+
engineResolveFiles,
|
|
1261
|
+
});
|
|
1246
1262
|
const runningRun = {
|
|
1247
1263
|
id: runId,
|
|
1248
1264
|
workflow: "eval",
|
|
@@ -1263,6 +1279,7 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
|
|
|
1263
1279
|
executionFingerprint,
|
|
1264
1280
|
activeCandidateId: activeCandidateIdBeforeEval,
|
|
1265
1281
|
outputCandidateId: evaluatedCandidateId,
|
|
1282
|
+
input: runInput,
|
|
1266
1283
|
};
|
|
1267
1284
|
snapshot = upsertLocalRun(snapshot, runningRun, [runStartedEvent]);
|
|
1268
1285
|
await saveLocalArchive(workspace, snapshot);
|
|
@@ -1348,7 +1365,7 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
|
|
|
1348
1365
|
durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
|
|
1349
1366
|
},
|
|
1350
1367
|
});
|
|
1351
|
-
|
|
1368
|
+
const finishedRun = {
|
|
1352
1369
|
id: runId,
|
|
1353
1370
|
workflow: "eval",
|
|
1354
1371
|
benchmarkFingerprint,
|
|
@@ -1372,7 +1389,9 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
|
|
|
1372
1389
|
outcome: currentRunFailedJobCount > 0 ? "error" : "ok",
|
|
1373
1390
|
activeCandidateId,
|
|
1374
1391
|
outputCandidateId: evaluatedCandidateId,
|
|
1375
|
-
|
|
1392
|
+
input: runInput,
|
|
1393
|
+
};
|
|
1394
|
+
snapshot = upsertLocalRun(snapshot, finishedRun, [runFinishedEvent]);
|
|
1376
1395
|
await saveLocalJobs(workspace, currentRunJobs);
|
|
1377
1396
|
await saveLocalArchive(workspace, snapshot);
|
|
1378
1397
|
const evaluation = materialized.evaluations[0] ?? null;
|
|
@@ -1576,8 +1595,7 @@ function localDevOpenUrl(baseUrl, snapshot, runId) {
|
|
|
1576
1595
|
if (!evaluation) {
|
|
1577
1596
|
return new URL("candidates", baseUrl).toString();
|
|
1578
1597
|
}
|
|
1579
|
-
|
|
1580
|
-
return new URL(`candidates/${encodeURIComponent(evaluation.candidateId)}?${params.toString()}`, baseUrl).toString();
|
|
1598
|
+
return new URL(`evaluations/${encodeURIComponent(evaluation.id)}`, baseUrl).toString();
|
|
1581
1599
|
}
|
|
1582
1600
|
async function readLocalBenchmarkFingerprint(workspace) {
|
|
1583
1601
|
return localBenchmarkFingerprint(await readLocalProjectSource(workspace));
|
|
@@ -1606,6 +1624,13 @@ function authoredBenchmarkSourceFiles(projectSource) {
|
|
|
1606
1624
|
executable: false,
|
|
1607
1625
|
}];
|
|
1608
1626
|
}
|
|
1627
|
+
function localRunInput(input) {
|
|
1628
|
+
return {
|
|
1629
|
+
benchmarkFingerprint: input.benchmarkFingerprint,
|
|
1630
|
+
sourceYaml: input.sourceYaml,
|
|
1631
|
+
engineResolveFiles: normalizeSurfaceFiles(input.engineResolveFiles),
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1609
1634
|
function shellQuote(value) {
|
|
1610
1635
|
return `'${value.replace(/'/gu, "'\\''")}'`;
|
|
1611
1636
|
}
|
|
@@ -1871,11 +1896,16 @@ async function localCandidatePreview(argv, io) {
|
|
|
1871
1896
|
const inspection = localInspectionFromParsed(parsed);
|
|
1872
1897
|
const snapshot = await inspection.snapshot();
|
|
1873
1898
|
const candidateId = readCandidateIdFlag(parsed, snapshot);
|
|
1874
|
-
const
|
|
1899
|
+
const requestedPath = requireFlag(parsed, "path");
|
|
1900
|
+
const surface = await inspection.candidateFileSurface({
|
|
1875
1901
|
id: candidateId,
|
|
1876
|
-
path:
|
|
1902
|
+
path: requestedPath,
|
|
1877
1903
|
view: readPreviewMode(parsed),
|
|
1878
1904
|
});
|
|
1905
|
+
const preview = surface.preview;
|
|
1906
|
+
if (!preview || preview.path !== normalizeRelativePath(requestedPath)) {
|
|
1907
|
+
throw new UsageError(`File not found: ${requestedPath}`);
|
|
1908
|
+
}
|
|
1879
1909
|
const content = preview.source?.content ?? preview.rendered_html ?? preview.diff ?? "";
|
|
1880
1910
|
const outputPath = asOptionalString(parsed.flags.output);
|
|
1881
1911
|
if (outputPath && outputPath !== "-") {
|
|
@@ -2010,46 +2040,21 @@ async function localDiagnose(argv, io) {
|
|
|
2010
2040
|
return 0;
|
|
2011
2041
|
}
|
|
2012
2042
|
async function runAuthCommand(argv, io) {
|
|
2013
|
-
|
|
2014
|
-
const rest = argv.slice(1);
|
|
2015
|
-
switch (command) {
|
|
2016
|
-
case "connect":
|
|
2017
|
-
return await authConnect(rest, io);
|
|
2018
|
-
case "disconnect":
|
|
2019
|
-
return await authDisconnect(rest, io);
|
|
2020
|
-
default:
|
|
2021
|
-
throw new UsageError(`Unknown command: auth ${argv.join(" ")}`);
|
|
2022
|
-
}
|
|
2043
|
+
return await runSubCommand("auth", AUTH_COMMAND_HANDLERS, argv, io);
|
|
2023
2044
|
}
|
|
2024
2045
|
async function runAdaptersCommand(argv, io) {
|
|
2025
|
-
|
|
2026
|
-
const rest = argv.slice(1);
|
|
2027
|
-
switch (command) {
|
|
2028
|
-
case "create":
|
|
2029
|
-
return await adaptersCreate(rest, io);
|
|
2030
|
-
case "list":
|
|
2031
|
-
return await adaptersList(rest, io);
|
|
2032
|
-
case "inspect":
|
|
2033
|
-
return await adaptersInspect(rest, io);
|
|
2034
|
-
case "test":
|
|
2035
|
-
return await adaptersTest(rest, io);
|
|
2036
|
-
default:
|
|
2037
|
-
throw new UsageError(`Unknown command: adapters ${argv.join(" ")}`);
|
|
2038
|
-
}
|
|
2046
|
+
return await runSubCommand("adapters", ADAPTERS_COMMAND_HANDLERS, argv, io);
|
|
2039
2047
|
}
|
|
2040
2048
|
async function runTracesCommand(argv, io) {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
case "show":
|
|
2049
|
-
return await localTraceShow(rest, io);
|
|
2050
|
-
default:
|
|
2051
|
-
throw new UsageError(`Unknown command: traces ${argv.join(" ")}`);
|
|
2049
|
+
return await runSubCommand("traces", TRACES_COMMAND_HANDLERS, argv, io);
|
|
2050
|
+
}
|
|
2051
|
+
async function runSubCommand(group, handlers, argv, io) {
|
|
2052
|
+
const command = argv[0] ?? "";
|
|
2053
|
+
const handler = handlers[command];
|
|
2054
|
+
if (!handler) {
|
|
2055
|
+
throw new UsageError(`Unknown command: ${group} ${argv.join(" ")}`);
|
|
2052
2056
|
}
|
|
2057
|
+
return await handler(argv.slice(1), io);
|
|
2053
2058
|
}
|
|
2054
2059
|
const DEFAULT_LOCAL_TRACE_LIMIT = 3;
|
|
2055
2060
|
const LOCAL_TRACE_WINDOW_FLAGS = new Set(["providers", "since", "workspace", "limit", "json"]);
|
|
@@ -3057,7 +3062,7 @@ function adapterAuthRecord(value) {
|
|
|
3057
3062
|
}
|
|
3058
3063
|
async function pushBenchmark(argv, io) {
|
|
3059
3064
|
const parsed = parseArgs(argv);
|
|
3060
|
-
rejectUnknownFlags(parsed, new Set(["dir", "visibility", "dry-run", "json"]));
|
|
3065
|
+
rejectUnknownFlags(parsed, new Set(["dir", "visibility", "dry-run", "force", "json"]));
|
|
3061
3066
|
const dir = resolveSourceDir(parsed);
|
|
3062
3067
|
const source = await readLocalProjectSource(dir);
|
|
3063
3068
|
const origin = await readWorkbenchOrigin(dir);
|
|
@@ -3065,17 +3070,18 @@ async function pushBenchmark(argv, io) {
|
|
|
3065
3070
|
const visibility = readOptionalBenchmarkVisibility(parsed.flags.visibility);
|
|
3066
3071
|
const createVisibility = visibility ?? "public";
|
|
3067
3072
|
const dryRun = parsed.flags["dry-run"] === true;
|
|
3073
|
+
const force = parsed.flags.force === true;
|
|
3068
3074
|
const runtime = await exportLocalRuntimeBundle(dir, {
|
|
3069
3075
|
currentBenchmarkFingerprint: localBenchmarkFingerprint(source),
|
|
3070
3076
|
});
|
|
3071
3077
|
const localRuntimeFingerprint = workbenchRuntimeBundleFingerprint(runtime);
|
|
3072
|
-
const state = localProjectState({
|
|
3073
|
-
source,
|
|
3074
|
-
runtime,
|
|
3075
|
-
origin,
|
|
3076
|
-
visibility: createVisibility,
|
|
3077
|
-
});
|
|
3078
3078
|
if (!origin) {
|
|
3079
|
+
const state = localProjectState({
|
|
3080
|
+
source,
|
|
3081
|
+
runtime,
|
|
3082
|
+
origin,
|
|
3083
|
+
visibility: createVisibility,
|
|
3084
|
+
});
|
|
3079
3085
|
if (dryRun) {
|
|
3080
3086
|
writeOutput({
|
|
3081
3087
|
ok: true,
|
|
@@ -3123,30 +3129,135 @@ async function pushBenchmark(argv, io) {
|
|
|
3123
3129
|
if (!projectId) {
|
|
3124
3130
|
throw new UsageError("Missing remote benchmark. Run workbench push from a source directory.");
|
|
3125
3131
|
}
|
|
3132
|
+
const remoteProject = force || dryRun
|
|
3133
|
+
? await readOriginProjectIfPresent({ baseUrl, origin })
|
|
3134
|
+
: null;
|
|
3126
3135
|
if (dryRun) {
|
|
3127
|
-
const
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3136
|
+
const state = localProjectState({
|
|
3137
|
+
source,
|
|
3138
|
+
runtime,
|
|
3139
|
+
origin: remoteProject
|
|
3140
|
+
? { ...origin, projectId: remoteProject.id }
|
|
3141
|
+
: origin,
|
|
3142
|
+
visibility: visibility ?? remoteProject?.visibility ?? createVisibility,
|
|
3131
3143
|
});
|
|
3144
|
+
if (!remoteProject && !force) {
|
|
3145
|
+
throw new WorkbenchApiRequestError(404, `Workbench benchmark not found: ${origin.remote}`, "");
|
|
3146
|
+
}
|
|
3147
|
+
if (!remoteProject && force) {
|
|
3148
|
+
await assertForceCreateAllowed({ baseUrl, origin, sourceName: source.spec.name });
|
|
3149
|
+
}
|
|
3150
|
+
const action = force && !remoteProject ? "create" : force ? "replace" : "update";
|
|
3132
3151
|
writeOutput({
|
|
3133
3152
|
ok: true,
|
|
3134
3153
|
dryRun: true,
|
|
3135
|
-
action
|
|
3154
|
+
action,
|
|
3136
3155
|
dir,
|
|
3137
3156
|
baseUrl,
|
|
3138
|
-
benchmarkId: projectId,
|
|
3157
|
+
benchmarkId: remoteProject?.id ?? projectId,
|
|
3139
3158
|
remote: origin.remote,
|
|
3140
|
-
benchmark: remoteProjectSummaryForOutput(remoteProject),
|
|
3159
|
+
...(remoteProject ? { benchmark: remoteProjectSummaryForOutput(remoteProject) } : {}),
|
|
3141
3160
|
benchmarkName: source.spec.name,
|
|
3142
|
-
visibility: visibility ?? "unchanged",
|
|
3161
|
+
visibility: visibility ?? (remoteProject ? remoteProject.visibility ?? "unchanged" : createVisibility),
|
|
3143
3162
|
sourceFileCount: sourceFileCount(source),
|
|
3144
3163
|
runtime: runtimeBundleStats(runtime),
|
|
3145
3164
|
sourceFingerprint: state.source.fingerprint,
|
|
3146
3165
|
runtimeFingerprint: localRuntimeFingerprint,
|
|
3147
|
-
}, parsed, io, () =>
|
|
3166
|
+
}, parsed, io, () => force && !remoteProject
|
|
3167
|
+
? `Would create ${origin.remote} from local project state.`
|
|
3168
|
+
: force
|
|
3169
|
+
? `Would replace ${origin.remote} with local project state.`
|
|
3170
|
+
: `Would push ${sourceFileCount(source)} source file(s) and runtime history to ${origin.remote}.`);
|
|
3171
|
+
return 0;
|
|
3172
|
+
}
|
|
3173
|
+
if (force) {
|
|
3174
|
+
const targetOrigin = remoteProject
|
|
3175
|
+
? { ...origin, projectId: remoteProject.id }
|
|
3176
|
+
: null;
|
|
3177
|
+
const state = localProjectState({
|
|
3178
|
+
source,
|
|
3179
|
+
runtime,
|
|
3180
|
+
origin: targetOrigin,
|
|
3181
|
+
visibility: visibility ?? remoteProject?.visibility ?? createVisibility,
|
|
3182
|
+
});
|
|
3183
|
+
if (!remoteProject) {
|
|
3184
|
+
await assertForceCreateAllowed({ baseUrl, origin, sourceName: source.spec.name });
|
|
3185
|
+
const { project, origin: nextOrigin, result } = await createRemoteBenchmarkFromState({
|
|
3186
|
+
baseUrl,
|
|
3187
|
+
dir,
|
|
3188
|
+
state,
|
|
3189
|
+
});
|
|
3190
|
+
writeOutput({
|
|
3191
|
+
ok: true,
|
|
3192
|
+
action: "create",
|
|
3193
|
+
benchmark: project,
|
|
3194
|
+
visibility: project.visibility ?? createVisibility,
|
|
3195
|
+
origin: nextOrigin,
|
|
3196
|
+
source: result.source,
|
|
3197
|
+
runtime: result.runtime.stats,
|
|
3198
|
+
urls: buildWorkbenchResourceUrls({
|
|
3199
|
+
baseUrl,
|
|
3200
|
+
projectId: project.id,
|
|
3201
|
+
...originRemoteUrlParts(nextOrigin),
|
|
3202
|
+
}),
|
|
3203
|
+
}, parsed, io, (record) => {
|
|
3204
|
+
const value = record;
|
|
3205
|
+
return [
|
|
3206
|
+
`Pushed ${value.origin.remote} (${value.origin.projectId}).`,
|
|
3207
|
+
`Open benchmark: ${value.urls.benchmark}`,
|
|
3208
|
+
].join("\n");
|
|
3209
|
+
});
|
|
3210
|
+
return 0;
|
|
3211
|
+
}
|
|
3212
|
+
const response = await apiRequest(projectApiPath(remoteProject.id, "/state?force=true"), {
|
|
3213
|
+
method: "PUT",
|
|
3214
|
+
body: state,
|
|
3215
|
+
}, baseUrl);
|
|
3216
|
+
assertAcceptedForceReplacement({
|
|
3217
|
+
expected: state,
|
|
3218
|
+
actual: response.state,
|
|
3219
|
+
});
|
|
3220
|
+
const responseProject = remoteProjectSummaryFromState(response.state);
|
|
3221
|
+
const publishedProject = await applyRequestedProjectVisibility({
|
|
3222
|
+
baseUrl,
|
|
3223
|
+
projectId: responseProject.id,
|
|
3224
|
+
responseProject,
|
|
3225
|
+
visibility,
|
|
3226
|
+
});
|
|
3227
|
+
const applied = await acceptPushedProjectStateToLocal({
|
|
3228
|
+
dir,
|
|
3229
|
+
baseUrl,
|
|
3230
|
+
state: response.state,
|
|
3231
|
+
});
|
|
3232
|
+
writeOutput({
|
|
3233
|
+
ok: true,
|
|
3234
|
+
action: "replace",
|
|
3235
|
+
changed: true,
|
|
3236
|
+
benchmark: publishedProject,
|
|
3237
|
+
visibility: visibility ?? publishedProject.visibility ?? "unchanged",
|
|
3238
|
+
origin: applied.origin,
|
|
3239
|
+
source: response.source,
|
|
3240
|
+
runtime: response.runtime.stats,
|
|
3241
|
+
urls: buildWorkbenchResourceUrls({
|
|
3242
|
+
baseUrl,
|
|
3243
|
+
projectId: publishedProject.id ?? responseProject.id,
|
|
3244
|
+
...originRemoteUrlParts(applied.origin),
|
|
3245
|
+
}),
|
|
3246
|
+
}, parsed, io, (record) => {
|
|
3247
|
+
const value = record;
|
|
3248
|
+
return [
|
|
3249
|
+
`Replaced ${value.origin.remote} (${value.origin.projectId}).`,
|
|
3250
|
+
`Open benchmark: ${value.urls.benchmark}`,
|
|
3251
|
+
].join("\n");
|
|
3252
|
+
});
|
|
3148
3253
|
return 0;
|
|
3149
3254
|
}
|
|
3255
|
+
const state = localProjectState({
|
|
3256
|
+
source,
|
|
3257
|
+
runtime,
|
|
3258
|
+
origin,
|
|
3259
|
+
visibility: createVisibility,
|
|
3260
|
+
});
|
|
3150
3261
|
const response = await apiRequest(projectApiPath(projectId, "/state"), {
|
|
3151
3262
|
method: "PUT",
|
|
3152
3263
|
body: state,
|
|
@@ -3186,18 +3297,62 @@ async function pushBenchmark(argv, io) {
|
|
|
3186
3297
|
});
|
|
3187
3298
|
return 0;
|
|
3188
3299
|
}
|
|
3189
|
-
async function
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
const actualProject = response.benchmark.name;
|
|
3194
|
-
if (actualOwner !== expected.owner || actualProject !== expected.project) {
|
|
3195
|
-
const actualRemote = actualOwner && actualProject
|
|
3196
|
-
? `${actualOwner}/${actualProject}`
|
|
3197
|
-
: "unknown";
|
|
3198
|
-
throw new UsageError(`Workbench origin points to ${args.origin.remote}, but ${args.projectId} resolved to ${actualRemote}.`);
|
|
3300
|
+
async function readOriginProjectIfPresent(args) {
|
|
3301
|
+
try {
|
|
3302
|
+
const response = await apiRequest(publicProjectApiPath(parseOriginRemote(args.origin)), {}, args.baseUrl);
|
|
3303
|
+
return response.benchmark;
|
|
3199
3304
|
}
|
|
3200
|
-
|
|
3305
|
+
catch (error) {
|
|
3306
|
+
if (error instanceof WorkbenchApiRequestError && error.status === 404) {
|
|
3307
|
+
return null;
|
|
3308
|
+
}
|
|
3309
|
+
throw error;
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
async function assertForceCreateAllowed(args) {
|
|
3313
|
+
const target = parseOriginRemote(args.origin);
|
|
3314
|
+
if (args.sourceName !== target.project) {
|
|
3315
|
+
throw new UsageError(`Local benchmark name ${args.sourceName} does not match force push target ${args.origin.remote}.`);
|
|
3316
|
+
}
|
|
3317
|
+
const expectedOwner = target.owner;
|
|
3318
|
+
const profileStatus = await readWorkbenchProfileStatus(await loadConfig(), args.baseUrl);
|
|
3319
|
+
const actualOwner = profileStatus.profile?.username ?? null;
|
|
3320
|
+
if (!profileStatus.authenticated || actualOwner !== expectedOwner) {
|
|
3321
|
+
throw new WorkbenchApiRequestError(404, `Workbench benchmark not found or not writable: ${args.origin.remote}`, "");
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
function assertAcceptedForceReplacement(args) {
|
|
3325
|
+
const expectedSourceFingerprint = args.expected.source.fingerprint ??
|
|
3326
|
+
workbenchProjectSourceFingerprint(args.expected.source);
|
|
3327
|
+
const actualSourceFingerprint = args.actual.source.fingerprint ??
|
|
3328
|
+
workbenchProjectSourceFingerprint(args.actual.source);
|
|
3329
|
+
if (actualSourceFingerprint !== expectedSourceFingerprint) {
|
|
3330
|
+
throw new Error(`Workbench force push did not replace remote source. Expected ${expectedSourceFingerprint}, received ${actualSourceFingerprint}.`);
|
|
3331
|
+
}
|
|
3332
|
+
const expectedRuntime = runtimeReplacementSignature(args.expected.runtime);
|
|
3333
|
+
const actualRuntime = runtimeReplacementSignature(args.actual.runtime);
|
|
3334
|
+
if (JSON.stringify(actualRuntime) !== JSON.stringify(expectedRuntime)) {
|
|
3335
|
+
throw new Error(`Workbench force push did not replace remote runtime. Remote returned different record identity.`);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
function runtimeReplacementSignature(runtime) {
|
|
3339
|
+
return {
|
|
3340
|
+
activeId: runtime.activeId ?? null,
|
|
3341
|
+
candidates: runtime.candidates
|
|
3342
|
+
.map(workbenchRuntimeCandidateIdentityForExchange)
|
|
3343
|
+
.sort((left, right) => left.id.localeCompare(right.id)),
|
|
3344
|
+
candidateFiles: runtimeFileGroupSignature(runtime.candidateFiles, (group) => group.candidateId),
|
|
3345
|
+
evaluations: runtime.evaluations.map((evaluation) => evaluation.id).sort(),
|
|
3346
|
+
runs: runtime.runs.map((run) => run.id).sort(),
|
|
3347
|
+
jobs: runtime.jobs.map((job) => job.id).sort(),
|
|
3348
|
+
executionFiles: runtimeFileGroupSignature(runtime.executionFiles, (group) => group.jobId),
|
|
3349
|
+
events: runtime.events.map((event) => event.id).sort(),
|
|
3350
|
+
};
|
|
3351
|
+
}
|
|
3352
|
+
function runtimeFileGroupSignature(groups, idForGroup) {
|
|
3353
|
+
return groups
|
|
3354
|
+
.map((group) => `${idForGroup(group)}:${group.files.map((file) => file.path).sort().join("\n")}`)
|
|
3355
|
+
.sort();
|
|
3201
3356
|
}
|
|
3202
3357
|
function remoteProjectSummaryForOutput(project) {
|
|
3203
3358
|
return {
|
|
@@ -3974,7 +4129,7 @@ function buildWorkbenchResourceUrls(target, refs = {}) {
|
|
|
3974
4129
|
? evaluationScorecardId(refs.runId, refs.candidateId)
|
|
3975
4130
|
: null;
|
|
3976
4131
|
urls.candidateEvaluation = evaluationId
|
|
3977
|
-
? `${benchmark}/
|
|
4132
|
+
? `${benchmark}/evaluations/${encodeURIComponent(evaluationId)}`
|
|
3978
4133
|
: `${benchmark}/candidates/${encodeURIComponent(refs.candidateId)}`;
|
|
3979
4134
|
}
|
|
3980
4135
|
return urls;
|
|
@@ -4087,10 +4242,10 @@ function localProjectState(args) {
|
|
|
4087
4242
|
};
|
|
4088
4243
|
}
|
|
4089
4244
|
function projectStateRuntimeStats(state) {
|
|
4090
|
-
const activeId =
|
|
4245
|
+
const activeId = workbenchRuntimeProjectedActiveId({
|
|
4091
4246
|
candidates: state.runtime.candidates,
|
|
4247
|
+
evaluations: state.runtime.evaluations,
|
|
4092
4248
|
runs: state.runtime.runs,
|
|
4093
|
-
preferredActiveId: state.runtime.activeId ?? null,
|
|
4094
4249
|
benchmarkFingerprint: projectStateBenchmarkFingerprint(state.source),
|
|
4095
4250
|
});
|
|
4096
4251
|
return runtimeBundleStats({
|
|
@@ -4420,11 +4575,11 @@ function selectWorkbenchBaseUrl(input = {}) {
|
|
|
4420
4575
|
input.configBaseUrl ??
|
|
4421
4576
|
DEFAULT_BASE_URL);
|
|
4422
4577
|
}
|
|
4423
|
-
async function readWorkbenchProfileStatus(config) {
|
|
4578
|
+
async function readWorkbenchProfileStatus(config, baseUrlOverride) {
|
|
4424
4579
|
if (!config.accessToken) {
|
|
4425
4580
|
return { authenticated: false, profile: null };
|
|
4426
4581
|
}
|
|
4427
|
-
const baseUrl = selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
|
|
4582
|
+
const baseUrl = baseUrlOverride ?? selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
|
|
4428
4583
|
try {
|
|
4429
4584
|
const response = await fetch(`${baseUrl}/api/workbench/profile`, {
|
|
4430
4585
|
headers: {
|
|
@@ -4455,6 +4610,7 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
|
|
|
4455
4610
|
: selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
|
|
4456
4611
|
const method = options.method ?? "GET";
|
|
4457
4612
|
const canRetry = method === "GET";
|
|
4613
|
+
const requestBody = encodeJsonRequestBody(options.body);
|
|
4458
4614
|
let lastError = null;
|
|
4459
4615
|
for (let attempt = 1; attempt <= API_REQUEST_MAX_ATTEMPTS; attempt += 1) {
|
|
4460
4616
|
let response;
|
|
@@ -4462,12 +4618,12 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
|
|
|
4462
4618
|
response = await fetch(`${baseUrl}${apiPath}`, {
|
|
4463
4619
|
method,
|
|
4464
4620
|
headers: {
|
|
4465
|
-
|
|
4621
|
+
...requestBody.headers,
|
|
4466
4622
|
...(config.accessToken
|
|
4467
4623
|
? { authorization: `Bearer ${config.accessToken}` }
|
|
4468
4624
|
: {}),
|
|
4469
4625
|
},
|
|
4470
|
-
body:
|
|
4626
|
+
body: requestBody.body,
|
|
4471
4627
|
});
|
|
4472
4628
|
}
|
|
4473
4629
|
catch (error) {
|
|
@@ -4493,6 +4649,22 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
|
|
|
4493
4649
|
}
|
|
4494
4650
|
throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "Workbench API request failed."));
|
|
4495
4651
|
}
|
|
4652
|
+
function encodeJsonRequestBody(body) {
|
|
4653
|
+
if (body == null) {
|
|
4654
|
+
return { headers: { "content-type": "application/json" } };
|
|
4655
|
+
}
|
|
4656
|
+
const text = JSON.stringify(body);
|
|
4657
|
+
if (Buffer.byteLength(text) < API_REQUEST_GZIP_THRESHOLD_BYTES) {
|
|
4658
|
+
return { body: text, headers: { "content-type": "application/json" } };
|
|
4659
|
+
}
|
|
4660
|
+
return {
|
|
4661
|
+
body: gzipSync(text),
|
|
4662
|
+
headers: {
|
|
4663
|
+
"content-encoding": "gzip",
|
|
4664
|
+
"content-type": "application/json",
|
|
4665
|
+
},
|
|
4666
|
+
};
|
|
4667
|
+
}
|
|
4496
4668
|
function apiRequestRetryDelayMs(attempt) {
|
|
4497
4669
|
return 250 * attempt;
|
|
4498
4670
|
}
|
|
@@ -5068,28 +5240,12 @@ async function resolveLocalProjectForExecution(workspace, source) {
|
|
|
5068
5240
|
};
|
|
5069
5241
|
}
|
|
5070
5242
|
function completedJobOutputFiles(job) {
|
|
5071
|
-
const output =
|
|
5243
|
+
const output = jsonRecord(job.output);
|
|
5072
5244
|
const files = Array.isArray(output.files)
|
|
5073
5245
|
? output.files.filter(isSurfaceSnapshotFile)
|
|
5074
5246
|
: [];
|
|
5075
5247
|
return normalizeSurfaceFiles(files);
|
|
5076
5248
|
}
|
|
5077
|
-
function asJsonRecord(value) {
|
|
5078
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
5079
|
-
? value
|
|
5080
|
-
: {};
|
|
5081
|
-
}
|
|
5082
|
-
function isSurfaceSnapshotFile(value) {
|
|
5083
|
-
const record = asJsonRecord(value);
|
|
5084
|
-
return (typeof record.path === "string" &&
|
|
5085
|
-
typeof record.content === "string" &&
|
|
5086
|
-
(record.kind === undefined ||
|
|
5087
|
-
record.kind === "text" ||
|
|
5088
|
-
record.kind === "binary") &&
|
|
5089
|
-
(record.encoding === undefined ||
|
|
5090
|
-
record.encoding === "utf8" ||
|
|
5091
|
-
record.encoding === "base64"));
|
|
5092
|
-
}
|
|
5093
5249
|
function createLocalEvent(type, at, event) {
|
|
5094
5250
|
return {
|
|
5095
5251
|
id: `evt_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-archive.d.ts","sourceRoot":"","sources":["../src/local-archive.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"local-archive.d.ts","sourceRoot":"","sources":["../src/local-archive.ts"],"names":[],"mappings":"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"}
|