@vela-science/canopus 0.7.0 → 0.8.0
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/CHANGELOG.md +80 -0
- package/README.md +67 -145
- package/capsules/erdos1056-k15/bin/linux-arm64/{10428801-10429000 → 10429201-10429400}/verifier +0 -0
- package/capsules/erdos1056-k15/bin/linux-x86_64/{10428801-10429000 → 10429201-10429400}/verifier +0 -0
- package/compatibility.json +25 -0
- package/dist/src/{receipt/map.d.ts → candidate/finalize.d.ts} +2 -9
- package/dist/src/candidate/finalize.js +71 -0
- package/dist/src/cli.js +89 -195
- package/dist/src/contracts/mission.d.ts +1 -13
- package/dist/src/contracts/mission.js +4 -42
- package/dist/src/contracts/validation.d.ts +1 -25
- package/dist/src/contracts/validation.js +1 -95
- package/dist/src/engines/codex-tools-native.js +2 -2
- package/dist/src/index.d.ts +5 -34
- package/dist/src/index.js +4 -34
- package/dist/src/mission/prepare.d.ts +10 -1
- package/dist/src/mission/prepare.js +29 -30
- package/dist/src/product/coverage.d.ts +17 -0
- package/dist/src/product/coverage.js +137 -0
- package/dist/src/product/doctor.d.ts +1 -2
- package/dist/src/product/doctor.js +29 -8
- package/dist/src/product/profile.js +8 -2
- package/dist/src/product/replay.js +11 -12
- package/dist/src/product/run.d.ts +5 -9
- package/dist/src/product/run.js +17 -81
- package/dist/src/product/submission.d.ts +40 -0
- package/dist/src/product/submission.js +217 -0
- package/dist/src/product/submit.d.ts +22 -0
- package/dist/src/product/submit.js +264 -0
- package/dist/src/product/version.d.ts +2 -2
- package/dist/src/product/version.js +27 -4
- package/dist/src/projection/current-run.d.ts +12 -0
- package/dist/src/projection/current-run.js +94 -0
- package/dist/src/projection/failure.d.ts +17 -7
- package/dist/src/projection/failure.js +43 -12
- package/dist/src/projection/retained-mission.d.ts +6 -0
- package/dist/src/projection/retained-mission.js +41 -0
- package/dist/src/projection/retained-run.d.ts +7 -0
- package/dist/src/projection/retained-run.js +50 -0
- package/dist/src/run.d.ts +43 -34
- package/dist/src/run.js +34 -397
- package/dist/src/util/canonical.d.ts +1 -5
- package/dist/src/util/canonical.js +1 -53
- package/dist/src/vela/cli.d.ts +7 -42
- package/dist/src/vela/cli.js +102 -475
- package/dist/src/vela/types.d.ts +3 -19
- package/dist/src/verifier/run.js +1 -1
- package/dist/src/workspace/prepare.d.ts +1 -1
- package/dist/src/workspace/prepare.js +15 -15
- package/docs/EVALUATION.md +142 -0
- package/docs/MISSIONS.md +42 -80
- package/docs/RUN_RECORD.md +77 -99
- package/docs/adr/0010-nonmutating-runs-and-explicit-submission.md +191 -0
- package/missions/erdos1056-k15-next/mission.draft.json +10 -8
- package/package.json +18 -15
- package/profiles/{erdos1056-k15-10428801-10429000.json → erdos1056-k15-10429201-10429400.json} +10 -10
- package/schemas/failure.v1.json +17 -0
- package/schemas/mission.v0.json +3 -4
- package/schemas/mission.v1.json +7 -28
- package/schemas/{run.v1.json → run.v2.json} +58 -116
- package/schemas/submission-bundle.v1.json +63 -0
- package/toolchain.lock.json +31 -0
- package/capsules/formal-erdos-505-test-dim-one/verifier +0 -105
- package/capsules/quantum-10-1-4/verifier.py +0 -152
- package/capsules/sidon-a24/bin/linux-arm64/verifier +0 -0
- package/capsules/sidon-a24/bin/linux-x86_64/verifier +0 -0
- package/dist/src/engines/codex-exec.d.ts +0 -17
- package/dist/src/engines/codex-exec.js +0 -136
- package/dist/src/engines/codex-tool-free.d.ts +0 -26
- package/dist/src/engines/codex-tool-free.js +0 -213
- package/dist/src/projection/diagnostic.d.ts +0 -12
- package/dist/src/projection/diagnostic.js +0 -61
- package/dist/src/projection/public-run.d.ts +0 -59
- package/dist/src/projection/public-run.js +0 -106
- package/dist/src/projection/publication.d.ts +0 -36
- package/dist/src/projection/publication.js +0 -47
- package/dist/src/projection/run.d.ts +0 -95
- package/dist/src/projection/run.js +0 -173
- package/dist/src/receipt/map.js +0 -168
- package/dist/src/repair/plan.d.ts +0 -2
- package/dist/src/repair/plan.js +0 -22
- package/dist/src/roles.d.ts +0 -2
- package/dist/src/roles.js +0 -9
- package/missions/formal-erdos-505-test-dim-one-gpt56/mission.draft.json +0 -40
- package/missions/quantum-10-1-4-retry/mission.draft.json +0 -40
- package/missions/sidon-a24-at-least-7194-gpt56/mission.draft.json +0 -42
- package/missions/sidon-a24-at-least-7194-gpt56-v2/mission.draft.json +0 -42
- package/missions/sidon-a24-at-least-7194-gpt56-v3/mission.draft.json +0 -42
- package/profiles/formal-erdos-505-test-dim-one-gpt56.json +0 -35
- package/profiles/quantum-10-1-4-stabilizer-retry.json +0 -35
- package/profiles/sidon-a24-at-least-7194-gpt56-v2.json +0 -35
- package/profiles/sidon-a24-at-least-7194-gpt56-v3.json +0 -35
- package/profiles/sidon-a24-at-least-7194-gpt56.json +0 -35
- package/schemas/canopus.public-run.v1.json +0 -116
package/dist/src/cli.js
CHANGED
|
@@ -3,45 +3,37 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
6
|
+
import { readdir, stat } from "node:fs/promises";
|
|
7
7
|
import { parseMission } from "./contracts/mission.js";
|
|
8
|
-
import { CodexExecEngine } from "./engines/codex-exec.js";
|
|
9
|
-
import { CodexToolsNativeEngine } from "./engines/codex-tools-native.js";
|
|
10
8
|
import { prepareMission, validateMissionBundle } from "./mission/prepare.js";
|
|
11
|
-
import {
|
|
9
|
+
import { projectCurrentRun } from "./projection/current-run.js";
|
|
10
|
+
import { parseRetainedRunRecord } from "./projection/retained-run.js";
|
|
12
11
|
import { parseFailureRecord, projectFailure } from "./projection/failure.js";
|
|
13
|
-
import { parseRunRecord, projectRun } from "./projection/run.js";
|
|
14
|
-
import { projectPublicRun } from "./projection/public-run.js";
|
|
15
|
-
import { buildPublicationBundle } from "./projection/publication.js";
|
|
16
12
|
import { doctorProduct } from "./product/doctor.js";
|
|
17
13
|
import { replayProduct } from "./product/replay.js";
|
|
18
14
|
import { runProduct } from "./product/run.js";
|
|
15
|
+
import { exportSubmission } from "./product/submission.js";
|
|
16
|
+
import { submitBundle } from "./product/submit.js";
|
|
19
17
|
import { CANOPUS_VERSION } from "./product/version.js";
|
|
20
18
|
import { listProductProfiles, packProductProfile, validateProductProfile, } from "./product/profile-bundle.js";
|
|
21
19
|
import { loadProductProfile } from "./product/profile.js";
|
|
22
|
-
import { runCanopus } from "./run.js";
|
|
23
20
|
import { readBoundedRegularFile } from "./util/files.js";
|
|
24
|
-
import { canonicalJson, contentDigest } from "./util/canonical.js";
|
|
25
|
-
import { VelaClient } from "./vela/cli.js";
|
|
26
21
|
function usage() {
|
|
27
22
|
return `Canopus — bounded Vela research harness
|
|
28
23
|
|
|
29
24
|
Primary workflow:
|
|
30
|
-
canopus --version
|
|
31
25
|
canopus doctor [frontier] [--profile <name>]
|
|
32
|
-
canopus run [frontier] [--first | --target <id>] [--profile <name>]
|
|
33
|
-
|
|
34
|
-
canopus inspect [run.json | latest]
|
|
35
|
-
canopus public-run <run.json> --mission <mission.json> \
|
|
36
|
-
--repository <public-url> --output <file>
|
|
37
|
-
canopus publish-run <run.json> --mission <mission.json> \
|
|
38
|
-
--repository <public-url> --output <new-directory>
|
|
26
|
+
canopus run [frontier] [--first | --target <id>] [--profile <name>] [--output <dir>]
|
|
27
|
+
canopus show [run.json | failure.json | latest]
|
|
39
28
|
canopus replay <run.json>
|
|
29
|
+
canopus export <run.json | latest> [--output <new-directory>] [--as <agent:id>] \\
|
|
30
|
+
[--claim <corrected-bounded-claim> --scope-limit <limit>]
|
|
31
|
+
canopus submit <submission-bundle> [frontier] [--vela <binary>]
|
|
40
32
|
|
|
41
33
|
Mission v1 prepare/validate remains available under advanced help.
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
Run and export never mutate Vela. submit explicitly registers an authenticated
|
|
36
|
+
Submission as pending review. Canopus cannot verify, decide, or change Standing.`;
|
|
45
37
|
}
|
|
46
38
|
function missionUsage() {
|
|
47
39
|
return `Usage:
|
|
@@ -66,19 +58,19 @@ remain replay-only; new tool-using missions require a validated version 2 profil
|
|
|
66
58
|
function runUsage() {
|
|
67
59
|
return `Usage:
|
|
68
60
|
canopus run [frontier] [--first | --target <id>] [--profile <name>] \\
|
|
69
|
-
[--output <dir>]
|
|
61
|
+
[--output <dir>]
|
|
70
62
|
|
|
71
63
|
Discovers and binds Vela, Codex, Git, Docker, the exact frontier roots, and the
|
|
72
|
-
registered verifier profile.
|
|
73
|
-
|
|
64
|
+
registered verifier profile. Runs the worker and verifier in disposable clones
|
|
65
|
+
and always leaves the source frontier unchanged.`;
|
|
74
66
|
}
|
|
75
|
-
function
|
|
67
|
+
function showUsage() {
|
|
76
68
|
return `Usage:
|
|
77
|
-
canopus
|
|
69
|
+
canopus show [run.json | failure.json | latest]
|
|
78
70
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
Shows the newest completed or safely stopped non-authoritative Run without
|
|
72
|
+
mutating Vela. Historical formats remain readable through their exact Canopus
|
|
73
|
+
release; the current product accepts only Run v2 and failure records.`;
|
|
82
74
|
}
|
|
83
75
|
function doctorUsage() {
|
|
84
76
|
return `Usage:
|
|
@@ -95,23 +87,23 @@ function replayUsage() {
|
|
|
95
87
|
Re-runs the frozen verifier over the content-addressed candidate without a
|
|
96
88
|
model call, Vela mutation, network, or authority action.`;
|
|
97
89
|
}
|
|
98
|
-
function
|
|
90
|
+
function exportUsage() {
|
|
99
91
|
return `Usage:
|
|
100
|
-
canopus
|
|
101
|
-
--
|
|
92
|
+
canopus export <run.json | latest> [--output <new-directory>] [--as <agent:id>] \\
|
|
93
|
+
[--claim <corrected-bounded-claim> --scope-limit <limit>]
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
Creates a signed portable vela.submission.v1 bundle from a successful Run.
|
|
96
|
+
Export does not touch a frontier and does not create Verification or Standing.
|
|
97
|
+
The correction pair is accepted only when the retained Run Claim has stale
|
|
98
|
+
verifier wording or control bytes; the immutable Run remains unchanged.`;
|
|
106
99
|
}
|
|
107
|
-
function
|
|
100
|
+
function submitUsage() {
|
|
108
101
|
return `Usage:
|
|
109
|
-
canopus
|
|
110
|
-
--repository <public-url> --output <new-directory>
|
|
102
|
+
canopus submit <submission-bundle> [frontier] [--vela <binary>]
|
|
111
103
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
Explicitly registers one authenticated Submission through Vela in a disposable
|
|
105
|
+
exact-head clone, then fast-forwards the clean source checkout. The result is
|
|
106
|
+
pending review with zero accepted-event delta.`;
|
|
115
107
|
}
|
|
116
108
|
function isHelp(value) {
|
|
117
109
|
return value === "--help" || value === "-h" || value === "help";
|
|
@@ -168,9 +160,6 @@ function required(values, key) {
|
|
|
168
160
|
throw new Error(`${key} is required`);
|
|
169
161
|
return value;
|
|
170
162
|
}
|
|
171
|
-
function authHome(values) {
|
|
172
|
-
return path.resolve(values.get("--codex-home") ?? process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex"));
|
|
173
|
-
}
|
|
174
163
|
function packagedOutputSchema() {
|
|
175
164
|
return fileURLToPath(new URL("../../schemas/engine-output.v0.json", import.meta.url));
|
|
176
165
|
}
|
|
@@ -274,67 +263,6 @@ async function profileCommand(args) {
|
|
|
274
263
|
}
|
|
275
264
|
throw new Error(`unknown profile command ${subcommand}`);
|
|
276
265
|
}
|
|
277
|
-
async function runMission(file, rest) {
|
|
278
|
-
const values = options(rest, [
|
|
279
|
-
"--source",
|
|
280
|
-
"--run-root",
|
|
281
|
-
"--vela",
|
|
282
|
-
"--docker",
|
|
283
|
-
"--codex",
|
|
284
|
-
"--codex-version",
|
|
285
|
-
"--codex-sha256",
|
|
286
|
-
"--model",
|
|
287
|
-
"--codex-home",
|
|
288
|
-
]);
|
|
289
|
-
const mission = parseMission(await jsonFile(file));
|
|
290
|
-
const sourceRepo = path.resolve(required(values, "--source"));
|
|
291
|
-
const runRoot = path.resolve(required(values, "--run-root"));
|
|
292
|
-
const velaBinary = path.resolve(required(values, "--vela"));
|
|
293
|
-
const outputSchema = mission.schema === "canopus.mission.v1"
|
|
294
|
-
? path.join(path.dirname(path.resolve(file)), "contract", "engine-output.v0.json")
|
|
295
|
-
: packagedOutputSchema();
|
|
296
|
-
const vela = new VelaClient({
|
|
297
|
-
binary: velaBinary,
|
|
298
|
-
expectedVersion: mission.vela_version,
|
|
299
|
-
expectedSha256: mission.vela_sha256,
|
|
300
|
-
home: path.join(runRoot, "vela-home"),
|
|
301
|
-
});
|
|
302
|
-
const engine = mission.schema === "canopus.mission.v1"
|
|
303
|
-
? new CodexToolsNativeEngine({
|
|
304
|
-
binary: path.resolve(required(values, "--codex")),
|
|
305
|
-
authHome: authHome(values),
|
|
306
|
-
outputSchema,
|
|
307
|
-
permissionProfile: path.join(path.dirname(path.resolve(file)), mission.worker.permission_profile_path),
|
|
308
|
-
})
|
|
309
|
-
: new CodexExecEngine({
|
|
310
|
-
binary: path.resolve(required(values, "--codex")),
|
|
311
|
-
expectedSha256: required(values, "--codex-sha256"),
|
|
312
|
-
expectedVersion: required(values, "--codex-version"),
|
|
313
|
-
model: required(values, "--model"),
|
|
314
|
-
authHome: authHome(values),
|
|
315
|
-
outputSchema,
|
|
316
|
-
});
|
|
317
|
-
const result = await runCanopus({
|
|
318
|
-
mission,
|
|
319
|
-
sourceRepo,
|
|
320
|
-
runRoot,
|
|
321
|
-
vela,
|
|
322
|
-
engine,
|
|
323
|
-
bundleRoot: path.dirname(path.resolve(file)),
|
|
324
|
-
dockerBinary: values.get("--docker") ?? "docker",
|
|
325
|
-
});
|
|
326
|
-
process.stdout.write(`${JSON.stringify({
|
|
327
|
-
ok: true,
|
|
328
|
-
command: "run",
|
|
329
|
-
run_id: result.record.run_id,
|
|
330
|
-
candidate_digest: result.record.candidate.digest,
|
|
331
|
-
receipt_root: result.record.landing.receipt_root,
|
|
332
|
-
route: result.record.landing.route,
|
|
333
|
-
accepted_event_delta: result.record.landing.accepted_event_delta,
|
|
334
|
-
clean_clone_reproduced: result.record.reproduction.matched,
|
|
335
|
-
run_file: path.join(result.paths.root, "run.json"),
|
|
336
|
-
})}\n`);
|
|
337
|
-
}
|
|
338
266
|
async function doctorCommand(args) {
|
|
339
267
|
const parsed = productOptions(args, ["--profile"], []);
|
|
340
268
|
if (parsed.positional.length > 1)
|
|
@@ -347,7 +275,7 @@ async function doctorCommand(args) {
|
|
|
347
275
|
process.stdout.write(`${JSON.stringify(result.public)}\n`);
|
|
348
276
|
}
|
|
349
277
|
async function productRunCommand(args) {
|
|
350
|
-
const parsed = productOptions(args, ["--target", "--profile", "--output", "--codex-home"], ["--first"
|
|
278
|
+
const parsed = productOptions(args, ["--target", "--profile", "--output", "--codex-home"], ["--first"]);
|
|
351
279
|
if (parsed.positional.length > 1)
|
|
352
280
|
throw new Error("run accepts at most one frontier");
|
|
353
281
|
if (parsed.flags.has("--first") && parsed.values.has("--target")) {
|
|
@@ -364,25 +292,20 @@ async function productRunCommand(args) {
|
|
|
364
292
|
...(requested === undefined ? {} : { requestedTarget: requested }),
|
|
365
293
|
...(outputRoot === undefined ? {} : { outputRoot: path.resolve(outputRoot) }),
|
|
366
294
|
...(codexHome === undefined ? {} : { codexHome: path.resolve(codexHome) }),
|
|
367
|
-
noLand: parsed.flags.has("--no-land"),
|
|
368
295
|
});
|
|
369
|
-
const landing = result.run.record.landing;
|
|
370
296
|
process.stdout.write(`${JSON.stringify({
|
|
371
297
|
ok: true,
|
|
372
298
|
command: "run",
|
|
373
|
-
|
|
299
|
+
effect: "none",
|
|
374
300
|
run_id: result.run.record.run_id,
|
|
375
301
|
target: result.run.record.mission.target,
|
|
376
302
|
candidate_digest: result.run.record.candidate.digest,
|
|
377
303
|
verifier_status: result.run.record.verifier.status,
|
|
378
304
|
observed_tokens: result.run.record.budget.observed_tokens,
|
|
379
|
-
|
|
380
|
-
proposal_id: landing?.proposal_id ?? null,
|
|
381
|
-
route: landing?.route ?? null,
|
|
382
|
-
accepted_event_delta: landing?.accepted_event_delta ?? null,
|
|
305
|
+
submission: null,
|
|
383
306
|
clean_clone_reproduced: result.run.record.reproduction.matched,
|
|
384
307
|
evidence_root: result.evidence_root,
|
|
385
|
-
|
|
308
|
+
source_state: result.source_state,
|
|
386
309
|
run_file: path.join(result.run.paths.root, "run.json"),
|
|
387
310
|
})}\n`);
|
|
388
311
|
}
|
|
@@ -419,9 +342,9 @@ async function latestInspectionFile() {
|
|
|
419
342
|
ranked.sort((left, right) => right.modified - left.modified || left.file.localeCompare(right.file));
|
|
420
343
|
return ranked[0]?.file ?? (() => { throw new Error("no inspectable Canopus product run was found"); })();
|
|
421
344
|
}
|
|
422
|
-
async function
|
|
345
|
+
async function showCommand(value, rest) {
|
|
423
346
|
if (rest.length !== 0)
|
|
424
|
-
throw new Error("
|
|
347
|
+
throw new Error("show accepts at most one run file");
|
|
425
348
|
const file = value === undefined || value === "latest"
|
|
426
349
|
? await latestInspectionFile()
|
|
427
350
|
: path.resolve(value);
|
|
@@ -429,76 +352,52 @@ async function inspectCommand(value, rest) {
|
|
|
429
352
|
const schema = typeof raw === "object" && raw !== null && !Array.isArray(raw)
|
|
430
353
|
? raw.schema
|
|
431
354
|
: undefined;
|
|
432
|
-
const projection = schema === "canopus.
|
|
433
|
-
?
|
|
434
|
-
: schema === "canopus.failure.v0"
|
|
355
|
+
const projection = schema === "canopus.run.v2"
|
|
356
|
+
? projectCurrentRun(parseRetainedRunRecord(raw).record)
|
|
357
|
+
: schema === "canopus.failure.v0" || schema === "canopus.failure.v1"
|
|
435
358
|
? projectFailure(parseFailureRecord(raw))
|
|
436
|
-
:
|
|
437
|
-
|
|
359
|
+
: (() => {
|
|
360
|
+
throw new Error("current Canopus accepts only canopus.run.v2 and retained failure records; use the exact historical release for older Run schemas");
|
|
361
|
+
})();
|
|
362
|
+
process.stdout.write(`${JSON.stringify({ ok: true, command: "show", run_file: file, projection })}\n`);
|
|
438
363
|
}
|
|
439
364
|
async function replayCommand(file, rest) {
|
|
440
365
|
if (file === undefined || rest.length !== 0)
|
|
441
366
|
throw new Error("replay requires exactly one run file");
|
|
442
367
|
process.stdout.write(`${JSON.stringify(await replayProduct(path.resolve(file)))}\n`);
|
|
443
368
|
}
|
|
444
|
-
async function
|
|
369
|
+
async function exportCommand(file, rest) {
|
|
445
370
|
if (file === undefined)
|
|
446
|
-
throw new Error("
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
await
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
})}\n`);
|
|
371
|
+
throw new Error("export requires a Run file or latest");
|
|
372
|
+
const parsed = productOptions(rest, ["--output", "--as", "--claim", "--scope-limit"], []);
|
|
373
|
+
if (parsed.positional.length !== 0)
|
|
374
|
+
throw new Error("export accepts one Run file");
|
|
375
|
+
const runFile = file === "latest" ? await latestRunFile() : path.resolve(file);
|
|
376
|
+
const output = parsed.values.get("--output") ??
|
|
377
|
+
path.join(path.dirname(path.dirname(runFile)), `submission-${Date.now()}`);
|
|
378
|
+
const actor = parsed.values.get("--as");
|
|
379
|
+
const correctedClaim = parsed.values.get("--claim");
|
|
380
|
+
const scopeLimit = parsed.values.get("--scope-limit");
|
|
381
|
+
process.stdout.write(`${JSON.stringify(await exportSubmission({
|
|
382
|
+
runFile,
|
|
383
|
+
outputRoot: path.resolve(output),
|
|
384
|
+
...(actor === undefined ? {} : { actor }),
|
|
385
|
+
...(correctedClaim === undefined ? {} : { correctedClaim }),
|
|
386
|
+
...(scopeLimit === undefined ? {} : { scopeLimit }),
|
|
387
|
+
}))}\n`);
|
|
464
388
|
}
|
|
465
|
-
async function
|
|
466
|
-
if (
|
|
467
|
-
throw new Error("
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const parent = path.dirname(output);
|
|
478
|
-
await mkdir(parent, { recursive: true, mode: 0o755 });
|
|
479
|
-
const temporary = await mkdtemp(path.join(parent, `.${path.basename(output)}.tmp-`));
|
|
480
|
-
try {
|
|
481
|
-
await Promise.all([
|
|
482
|
-
writeFile(path.join(temporary, "public-run.json"), canonicalJson(bundle.projection), { flag: "wx", mode: 0o644 }),
|
|
483
|
-
writeFile(path.join(temporary, "pending-commands.json"), canonicalJson(bundle.pendingCommands), { flag: "wx", mode: 0o644 }),
|
|
484
|
-
writeFile(path.join(temporary, "web-import.json"), canonicalJson(bundle.webImport), { flag: "wx", mode: 0o644 }),
|
|
485
|
-
writeFile(path.join(temporary, "root-manifest.json"), canonicalJson(bundle.manifest), { flag: "wx", mode: 0o644 }),
|
|
486
|
-
]);
|
|
487
|
-
await rename(temporary, output);
|
|
488
|
-
}
|
|
489
|
-
catch (error) {
|
|
490
|
-
await rm(temporary, { recursive: true, force: true });
|
|
491
|
-
throw error;
|
|
492
|
-
}
|
|
493
|
-
process.stdout.write(`${JSON.stringify({
|
|
494
|
-
ok: true,
|
|
495
|
-
command: "publish-run",
|
|
496
|
-
run_id: bundle.projection.run_id,
|
|
497
|
-
output,
|
|
498
|
-
projection_root: contentDigest(bundle.projection),
|
|
499
|
-
bundle_root: contentDigest(bundle.manifest),
|
|
500
|
-
authority_effect: "none",
|
|
501
|
-
})}\n`);
|
|
389
|
+
async function submitCommand(bundle, rest) {
|
|
390
|
+
if (bundle === undefined)
|
|
391
|
+
throw new Error("submit requires a Submission bundle");
|
|
392
|
+
const parsed = productOptions(rest, ["--vela"], []);
|
|
393
|
+
if (parsed.positional.length > 1)
|
|
394
|
+
throw new Error("submit accepts at most one frontier");
|
|
395
|
+
const velaBinary = parsed.values.get("--vela");
|
|
396
|
+
process.stdout.write(`${JSON.stringify(await submitBundle({
|
|
397
|
+
bundle: path.resolve(bundle),
|
|
398
|
+
frontier: path.resolve(parsed.positional[0] ?? "."),
|
|
399
|
+
...(velaBinary === undefined ? {} : { velaBinary }),
|
|
400
|
+
}))}\n`);
|
|
502
401
|
}
|
|
503
402
|
async function main(argv) {
|
|
504
403
|
const [command, file, ...rest] = argv;
|
|
@@ -522,11 +421,11 @@ async function main(argv) {
|
|
|
522
421
|
}
|
|
523
422
|
if (isHelp(file)) {
|
|
524
423
|
process.stdout.write(`${command === "run" ? runUsage()
|
|
525
|
-
: command === "
|
|
424
|
+
: command === "show" ? showUsage()
|
|
526
425
|
: command === "doctor" ? doctorUsage()
|
|
527
426
|
: command === "replay" ? replayUsage()
|
|
528
|
-
: command === "
|
|
529
|
-
: command === "
|
|
427
|
+
: command === "export" ? exportUsage()
|
|
428
|
+
: command === "submit" ? submitUsage()
|
|
530
429
|
: usage()}\n`);
|
|
531
430
|
return;
|
|
532
431
|
}
|
|
@@ -538,31 +437,26 @@ async function main(argv) {
|
|
|
538
437
|
await replayCommand(file, rest);
|
|
539
438
|
return;
|
|
540
439
|
}
|
|
541
|
-
if (command === "public-run") {
|
|
542
|
-
await publicRunCommand(file, rest);
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
if (command === "publish-run") {
|
|
546
|
-
await publishRunCommand(file, rest);
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
440
|
if (command === "validate") {
|
|
550
441
|
if (file === undefined)
|
|
551
442
|
throw new Error("validate requires a mission file");
|
|
552
443
|
await missionCommand(["validate", file, ...rest]);
|
|
553
444
|
return;
|
|
554
445
|
}
|
|
555
|
-
if (command === "
|
|
556
|
-
await
|
|
446
|
+
if (command === "show") {
|
|
447
|
+
await showCommand(file, rest);
|
|
557
448
|
return;
|
|
558
449
|
}
|
|
559
450
|
if (command === "run") {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
451
|
+
await productRunCommand(argv.slice(1));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (command === "export") {
|
|
455
|
+
await exportCommand(file, rest);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (command === "submit") {
|
|
459
|
+
await submitCommand(file, rest);
|
|
566
460
|
return;
|
|
567
461
|
}
|
|
568
462
|
throw new Error(`unknown command ${command}`);
|
|
@@ -13,8 +13,7 @@ export type Replayability = (typeof REPLAYABILITY)[number];
|
|
|
13
13
|
export interface MissionRoots {
|
|
14
14
|
git_commit: string;
|
|
15
15
|
git_tree: string;
|
|
16
|
-
|
|
17
|
-
vela_snapshot: string;
|
|
16
|
+
vela_repository: string;
|
|
18
17
|
}
|
|
19
18
|
export interface MissionBudgets {
|
|
20
19
|
max_research_wall_time_ms: number;
|
|
@@ -61,16 +60,6 @@ export interface ProducerProfileSpec {
|
|
|
61
60
|
name: string;
|
|
62
61
|
root: string;
|
|
63
62
|
}
|
|
64
|
-
export interface StrictRuleCount {
|
|
65
|
-
rule: string;
|
|
66
|
-
count: number;
|
|
67
|
-
}
|
|
68
|
-
export interface StrictBaseline {
|
|
69
|
-
status: "pass" | "fail";
|
|
70
|
-
blocker_count: number;
|
|
71
|
-
blockers_root: string;
|
|
72
|
-
rule_counts: StrictRuleCount[];
|
|
73
|
-
}
|
|
74
63
|
export interface LandingSpec {
|
|
75
64
|
expected_routes: LandRoute[];
|
|
76
65
|
max_accepted_delta: number;
|
|
@@ -127,7 +116,6 @@ export interface MissionV1 extends MissionBase {
|
|
|
127
116
|
schema: typeof MISSION_V1_SCHEMA;
|
|
128
117
|
target_packet: TargetPacketSpec;
|
|
129
118
|
profile?: ProducerProfileSpec;
|
|
130
|
-
strict_baseline: StrictBaseline;
|
|
131
119
|
worker: WorkerSpec;
|
|
132
120
|
verifier: ContainerVerifierSpec;
|
|
133
121
|
execution_binding?: ExecutionBindingV1;
|
|
@@ -24,12 +24,11 @@ export const REPLAYABILITY = [
|
|
|
24
24
|
];
|
|
25
25
|
function parseRoots(value) {
|
|
26
26
|
const object = objectAt(value, "mission.roots");
|
|
27
|
-
exactKeys(object, ["git_commit", "git_tree", "
|
|
27
|
+
exactKeys(object, ["git_commit", "git_tree", "vela_repository"], [], "mission.roots");
|
|
28
28
|
return {
|
|
29
29
|
git_commit: gitObjectAt(object.git_commit, "mission.roots.git_commit"),
|
|
30
30
|
git_tree: gitObjectAt(object.git_tree, "mission.roots.git_tree"),
|
|
31
|
-
|
|
32
|
-
vela_snapshot: sha256At(object.vela_snapshot, "mission.roots.vela_snapshot"),
|
|
31
|
+
vela_repository: sha256At(object.vela_repository, "mission.roots.vela_repository"),
|
|
33
32
|
};
|
|
34
33
|
}
|
|
35
34
|
function frontierPathAt(value) {
|
|
@@ -118,41 +117,6 @@ function parseTargetPacket(value) {
|
|
|
118
117
|
sha256: sha256At(object.sha256, "mission.target_packet.sha256"),
|
|
119
118
|
};
|
|
120
119
|
}
|
|
121
|
-
function parseStrictBaseline(value) {
|
|
122
|
-
const object = objectAt(value, "mission.strict_baseline");
|
|
123
|
-
exactKeys(object, ["status", "blocker_count", "blockers_root", "rule_counts"], [], "mission.strict_baseline");
|
|
124
|
-
const rule_counts = arrayAt(object.rule_counts, "mission.strict_baseline.rule_counts", { min: 0, max: 64 }, (item, at) => {
|
|
125
|
-
const entry = objectAt(item, at);
|
|
126
|
-
exactKeys(entry, ["rule", "count"], [], at);
|
|
127
|
-
return {
|
|
128
|
-
rule: stringAt(entry.rule, `${at}.rule`, {
|
|
129
|
-
min: 1,
|
|
130
|
-
max: 128,
|
|
131
|
-
pattern: /^[a-z][a-z0-9_]*$/u,
|
|
132
|
-
}),
|
|
133
|
-
count: integerAt(entry.count, `${at}.count`, 1, 1_000_000),
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
for (let index = 1; index < rule_counts.length; index += 1) {
|
|
137
|
-
if ((rule_counts[index - 1]?.rule ?? "") >= (rule_counts[index]?.rule ?? "")) {
|
|
138
|
-
throw new ContractError("mission.strict_baseline.rule_counts must be sorted and unique");
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
const blocker_count = integerAt(object.blocker_count, "mission.strict_baseline.blocker_count", 0, 1_000_000);
|
|
142
|
-
if (rule_counts.reduce((sum, entry) => sum + entry.count, 0) !== blocker_count) {
|
|
143
|
-
throw new ContractError("mission.strict_baseline.rule_counts must sum to blocker_count");
|
|
144
|
-
}
|
|
145
|
-
const status = enumAt(object.status, "mission.strict_baseline.status", ["pass", "fail"]);
|
|
146
|
-
if ((status === "pass") !== (blocker_count === 0)) {
|
|
147
|
-
throw new ContractError("mission.strict_baseline status must agree with blocker_count");
|
|
148
|
-
}
|
|
149
|
-
return {
|
|
150
|
-
status,
|
|
151
|
-
blocker_count,
|
|
152
|
-
blockers_root: sha256At(object.blockers_root, "mission.strict_baseline.blockers_root"),
|
|
153
|
-
rule_counts,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
120
|
function parseLanding(value) {
|
|
157
121
|
const object = objectAt(value, "mission.landing");
|
|
158
122
|
exactKeys(object, ["expected_routes", "max_accepted_delta"], [], "mission.landing");
|
|
@@ -275,14 +239,13 @@ export function parseMission(value) {
|
|
|
275
239
|
"repair_reason",
|
|
276
240
|
"target_packet",
|
|
277
241
|
"profile",
|
|
278
|
-
"strict_baseline",
|
|
279
242
|
"worker",
|
|
280
243
|
"execution_binding",
|
|
281
244
|
"result_contract",
|
|
282
245
|
]
|
|
283
246
|
: ["parent_candidate", "repair_reason"], "mission");
|
|
284
247
|
if (isV1) {
|
|
285
|
-
for (const required of ["target_packet", "
|
|
248
|
+
for (const required of ["target_packet", "worker"]) {
|
|
286
249
|
if (object[required] === undefined) {
|
|
287
250
|
throw new ContractError(`mission.${required} is required for mission v1`);
|
|
288
251
|
}
|
|
@@ -313,7 +276,7 @@ export function parseMission(value) {
|
|
|
313
276
|
vela_version: stringAt(object.vela_version, "mission.vela_version", {
|
|
314
277
|
min: 5,
|
|
315
278
|
max: 32,
|
|
316
|
-
pattern: /^0\.[0-9]+\.[0-9]
|
|
279
|
+
pattern: /^0\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u,
|
|
317
280
|
}),
|
|
318
281
|
vela_sha256: sha256At(object.vela_sha256, "mission.vela_sha256"),
|
|
319
282
|
frontier: frontierPathAt(object.frontier),
|
|
@@ -376,7 +339,6 @@ export function parseMission(value) {
|
|
|
376
339
|
schema: MISSION_V1_SCHEMA,
|
|
377
340
|
target_packet: targetPacket,
|
|
378
341
|
...(profile === undefined ? {} : { profile }),
|
|
379
|
-
strict_baseline: parseStrictBaseline(object.strict_baseline),
|
|
380
342
|
worker: parseWorker(object.worker),
|
|
381
343
|
verifier: verifier,
|
|
382
344
|
...(executionBinding === undefined ? {} : { execution_binding: executionBinding }),
|
|
@@ -1,25 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
constructor(message: string);
|
|
3
|
-
}
|
|
4
|
-
export type JsonObject = Record<string, unknown>;
|
|
5
|
-
export declare function objectAt(value: unknown, at: string): JsonObject;
|
|
6
|
-
export declare function exactKeys(value: JsonObject, required: readonly string[], optional: readonly string[], at: string): void;
|
|
7
|
-
export declare function stringAt(value: unknown, at: string, options: {
|
|
8
|
-
min?: number;
|
|
9
|
-
max: number;
|
|
10
|
-
pattern?: RegExp;
|
|
11
|
-
}): string;
|
|
12
|
-
export declare function integerAt(value: unknown, at: string, min: number, max: number): number;
|
|
13
|
-
export declare function enumAt<const T extends string>(value: unknown, at: string, allowed: readonly T[]): T;
|
|
14
|
-
export declare function arrayAt<T>(value: unknown, at: string, options: {
|
|
15
|
-
min?: number;
|
|
16
|
-
max: number;
|
|
17
|
-
unique?: boolean;
|
|
18
|
-
}, parse: (item: unknown, at: string) => T): T[];
|
|
19
|
-
export declare const GIT_OBJECT_RE: RegExp;
|
|
20
|
-
export declare const SHA256_RE: RegExp;
|
|
21
|
-
export declare const MISSION_ID_RE: RegExp;
|
|
22
|
-
export declare const AGENT_RE: RegExp;
|
|
23
|
-
export declare function relativePathAt(value: unknown, at: string): string;
|
|
24
|
-
export declare function sha256At(value: unknown, at: string): string;
|
|
25
|
-
export declare function gitObjectAt(value: unknown, at: string): string;
|
|
1
|
+
export { AGENT_RE, ContractError, GIT_OBJECT_RE, MISSION_ID_RE, SHA256_RE, arrayAt, enumAt, exactKeys, gitObjectAt, integerAt, objectAt, relativePathAt, sha256At, stringAt, type JsonObject, } from "@vela-science/protocol";
|