@kungfu-tech/buildchain 3.0.6-alpha.0 → 3.0.6-alpha.2

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/README.md +4 -4
  2. package/actions/promote-buildchain-ref/README.md +8 -0
  3. package/bin/buildchain.mjs +13 -1
  4. package/contracts/auditable-demo-scenario-v1.schema.json +52 -0
  5. package/dist/site/buildchain-contract.json +47 -27
  6. package/dist/site/buildchain-site.json +165 -44
  7. package/dist/site/capability-registry.json +3 -3
  8. package/dist/site/cli-registry.json +40 -4
  9. package/dist/site/controller-registry.json +20 -4
  10. package/dist/site/kfd-claims.json +140 -19
  11. package/dist/site/kfd-upstream-aggregate.json +9 -9
  12. package/dist/site/manual-registry.json +9 -9
  13. package/dist/site/node-api-registry.json +1161 -180
  14. package/dist/site/page-registry.json +152 -31
  15. package/dist/site/public-surface-audit.json +386 -19
  16. package/dist/site/publication-authority-registry.json +61 -1
  17. package/dist/site/publication-registry.json +4 -4
  18. package/dist/site/release-provenance.json +1 -0
  19. package/dist/site/site-manifest.json +13 -13
  20. package/dist/site/workflow-registry.json +151 -13
  21. package/docs/MAP.md +2 -0
  22. package/docs/auditable-demo.md +58 -11
  23. package/docs/aws-us-elastic-runner-burst-plane.md +114 -80
  24. package/docs/cli-reference.md +154 -0
  25. package/docs/dev-alpha-candidate-patrol.md +13 -5
  26. package/docs/dev-delivery-warrant.md +158 -0
  27. package/docs/node-api-reference.md +54 -15
  28. package/docs/publication-authority.md +11 -0
  29. package/docs/release-candidate.md +19 -2
  30. package/docs/release-governance.md +60 -1
  31. package/docs/reusable-build-surface.md +11 -1
  32. package/docs/shifu-gate-profiles.md +12 -1
  33. package/docs/versioning.md +2 -0
  34. package/package.json +4 -2
  35. package/packages/core/buildchain-publication-authority.js +3 -1
  36. package/packages/core/channel-candidate.js +2 -21
  37. package/packages/core/channel-promotion-baseline.js +199 -0
  38. package/packages/core/dev-delivery-candidate-identity.js +94 -0
  39. package/packages/core/dev-delivery-common.js +73 -0
  40. package/packages/core/dev-delivery-proof.js +252 -0
  41. package/packages/core/dev-delivery-warrant-cancellation.js +94 -0
  42. package/packages/core/dev-delivery-warrant-settlement.js +73 -0
  43. package/packages/core/dev-delivery-warrant.js +591 -0
  44. package/scripts/auditable-demo-bundle-verification.mjs +148 -0
  45. package/scripts/auditable-demo-platform.mjs +86 -50
  46. package/scripts/auditable-demo-presentation.mjs +83 -0
  47. package/scripts/auditable-demo-renditions.mjs +264 -0
  48. package/scripts/auditable-demo.mjs +24 -30
  49. package/scripts/aws-windows-jit-campaign-core.mjs +7 -8
  50. package/scripts/aws-windows-jit-controller.mjs +1 -0
  51. package/scripts/aws-windows-jit-core.mjs +1 -1
  52. package/scripts/build-contract-core.mjs +58 -3
  53. package/scripts/buildchain-cli-help.mjs +8 -0
  54. package/scripts/buildchain-patrol.mjs +9 -0
  55. package/scripts/check-inventory.mjs +1 -0
  56. package/scripts/dev-alpha-candidate-patrol.mjs +45 -48
  57. package/scripts/dev-delivery-proof.mjs +193 -0
  58. package/scripts/dev-delivery-warrant.mjs +426 -0
  59. package/scripts/dev-pr-auto-merge.mjs +497 -55
  60. package/scripts/dev-pr-delivery-warrant.mjs +209 -0
  61. package/scripts/dispatch-artifact-signing-authority.mjs +2 -4
  62. package/scripts/gate-profile-core.mjs +24 -0
  63. package/scripts/generate-site-bundle.mjs +2 -2
  64. package/scripts/git-fetch-process-tree.mjs +142 -0
  65. package/scripts/lifecycle-substage-evidence.mjs +274 -0
  66. package/scripts/locked-source-checkout.mjs +6 -3
  67. package/scripts/resolve-artifact-transfer-mode.mjs +9 -0
  68. package/scripts/resolve-build-contract.mjs +7 -0
  69. package/scripts/route-offline-runners.mjs +1 -0
  70. package/scripts/run-lifecycle-core.mjs +9 -9
  71. package/scripts/shifu-gate-profile.mjs +10 -16
  72. package/scripts/site-capability-metadata.mjs +14 -0
@@ -0,0 +1,426 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { cancelQueuedDevDeliveryCandidate, closeDevDeliveryWarrant, createDevDeliveryQueue, heartbeatDevDeliveryWarrant, observeDevDeliveryQueue, recoverExpiredDevDeliveryWarrant, selectDevDeliveryWarrant, settleDevDeliveryTerminalEvent, submitDevDeliveryCandidate } from "../packages/core/dev-delivery-warrant.js";
5
+
6
+ const STATE_PATH = "queue.json";
7
+ const STATE_REF_PREFIX = "buildchain/dev-delivery-warrant/";
8
+
9
+ function text(value = "") {
10
+ return String(value ?? "").trim();
11
+ }
12
+
13
+ function bool(value, fallback = false) {
14
+ if (value === undefined || value === null || value === "") return fallback;
15
+ if (typeof value === "boolean") return value;
16
+ return ["1", "true", "yes", "on"].includes(text(value).toLowerCase());
17
+ }
18
+
19
+ function positiveInteger(value, label, fallback = 0) {
20
+ const parsed = Number(value ?? fallback);
21
+ if (!Number.isInteger(parsed) || parsed < 1) throw new Error(`${label} must be a positive integer`);
22
+ return parsed;
23
+ }
24
+
25
+ function exactRoot(value, label) {
26
+ const normalized = text(value).toLowerCase();
27
+ if (!/^sha256:[0-9a-f]{64}$/.test(normalized)) throw new Error(`${label} must be a sha256 content root`);
28
+ return normalized;
29
+ }
30
+
31
+ function exactSha(value, label) {
32
+ const normalized = text(value).toLowerCase();
33
+ if (!/^[0-9a-f]{40}$/.test(normalized)) throw new Error(`${label} must be a 40-character Git SHA`);
34
+ return normalized;
35
+ }
36
+
37
+ function normalizeRepository(value) {
38
+ const normalized = text(value);
39
+ const match = normalized.match(/^([^/\s]+)\/([^/\s]+)$/);
40
+ if (!match) throw new Error(`repository must be owner/repo, got ${normalized || "<empty>"}`);
41
+ return { owner: match[1], repo: match[2], fullName: normalized };
42
+ }
43
+
44
+ function normalizeBranch(value) {
45
+ const normalized = text(value).replace(/^refs\/heads\//, "");
46
+ if (!/^dev\/v\d+\/v\d+\.\d+$/.test(normalized)) {
47
+ throw new Error(`branch must be dev/vN/vN.M, got ${normalized || "<empty>"}`);
48
+ }
49
+ return normalized;
50
+ }
51
+
52
+ export function defaultDevDeliveryStateRef(branch) {
53
+ const normalized = normalizeBranch(branch);
54
+ return `${STATE_REF_PREFIX}${normalized.replaceAll("/", "-")}`;
55
+ }
56
+
57
+ function normalizeStateRef(value, branch) {
58
+ const normalized = text(value || defaultDevDeliveryStateRef(branch)).replace(/^refs\/heads\//, "");
59
+ if (!normalized.startsWith(STATE_REF_PREFIX) || normalized.includes("..") || normalized.endsWith("/")) {
60
+ throw new Error(`state ref must remain under ${STATE_REF_PREFIX}`);
61
+ }
62
+ return normalized;
63
+ }
64
+
65
+ function encodeRef(ref) {
66
+ return ref.split("/").map(encodeURIComponent).join("/");
67
+ }
68
+
69
+ function decodeBlob(blob) {
70
+ if (blob.encoding !== "base64") throw new Error(`unsupported Git blob encoding ${blob.encoding || "<empty>"}`);
71
+ return Buffer.from(String(blob.content || "").replace(/\s+/g, ""), "base64").toString("utf8");
72
+ }
73
+
74
+ export class GitHubDevDeliveryStore {
75
+ constructor({ repository, token, apiUrl = "https://api.github.com", fetchImpl = globalThis.fetch } = {}) {
76
+ this.repository = normalizeRepository(repository);
77
+ if (!fetchImpl) throw new Error("fetch is required");
78
+ if (!token) throw new Error("GITHUB_TOKEN is required for the GitHub dev delivery store");
79
+ this.token = token;
80
+ this.apiUrl = apiUrl.replace(/\/+$/, "");
81
+ this.fetch = fetchImpl;
82
+ }
83
+
84
+ async request(method, requestPath, body) {
85
+ const response = await this.fetch(`${this.apiUrl}${requestPath}`, {
86
+ method,
87
+ headers: {
88
+ accept: "application/vnd.github+json",
89
+ authorization: `Bearer ${this.token}`,
90
+ "content-type": "application/json",
91
+ "x-github-api-version": "2022-11-28",
92
+ },
93
+ body: body === undefined ? undefined : JSON.stringify(body),
94
+ });
95
+ const raw = await response.text();
96
+ const data = raw ? JSON.parse(raw) : null;
97
+ if (!response.ok) {
98
+ const error = new Error(data?.message || raw || `${method} ${requestPath} failed`);
99
+ error.status = response.status;
100
+ error.data = data;
101
+ throw error;
102
+ }
103
+ return data;
104
+ }
105
+
106
+ async read({ stateRef, protectedBase, now }) {
107
+ let ref;
108
+ try {
109
+ ref = await this.request("GET", `/repos/${this.repository.fullName}/git/ref/heads/${encodeRef(stateRef)}`);
110
+ } catch (error) {
111
+ if (error.status !== 404) throw error;
112
+ return {
113
+ exists: false,
114
+ commitSha: "",
115
+ queue: createDevDeliveryQueue({
116
+ repository: this.repository.fullName,
117
+ protectedBase,
118
+ now,
119
+ }),
120
+ };
121
+ }
122
+ const commitSha = exactSha(ref?.object?.sha, "state ref commit");
123
+ const readback = await this.readCommit(commitSha);
124
+ return { exists: true, ...readback };
125
+ }
126
+
127
+ async readCommit(commitShaInput) {
128
+ const commitSha = exactSha(commitShaInput, "state commit");
129
+ const commit = await this.request("GET", `/repos/${this.repository.fullName}/git/commits/${commitSha}`);
130
+ const tree = await this.request("GET", `/repos/${this.repository.fullName}/git/trees/${commit.tree?.sha}`);
131
+ const entry = (tree.tree || []).find((item) => item.path === STATE_PATH && item.type === "blob");
132
+ if (!entry?.sha) throw new Error(`${commitSha} does not contain ${STATE_PATH}`);
133
+ const blob = await this.request("GET", `/repos/${this.repository.fullName}/git/blobs/${entry.sha}`);
134
+ const queue = JSON.parse(decodeBlob(blob));
135
+ return { commitSha, queue };
136
+ }
137
+
138
+ async write({ stateRef, queue, expectedCommitSha, expectedStateRoot, receiptRoot }) {
139
+ if (queue.stateRoot === expectedStateRoot) throw new Error("state transition did not advance the queue root");
140
+ const blob = await this.request("POST", `/repos/${this.repository.fullName}/git/blobs`, {
141
+ content: `${JSON.stringify(queue, null, 2)}\n`,
142
+ encoding: "utf-8",
143
+ });
144
+ const tree = await this.request("POST", `/repos/${this.repository.fullName}/git/trees`, {
145
+ tree: [{ path: STATE_PATH, mode: "100644", type: "blob", sha: blob.sha }],
146
+ });
147
+ const commit = await this.request("POST", `/repos/${this.repository.fullName}/git/commits`, {
148
+ message: `chore(dev-delivery): advance Warrant queue ${receiptRoot.slice(0, 20)}`,
149
+ tree: tree.sha,
150
+ parents: expectedCommitSha ? [expectedCommitSha] : [],
151
+ });
152
+ if (expectedCommitSha) {
153
+ await this.request("PATCH", `/repos/${this.repository.fullName}/git/refs/heads/${encodeRef(stateRef)}`, { sha: commit.sha, force: false });
154
+ } else {
155
+ await this.request("POST", `/repos/${this.repository.fullName}/git/refs`, {
156
+ ref: `refs/heads/${stateRef}`,
157
+ sha: commit.sha,
158
+ });
159
+ }
160
+ const readback = await this.readCommit(commit.sha);
161
+ if (readback.commitSha !== commit.sha || readback.queue.stateRoot !== queue.stateRoot) {
162
+ throw new Error("dev delivery state commit readback mismatch after expected-old update");
163
+ }
164
+ return { commitSha: commit.sha, stateRoot: readback.queue.stateRoot };
165
+ }
166
+ }
167
+
168
+ function warrantIdentity(queue, options) {
169
+ const active = queue.activeWarrant;
170
+ if (!active) throw new Error("no active Delivery Warrant");
171
+ const fencingToken = exactRoot(options.fencingToken, "fencingToken");
172
+ const generation = positiveInteger(options.leaseGeneration, "leaseGeneration");
173
+ return { candidateId: active.candidateId, fencingToken, generation };
174
+ }
175
+
176
+ function transitionFor(command, queue, options) {
177
+ if (command === "submit") {
178
+ return submitDevDeliveryCandidate(
179
+ queue,
180
+ {
181
+ pullRequestNumber: positiveInteger(options.pullRequestNumber, "pullRequestNumber"),
182
+ sourceHead: exactSha(options.sourceHead, "sourceHead"),
183
+ assignmentRoot: exactRoot(options.assignmentRoot, "assignmentRoot"),
184
+ initiativeRoot: exactRoot(options.initiativeRoot, "initiativeRoot"),
185
+ sourceIdentityRoot: exactRoot(options.sourceIdentityRoot, "sourceIdentityRoot"),
186
+ sourcePatchRoot: exactRoot(options.sourcePatchRoot, "sourcePatchRoot"),
187
+ sourceProofRoot: exactRoot(options.sourceProofRoot, "sourceProofRoot"),
188
+ planRoot: exactRoot(options.planRoot, "planRoot"),
189
+ closureRoot: exactRoot(options.closureRoot, "closureRoot"),
190
+ dependencyRoot: exactRoot(options.dependencyRoot, "dependencyRoot"),
191
+ toolchainRoot: exactRoot(options.toolchainRoot, "toolchainRoot"),
192
+ deliveryClass: options.deliveryClass,
193
+ priority: options.priority || "ordinary",
194
+ },
195
+ { now: options.now },
196
+ );
197
+ }
198
+ if (command === "select") {
199
+ return selectDevDeliveryWarrant(queue, {
200
+ now: options.now,
201
+ leaseSeconds: options.leaseSeconds,
202
+ });
203
+ }
204
+ if (command === "heartbeat") {
205
+ return heartbeatDevDeliveryWarrant(queue, warrantIdentity(queue, options), {
206
+ now: options.now,
207
+ leaseSeconds: options.leaseSeconds,
208
+ });
209
+ }
210
+ if (command === "recover") return recoverExpiredDevDeliveryWarrant(queue, { now: options.now });
211
+ if (command === "close") {
212
+ return closeDevDeliveryWarrant(queue, warrantIdentity(queue, options), {
213
+ outcome: options.outcome,
214
+ evidenceRoot: exactRoot(options.evidenceRoot, "evidenceRoot"),
215
+ reason: options.reason,
216
+ now: options.now,
217
+ });
218
+ }
219
+ if (command === "settle") {
220
+ return settleDevDeliveryTerminalEvent(
221
+ queue,
222
+ {
223
+ pullRequestNumber: positiveInteger(options.pullRequestNumber, "pullRequestNumber"),
224
+ sourceHead: exactSha(options.expectedSourceHead || options.sourceHead, "sourceHead"),
225
+ fencingToken: options.fencingToken,
226
+ leaseGeneration: options.leaseGeneration,
227
+ outcome: options.outcome,
228
+ eventAction: options.eventAction,
229
+ evidenceRoot: options.evidenceRoot,
230
+ reason: options.reason,
231
+ },
232
+ { now: options.now },
233
+ );
234
+ }
235
+ if (command === "cancel-queued") {
236
+ return cancelQueuedDevDeliveryCandidate(
237
+ queue,
238
+ {
239
+ candidateId: exactRoot(options.candidateId, "candidateId"),
240
+ pullRequestNumber: positiveInteger(options.pullRequestNumber, "pullRequestNumber"),
241
+ expectedSourceHead: exactSha(options.expectedSourceHead, "expectedSourceHead"),
242
+ observedSourceHead: exactSha(options.observedSourceHead, "observedSourceHead"),
243
+ eventAction: options.eventAction,
244
+ outcome: options.outcome,
245
+ evidenceRoot: exactRoot(options.evidenceRoot, "evidenceRoot"),
246
+ reason: options.reason,
247
+ },
248
+ { now: options.now },
249
+ );
250
+ }
251
+ throw new Error(`unsupported dev delivery command ${command || "<empty>"}`);
252
+ }
253
+
254
+ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
255
+ const options = {
256
+ ...optionsInput,
257
+ repository: normalizeRepository(optionsInput.repository).fullName,
258
+ branch: normalizeBranch(optionsInput.branch),
259
+ stateRef: normalizeStateRef(optionsInput.stateRef, optionsInput.branch),
260
+ now: new Date(optionsInput.now || Date.now()).toISOString(),
261
+ execute: bool(optionsInput.execute, false),
262
+ };
263
+ const store =
264
+ clientInput ||
265
+ new GitHubDevDeliveryStore({
266
+ repository: options.repository,
267
+ token: options.token || process.env.GITHUB_TOKEN,
268
+ apiUrl: options.apiUrl || process.env.GITHUB_API_URL || "https://api.github.com",
269
+ });
270
+ let loaded = await store.read({
271
+ stateRef: options.stateRef,
272
+ protectedBase: options.branch,
273
+ now: options.now,
274
+ });
275
+ if (options.expectedOldStateRoot && loaded.queue.stateRoot !== options.expectedOldStateRoot) {
276
+ throw new Error(`expected-old state drift: ${loaded.queue.stateRoot} != ${options.expectedOldStateRoot}`);
277
+ }
278
+ if (options.command === "observe") {
279
+ return {
280
+ schema: "kungfu.buildchain.dev-delivery-command-result/v1",
281
+ ok: true,
282
+ mode: "observe",
283
+ stateRef: options.stateRef,
284
+ stateCommit: loaded.commitSha,
285
+ observation: observeDevDeliveryQueue(loaded.queue, { now: options.now }),
286
+ };
287
+ }
288
+ const initialLoaded = loaded;
289
+ let changed = transitionFor(options.command, loaded.queue, options);
290
+ let mutates = changed.queue.stateRoot !== loaded.queue.stateRoot;
291
+ let write = null;
292
+ let concurrencyRecovery = null;
293
+ if (options.execute && mutates) {
294
+ if (changed.receipt.expectedOldStateRoot !== loaded.queue.stateRoot) {
295
+ throw new Error("transition receipt expected-old root does not match the loaded authority");
296
+ }
297
+ try {
298
+ write = await store.write({
299
+ stateRef: options.stateRef,
300
+ queue: changed.queue,
301
+ expectedCommitSha: loaded.commitSha,
302
+ expectedStateRoot: loaded.queue.stateRoot,
303
+ receiptRoot: changed.receiptRoot,
304
+ });
305
+ } catch (error) {
306
+ if (options.command !== "settle" || options.expectedOldStateRoot) throw error;
307
+ const latest = await store.read({
308
+ stateRef: options.stateRef,
309
+ protectedBase: options.branch,
310
+ now: options.now,
311
+ });
312
+ const reconciled = transitionFor(options.command, latest.queue, options);
313
+ const reconciledMutates = reconciled.queue.stateRoot !== latest.queue.stateRoot;
314
+ if (reconciledMutates || reconciled.receipt.action !== "duplicate-terminal-event-noop") throw error;
315
+ loaded = latest;
316
+ changed = reconciled;
317
+ mutates = false;
318
+ concurrencyRecovery = {
319
+ schema: "kungfu.buildchain.dev-delivery-concurrency-recovery/v1",
320
+ action: "terminal-settlement-race-noop",
321
+ initialCommitSha: initialLoaded.commitSha,
322
+ observedCommitSha: latest.commitSha,
323
+ observedStateRoot: latest.queue.stateRoot,
324
+ };
325
+ }
326
+ }
327
+ return {
328
+ schema: "kungfu.buildchain.dev-delivery-command-result/v1",
329
+ ok: true,
330
+ mode: options.execute ? "execute" : "plan",
331
+ command: options.command,
332
+ stateRef: options.stateRef,
333
+ before: { commitSha: loaded.commitSha, stateRoot: loaded.queue.stateRoot },
334
+ after: {
335
+ commitSha: write?.commitSha || loaded.commitSha,
336
+ stateRoot: changed.queue.stateRoot,
337
+ },
338
+ mutationAuthorized: options.execute,
339
+ mutationApplied: Boolean(write),
340
+ concurrencyRecovery,
341
+ receipt: changed.receipt,
342
+ receiptRoot: changed.receiptRoot,
343
+ warrant: changed.warrant || changed.queue.activeWarrant || null,
344
+ observation: observeDevDeliveryQueue(changed.queue, { now: options.now }),
345
+ };
346
+ }
347
+
348
+ function flag(args, name, fallback = "") {
349
+ const index = args.indexOf(`--${name}`);
350
+ return index === -1 ? fallback : args[index + 1] || "";
351
+ }
352
+
353
+ function hasFlag(args, name) {
354
+ return args.includes(`--${name}`);
355
+ }
356
+
357
+ export function devDeliveryCliOptions(args = [], environment = process.env) {
358
+ const [command = "", ...rest] = args;
359
+ return {
360
+ command,
361
+ repository: flag(rest, "repository", environment.GITHUB_REPOSITORY),
362
+ branch: flag(rest, "branch", environment.BUILDCHAIN_DEV_DELIVERY_BRANCH || environment.GITHUB_BASE_REF),
363
+ stateRef: flag(rest, "state-ref", environment.BUILDCHAIN_DEV_DELIVERY_STATE_REF),
364
+ expectedOldStateRoot: flag(rest, "expected-old", environment.BUILDCHAIN_DEV_DELIVERY_EXPECTED_OLD),
365
+ pullRequestNumber: flag(rest, "pull-request", environment.BUILDCHAIN_DEV_DELIVERY_PR_NUMBER),
366
+ candidateId: flag(rest, "candidate-id", environment.BUILDCHAIN_DEV_DELIVERY_CANDIDATE_ID),
367
+ sourceHead: flag(rest, "source-head", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_HEAD),
368
+ expectedSourceHead: flag(rest, "expected-source-head", environment.BUILDCHAIN_DEV_DELIVERY_EXPECTED_SOURCE_HEAD),
369
+ observedSourceHead: flag(rest, "observed-source-head", environment.BUILDCHAIN_DEV_DELIVERY_OBSERVED_SOURCE_HEAD),
370
+ assignmentRoot: flag(rest, "assignment-root", environment.BUILDCHAIN_DEV_DELIVERY_ASSIGNMENT_ROOT),
371
+ initiativeRoot: flag(rest, "initiative-root", environment.BUILDCHAIN_DEV_DELIVERY_INITIATIVE_ROOT),
372
+ sourceIdentityRoot: flag(rest, "source-identity-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_IDENTITY_ROOT),
373
+ sourcePatchRoot: flag(rest, "source-patch-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PATCH_ROOT),
374
+ sourceProofRoot: flag(rest, "source-proof-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PROOF_ROOT),
375
+ planRoot: flag(rest, "plan-root", environment.BUILDCHAIN_DEV_DELIVERY_PLAN_ROOT),
376
+ closureRoot: flag(rest, "closure-root", environment.BUILDCHAIN_DEV_DELIVERY_CLOSURE_ROOT),
377
+ dependencyRoot: flag(rest, "dependency-root", environment.BUILDCHAIN_DEV_DELIVERY_DEPENDENCY_ROOT),
378
+ toolchainRoot: flag(rest, "toolchain-root", environment.BUILDCHAIN_DEV_DELIVERY_TOOLCHAIN_ROOT),
379
+ deliveryClass: flag(rest, "delivery-class", environment.BUILDCHAIN_DEV_DELIVERY_CLASS),
380
+ priority: flag(rest, "priority", environment.BUILDCHAIN_DEV_DELIVERY_PRIORITY || "ordinary"),
381
+ fencingToken: flag(rest, "fencing-token", environment.BUILDCHAIN_DEV_DELIVERY_FENCING_TOKEN),
382
+ leaseGeneration: flag(rest, "lease-generation", environment.BUILDCHAIN_DEV_DELIVERY_LEASE_GENERATION),
383
+ leaseSeconds: flag(rest, "lease-seconds", environment.BUILDCHAIN_DEV_DELIVERY_LEASE_SECONDS),
384
+ outcome: flag(rest, "outcome", environment.BUILDCHAIN_DEV_DELIVERY_OUTCOME),
385
+ eventAction: flag(rest, "event-action", environment.BUILDCHAIN_DEV_DELIVERY_EVENT_ACTION),
386
+ evidenceRoot: flag(rest, "evidence-root", environment.BUILDCHAIN_DEV_DELIVERY_EVIDENCE_ROOT),
387
+ reason: flag(rest, "reason", environment.BUILDCHAIN_DEV_DELIVERY_REASON),
388
+ now: flag(rest, "now", environment.BUILDCHAIN_DEV_DELIVERY_NOW),
389
+ outputPath: flag(rest, "output", environment.BUILDCHAIN_DEV_DELIVERY_OUTPUT || ".buildchain/dev-delivery/result.json"),
390
+ execute: hasFlag(rest, "execute"),
391
+ json: hasFlag(rest, "json"),
392
+ };
393
+ }
394
+
395
+ function usage() {
396
+ return "Usage:\n buildchain dev warrant <submit|select|heartbeat|recover|close|settle|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n";
397
+ }
398
+
399
+ async function main() {
400
+ const args = process.argv.slice(2);
401
+ if (args.length === 0 || hasFlag(args, "help")) {
402
+ process.stdout.write(usage());
403
+ return;
404
+ }
405
+ const options = devDeliveryCliOptions(args);
406
+ if (!["submit", "select", "heartbeat", "recover", "close", "settle", "cancel-queued", "observe"].includes(options.command)) {
407
+ throw new Error(usage().trim());
408
+ }
409
+ const result = await runDevDeliveryCommand(options);
410
+ fs.mkdirSync(path.dirname(options.outputPath), { recursive: true });
411
+ fs.writeFileSync(options.outputPath, `${JSON.stringify(result, null, 2)}\n`);
412
+ if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
413
+ else {
414
+ process.stdout.write(`Buildchain dev delivery ${options.command}: ${result.receipt?.reason || result.mode}\n`);
415
+ process.stdout.write(`State root: ${result.after?.stateRoot || result.observation.stateRoot}\n`);
416
+ if (result.receiptRoot) process.stdout.write(`Receipt root: ${result.receiptRoot}\n`);
417
+ process.stdout.write(`Result: ${options.outputPath}\n`);
418
+ }
419
+ }
420
+
421
+ if (import.meta.url === `file://${process.argv[1]}`) {
422
+ main().catch((error) => {
423
+ console.error(`buildchain dev warrant: ${error.message}`);
424
+ process.exit(1);
425
+ });
426
+ }