githolon 0.15.0 → 0.16.1
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/cli.mjs +388 -465
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -190,7 +190,7 @@ async function wsCreate(name, opts) {
|
|
|
190
190
|
out(` session saved \u2192 ${credsPath()} (auto-refreshed; births ride x-nomos-auth)`);
|
|
191
191
|
principal = s.accessToken;
|
|
192
192
|
}
|
|
193
|
-
const r = await fetch(`${cloud}/
|
|
193
|
+
const r = await fetch(`${cloud}/v2/workspaces/${name}`, {
|
|
194
194
|
method: "POST",
|
|
195
195
|
headers: principalHeaders(principal)
|
|
196
196
|
});
|
|
@@ -214,7 +214,7 @@ async function wsCreate(name, opts) {
|
|
|
214
214
|
}
|
|
215
215
|
async function wsStatus(name, opts) {
|
|
216
216
|
const cloud = cloudBase(opts.cloud);
|
|
217
|
-
const r = await fetch(`${cloud}/
|
|
217
|
+
const r = await fetch(`${cloud}/v2/workspaces/${name}`);
|
|
218
218
|
const text = await r.text();
|
|
219
219
|
if (!r.ok) {
|
|
220
220
|
err(`status '${name}' (${r.status}): ${text}`);
|
|
@@ -233,7 +233,7 @@ async function wsRetire(ws, opts) {
|
|
|
233
233
|
);
|
|
234
234
|
return 1;
|
|
235
235
|
}
|
|
236
|
-
const r = await fetch(`${cloud}/
|
|
236
|
+
const r = await fetch(`${cloud}/v2/workspaces/${ws}/retire`, {
|
|
237
237
|
method: "POST",
|
|
238
238
|
headers: { authorization: `Bearer ${secret}` }
|
|
239
239
|
});
|
|
@@ -249,7 +249,7 @@ async function wsRetire(ws, opts) {
|
|
|
249
249
|
out(`\u2713 workspace ${ws} retired on ${cloud}${d.alreadyRetired === true ? " (it was already retired)" : ""}`);
|
|
250
250
|
out(" nothing was deleted \u2014 the ledger and its data are retained per the Nomos Pre-Release License");
|
|
251
251
|
out(" it no longer counts toward your workspace allowance; reads keep serving, deploys/pushes refuse");
|
|
252
|
-
out(" to un-retire: contact the operator (an admin restores the record via POST /
|
|
252
|
+
out(" to un-retire: contact the operator (an admin restores the record via root's governance offer, POST /v2/workspaces/root/createWorkspace)");
|
|
253
253
|
return 0;
|
|
254
254
|
}
|
|
255
255
|
async function deploy(ws, opts) {
|
|
@@ -270,8 +270,8 @@ async function deploy(ws, opts) {
|
|
|
270
270
|
);
|
|
271
271
|
return 1;
|
|
272
272
|
}
|
|
273
|
-
const before = await fetch(`${cloud}/
|
|
274
|
-
const r = await fetch(`${cloud}/
|
|
273
|
+
const before = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((br) => br.json()).then((bd) => bd.ok === true ? bd.domains ?? [] : []).catch(() => []);
|
|
274
|
+
const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`, {
|
|
275
275
|
method: "POST",
|
|
276
276
|
headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
|
|
277
277
|
body: readFileSync(file, "utf8")
|
|
@@ -310,7 +310,7 @@ async function secretRotate(ws, opts) {
|
|
|
310
310
|
err(`no stored secret for ${ws} on ${cloud} \u2014 githolon secret set ${ws} <secret> first`);
|
|
311
311
|
return 1;
|
|
312
312
|
}
|
|
313
|
-
const r = await fetch(`${cloud}/
|
|
313
|
+
const r = await fetch(`${cloud}/v2/workspaces/${ws}/rotate-secret`, {
|
|
314
314
|
method: "POST",
|
|
315
315
|
headers: { authorization: `Bearer ${current}` }
|
|
316
316
|
});
|
|
@@ -449,30 +449,182 @@ var init_tree = __esm({
|
|
|
449
449
|
}
|
|
450
450
|
});
|
|
451
451
|
|
|
452
|
+
// vendor/engine/custody-transport.mjs
|
|
453
|
+
function concatBytes(chunks) {
|
|
454
|
+
let n = 0;
|
|
455
|
+
for (const c of chunks) n += c.length;
|
|
456
|
+
const out9 = new Uint8Array(n);
|
|
457
|
+
let o = 0;
|
|
458
|
+
for (const c of chunks) {
|
|
459
|
+
out9.set(c, o);
|
|
460
|
+
o += c.length;
|
|
461
|
+
}
|
|
462
|
+
return out9;
|
|
463
|
+
}
|
|
464
|
+
function pkt(payload) {
|
|
465
|
+
const body = typeof payload === "string" ? enc2.encode(payload) : payload;
|
|
466
|
+
const len = (body.length + 4).toString(16).padStart(4, "0");
|
|
467
|
+
return concatBytes([enc2.encode(len), body]);
|
|
468
|
+
}
|
|
469
|
+
function readPktLines(buf, off = 0) {
|
|
470
|
+
const lines = [];
|
|
471
|
+
while (off + 4 <= buf.length) {
|
|
472
|
+
const hex = dec2.decode(buf.subarray(off, off + 4));
|
|
473
|
+
if (!/^[0-9a-f]{4}$/.test(hex)) break;
|
|
474
|
+
const len = parseInt(hex, 16);
|
|
475
|
+
if (len === 0) {
|
|
476
|
+
off += 4;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
if (len < 4 || off + len > buf.length) break;
|
|
480
|
+
lines.push(buf.subarray(off + 4, off + len));
|
|
481
|
+
off += len;
|
|
482
|
+
}
|
|
483
|
+
return { lines, rest: off };
|
|
484
|
+
}
|
|
485
|
+
async function lsRefs(remote, headers = {}, { service = "git-upload-pack", prefix = null } = {}) {
|
|
486
|
+
const r = await fetch(`${remote}/info/refs?service=${service}`, { headers });
|
|
487
|
+
if (!r.ok) {
|
|
488
|
+
if (r.status === 404) return {};
|
|
489
|
+
throw new Error(`info/refs ${r.status}: ${(await r.text()).slice(0, 180)}`);
|
|
490
|
+
}
|
|
491
|
+
const buf = new Uint8Array(await r.arrayBuffer());
|
|
492
|
+
const banner = readPktLines(buf, 0);
|
|
493
|
+
const { lines } = readPktLines(buf, banner.rest);
|
|
494
|
+
const refs = {};
|
|
495
|
+
for (const line of lines) {
|
|
496
|
+
let s = dec2.decode(line).replace(/\n$/, "");
|
|
497
|
+
if (s.startsWith("#")) continue;
|
|
498
|
+
const nul = s.indexOf("\0");
|
|
499
|
+
if (nul >= 0) s = s.slice(0, nul);
|
|
500
|
+
const sp = s.indexOf(" ");
|
|
501
|
+
if (sp !== 40) continue;
|
|
502
|
+
const oid = s.slice(0, 40), name = s.slice(41);
|
|
503
|
+
if (!/^[0-9a-f]{40}$/.test(oid)) continue;
|
|
504
|
+
if (prefix && !name.startsWith(prefix)) continue;
|
|
505
|
+
refs[name] = oid;
|
|
506
|
+
}
|
|
507
|
+
return refs;
|
|
508
|
+
}
|
|
509
|
+
async function fetchPack(remote, headers = {}, { wants = [], haves = [] } = {}) {
|
|
510
|
+
wants = wants.filter((w) => /^[0-9a-f]{40}$/.test(w) && w !== ZERO_OID);
|
|
511
|
+
if (!wants.length) return null;
|
|
512
|
+
const caps = "thin-pack ofs-delta agent=nomos-cloud";
|
|
513
|
+
const parts = [];
|
|
514
|
+
wants.forEach((w, i) => parts.push(pkt(`want ${w}${i === 0 ? " " + caps : ""}
|
|
515
|
+
`)));
|
|
516
|
+
parts.push(FLUSH);
|
|
517
|
+
for (const h of haves) if (/^[0-9a-f]{40}$/.test(h) && h !== ZERO_OID) parts.push(pkt(`have ${h}
|
|
518
|
+
`));
|
|
519
|
+
parts.push(pkt("done\n"));
|
|
520
|
+
const r = await fetch(`${remote}/git-upload-pack`, {
|
|
521
|
+
method: "POST",
|
|
522
|
+
headers: { "content-type": "application/x-git-upload-pack-request", "accept": "application/x-git-upload-pack-result", ...headers },
|
|
523
|
+
body: concatBytes(parts)
|
|
524
|
+
});
|
|
525
|
+
if (!r.ok) throw new Error(`upload-pack ${r.status}: ${(await r.text()).slice(0, 180)}`);
|
|
526
|
+
const buf = new Uint8Array(await r.arrayBuffer());
|
|
527
|
+
const { lines, rest } = readPktLines(buf, 0);
|
|
528
|
+
const packChunks = [];
|
|
529
|
+
let sideband = false;
|
|
530
|
+
for (const line of lines) {
|
|
531
|
+
if (line.length === 0) continue;
|
|
532
|
+
const head = dec2.decode(line.subarray(0, Math.min(line.length, 3)));
|
|
533
|
+
if (head === "NAK" || head === "ACK" || head === "ERR" || head.startsWith("sha")) continue;
|
|
534
|
+
const band = line[0];
|
|
535
|
+
if (band === 1) {
|
|
536
|
+
packChunks.push(line.subarray(1));
|
|
537
|
+
sideband = true;
|
|
538
|
+
} else if (band === 2) {
|
|
539
|
+
} else if (band === 3) {
|
|
540
|
+
throw new Error("upload-pack fatal: " + dec2.decode(line.subarray(1)).slice(0, 180));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const pack = sideband ? concatBytes(packChunks) : buf.subarray(rest);
|
|
544
|
+
if (pack.length < 12 || dec2.decode(pack.subarray(0, 4)) !== "PACK") {
|
|
545
|
+
throw new Error(`upload-pack: no pack in response (${pack.length}B, sideband=${sideband})`);
|
|
546
|
+
}
|
|
547
|
+
return pack;
|
|
548
|
+
}
|
|
549
|
+
var enc2, dec2, ZERO_OID, EMPTY_PACK, FLUSH;
|
|
550
|
+
var init_custody_transport = __esm({
|
|
551
|
+
"vendor/engine/custody-transport.mjs"() {
|
|
552
|
+
"use strict";
|
|
553
|
+
enc2 = new TextEncoder();
|
|
554
|
+
dec2 = new TextDecoder();
|
|
555
|
+
ZERO_OID = "0".repeat(40);
|
|
556
|
+
EMPTY_PACK = new Uint8Array([
|
|
557
|
+
80,
|
|
558
|
+
65,
|
|
559
|
+
67,
|
|
560
|
+
75,
|
|
561
|
+
0,
|
|
562
|
+
0,
|
|
563
|
+
0,
|
|
564
|
+
2,
|
|
565
|
+
0,
|
|
566
|
+
0,
|
|
567
|
+
0,
|
|
568
|
+
0,
|
|
569
|
+
// "PACK", ver 2, 0 objects
|
|
570
|
+
2,
|
|
571
|
+
157,
|
|
572
|
+
8,
|
|
573
|
+
130,
|
|
574
|
+
59,
|
|
575
|
+
216,
|
|
576
|
+
168,
|
|
577
|
+
234,
|
|
578
|
+
181,
|
|
579
|
+
16,
|
|
580
|
+
173,
|
|
581
|
+
106,
|
|
582
|
+
// sha1(the 12 header bytes)
|
|
583
|
+
199,
|
|
584
|
+
92,
|
|
585
|
+
130,
|
|
586
|
+
60,
|
|
587
|
+
253,
|
|
588
|
+
62,
|
|
589
|
+
211,
|
|
590
|
+
30
|
|
591
|
+
]);
|
|
592
|
+
FLUSH = enc2.encode("0000");
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
|
|
452
596
|
// vendor/engine/engine.mjs
|
|
453
597
|
import { WASI, File, Directory, OpenFile, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
|
|
454
|
-
|
|
455
|
-
|
|
598
|
+
function b64Bytes(bytes) {
|
|
599
|
+
let s = "";
|
|
600
|
+
for (let i = 0; i < bytes.length; i += 32768) {
|
|
601
|
+
s += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
602
|
+
}
|
|
603
|
+
return btoa(s);
|
|
604
|
+
}
|
|
605
|
+
function b64Json(o) {
|
|
606
|
+
return b64Bytes(enc3.encode(JSON.stringify(o)));
|
|
607
|
+
}
|
|
456
608
|
async function sha256hex(t) {
|
|
457
|
-
const b = await crypto.subtle.digest("SHA-256",
|
|
609
|
+
const b = await crypto.subtle.digest("SHA-256", enc3.encode(t));
|
|
458
610
|
return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
459
611
|
}
|
|
460
612
|
function osEntropyBuffer(n) {
|
|
461
613
|
const bytes = new Uint8Array(n * 8);
|
|
462
614
|
for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
|
|
463
|
-
const
|
|
615
|
+
const out9 = new Array(n), T = 2 ** 53;
|
|
464
616
|
for (let i = 0; i < n; i++) {
|
|
465
617
|
let z = 0n;
|
|
466
618
|
for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
|
|
467
|
-
|
|
619
|
+
out9[i] = Number(z >> 11n) / T;
|
|
468
620
|
}
|
|
469
|
-
return
|
|
621
|
+
return out9;
|
|
470
622
|
}
|
|
471
623
|
function stringifyBig(o) {
|
|
472
624
|
return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
|
|
473
625
|
}
|
|
474
626
|
function call(ex, mode, fields, STDERR) {
|
|
475
|
-
const req =
|
|
627
|
+
const req = enc3.encode(JSON.stringify({ mode, ...fields }));
|
|
476
628
|
const reqPtr = ex.git_holon_alloc(req.length);
|
|
477
629
|
try {
|
|
478
630
|
new Uint8Array(ex.memory.buffer, reqPtr, req.length).set(req);
|
|
@@ -485,7 +637,7 @@ function call(ex, mode, fields, STDERR) {
|
|
|
485
637
|
}
|
|
486
638
|
const { ptr, len } = unpack(packed);
|
|
487
639
|
try {
|
|
488
|
-
const e = JSON.parse(
|
|
640
|
+
const e = JSON.parse(dec3.decode(new Uint8Array(ex.memory.buffer, ptr, len).slice()));
|
|
489
641
|
if (!e.ok) throw new Error((e.error || "holon error") + (STDERR.length ? ` | ${STDERR.slice(-4).join(" / ")}` : ""));
|
|
490
642
|
return e.result;
|
|
491
643
|
} finally {
|
|
@@ -498,7 +650,7 @@ function call(ex, mode, fields, STDERR) {
|
|
|
498
650
|
async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, replica, installedBy = "nomos-cloud" }) {
|
|
499
651
|
const hashes = { bootstrap: await sha256hex(bootstrapPkg), nomos: await sha256hex(nomosPkg) };
|
|
500
652
|
const root = /* @__PURE__ */ new Map();
|
|
501
|
-
root.set("domain.package.usda", new File(
|
|
653
|
+
root.set("domain.package.usda", new File(enc3.encode(bootstrapPkg)));
|
|
502
654
|
root.set("ws", new Directory(/* @__PURE__ */ new Map()));
|
|
503
655
|
const preopen = new PreopenDirectory("/work", root);
|
|
504
656
|
const STDERR = [];
|
|
@@ -535,39 +687,18 @@ async function mountWorkspace(eng, ws, ledger) {
|
|
|
535
687
|
if (eng.mounted.has(ws)) return eng.mounted.get(ws);
|
|
536
688
|
stageWorkspaceDir(eng, ws);
|
|
537
689
|
const gitdir = gitdirOf(ws);
|
|
538
|
-
let restored = false, remoteMain = null, restoreError = null
|
|
690
|
+
let restored = false, remoteMain = null, restoreError = null;
|
|
539
691
|
try {
|
|
540
|
-
|
|
541
|
-
remoteMain =
|
|
692
|
+
await custodySkeleton(eng, gitdir);
|
|
693
|
+
remoteMain = await custodyFetch(eng, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
542
694
|
if (remoteMain) {
|
|
543
|
-
|
|
544
|
-
await git.addRemote({ fs: eng.fs, gitdir, remote: "origin", url: ledger.remote, force: true });
|
|
545
|
-
const ckpt = await discoverCheckpointFromCustody(ledger).catch(() => null);
|
|
546
|
-
let shallowOk = false;
|
|
547
|
-
if (ckpt?.cursor && ckpt.frontier) {
|
|
548
|
-
const steps = [32, 96, 256, 1024];
|
|
549
|
-
for (let i = 0; i < steps.length; i++) {
|
|
550
|
-
const depth = steps[i], relative = i > 0;
|
|
551
|
-
await git.fetch({ fs: eng.fs, http, gitdir, remote: "origin", ref: "main", singleBranch: true, depth, relative, headers: ledger.headers });
|
|
552
|
-
if (await git.readCommit({ fs: eng.fs, gitdir, oid: ckpt.cursor }).then(() => true, () => false)) {
|
|
553
|
-
shallowOk = true;
|
|
554
|
-
break;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
if (!shallowOk) await git.fetch({ fs: eng.fs, http, gitdir, remote: "origin", ref: "main", singleBranch: true, headers: ledger.headers });
|
|
559
|
-
await git.writeRef({ fs: eng.fs, gitdir, ref: "refs/heads/main", value: remoteMain, force: true });
|
|
560
|
-
await git.writeRef({ fs: eng.fs, gitdir, ref: "HEAD", value: "ref: refs/heads/main", force: true, symbolic: true });
|
|
561
|
-
if (shallowOk && ckpt?.blob) {
|
|
562
|
-
const r = importCheckpoint(eng, ws, ckpt.blob, ckpt.frontier);
|
|
563
|
-
checkpoint = { restored: !!(r && r.restored), frontierSeeded: !!(r && r.frontierSeeded), cursor: r && r.cursor ? r.cursor : ckpt.cursor, ref: ckpt.ref };
|
|
564
|
-
}
|
|
695
|
+
kgit.writeRef(eng, gitdir, "HEAD", "ref: refs/heads/main");
|
|
565
696
|
restored = true;
|
|
566
697
|
}
|
|
567
698
|
} catch (e) {
|
|
568
699
|
restoreError = String(e && e.stack || e).split("\n").slice(0, 5).join(" | ");
|
|
569
700
|
}
|
|
570
|
-
const m = { restored, remoteMain, restoreError
|
|
701
|
+
const m = { restored, remoteMain, restoreError };
|
|
571
702
|
eng.mounted.set(ws, m);
|
|
572
703
|
return m;
|
|
573
704
|
}
|
|
@@ -586,108 +717,120 @@ function mintId(eng, typeTag) {
|
|
|
586
717
|
function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}) {
|
|
587
718
|
const seq = eng.seq++;
|
|
588
719
|
const envelope = { payload: { domain, directiveId, payload }, captured_ports: { clock: { physical: Date.now(), logical: 0, replica: eng.replica }, rng: osEntropyBuffer(opts.rngDraws ?? 64) }, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
|
|
589
|
-
writeWork(eng, `payload-${seq}.json`,
|
|
590
|
-
writeWork(eng, `envelope-${seq}.json`,
|
|
720
|
+
writeWork(eng, `payload-${seq}.json`, enc3.encode(JSON.stringify(payload)));
|
|
721
|
+
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
591
722
|
const genesis = !controllerHash;
|
|
592
723
|
const defer = opts.deferProjection !== false;
|
|
593
|
-
const
|
|
724
|
+
const v = JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...opts.authorSecret ? { authorSecret: opts.authorSecret } : {}, ...opts.dryRun ? { dryRun: true } : defer ? { deferProjection: true } : {} }, eng.STDERR));
|
|
725
|
+
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
594
726
|
if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
595
727
|
return res;
|
|
596
728
|
}
|
|
597
|
-
function
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
async function streamAll(stream) {
|
|
601
|
-
const chunks = [];
|
|
602
|
-
const reader = stream.getReader();
|
|
603
|
-
for (; ; ) {
|
|
604
|
-
const { done, value } = await reader.read();
|
|
605
|
-
if (done) break;
|
|
606
|
-
chunks.push(value);
|
|
607
|
-
}
|
|
608
|
-
let len = 0;
|
|
609
|
-
for (const c of chunks) len += c.length;
|
|
610
|
-
const out10 = new Uint8Array(len);
|
|
611
|
-
let at = 0;
|
|
612
|
-
for (const c of chunks) {
|
|
613
|
-
out10.set(c, at);
|
|
614
|
-
at += c.length;
|
|
615
|
-
}
|
|
616
|
-
return out10;
|
|
617
|
-
}
|
|
618
|
-
async function gunzip(bytes) {
|
|
619
|
-
const ds = new DecompressionStream("gzip");
|
|
620
|
-
return streamAll(new Response(bytes).body.pipeThrough(ds));
|
|
621
|
-
}
|
|
622
|
-
async function discoverCheckpointFromCustody(ledger) {
|
|
623
|
-
const freshHeaders = () => ({ ...ledger.headers || {} });
|
|
624
|
-
const refs = (await git.listServerRefs({ http, url: ledger.remote, headers: freshHeaders(), prefix: "refs/meta/checkpoint/", protocolVersion: 2 }) || []).filter((r) => r.ref && r.ref.startsWith("refs/meta/checkpoint/") && r.oid);
|
|
625
|
-
if (!refs.length) return null;
|
|
626
|
-
const root = /* @__PURE__ */ new Map();
|
|
627
|
-
root.set("ckpt", new Directory(/* @__PURE__ */ new Map()));
|
|
628
|
-
const preopen = new PreopenDirectory("/work", root);
|
|
629
|
-
const fs = makeGitFs(preopen, "/work", { File, Directory });
|
|
630
|
-
const gitdir = "/work/ckpt/nomos.git";
|
|
631
|
-
await git.init({ fs, gitdir, bare: true, defaultBranch: BRANCH });
|
|
632
|
-
await git.addRemote({ fs, gitdir, remote: "origin", url: ledger.remote, force: true });
|
|
633
|
-
const readFile = async (oid, filepath) => {
|
|
634
|
-
try {
|
|
635
|
-
return (await git.readBlob({ fs, gitdir, oid, filepath })).blob;
|
|
636
|
-
} catch {
|
|
637
|
-
return null;
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
for (let i = refs.length - 1; i >= 0 && i >= refs.length - 3; i--) {
|
|
641
|
-
const { ref, oid } = refs[i];
|
|
729
|
+
async function custodySkeleton(eng, gitdir) {
|
|
730
|
+
const p = eng.fs.promises;
|
|
731
|
+
for (const d of ["", "/objects", "/objects/pack", "/objects/info", "/refs", "/refs/heads"]) {
|
|
642
732
|
try {
|
|
643
|
-
await
|
|
644
|
-
|
|
645
|
-
if (
|
|
646
|
-
let meta = {};
|
|
647
|
-
try {
|
|
648
|
-
const mb = await readFile(oid, "meta");
|
|
649
|
-
if (mb) meta = JSON.parse(dec2.decode(mb));
|
|
650
|
-
} catch {
|
|
651
|
-
}
|
|
652
|
-
const codec = meta.codec ?? "gzip";
|
|
653
|
-
const blob = dec2.decode(codec === "gzip" ? await gunzip(gz) : gz);
|
|
654
|
-
const fgz = await readFile(oid, "frontier");
|
|
655
|
-
const frontier = fgz ? dec2.decode(codec === "gzip" ? await gunzip(fgz) : fgz) : null;
|
|
656
|
-
return { ref, oid, cursor: meta.cursor || null, blob, frontier };
|
|
657
|
-
} catch {
|
|
733
|
+
await p.mkdir(`${gitdir}${d}`);
|
|
734
|
+
} catch (e) {
|
|
735
|
+
if (e && e.code !== "EEXIST") throw e;
|
|
658
736
|
}
|
|
659
737
|
}
|
|
660
|
-
|
|
738
|
+
try {
|
|
739
|
+
await p.writeFile(`${gitdir}/HEAD`, `ref: refs/heads/${BRANCH}
|
|
740
|
+
`);
|
|
741
|
+
} catch {
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
await p.writeFile(`${gitdir}/config`, "[core]\n repositoryformatversion = 0\n bare = true\n");
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
661
747
|
}
|
|
662
|
-
function
|
|
663
|
-
const
|
|
748
|
+
async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", localRef = null, have = null } = {}) {
|
|
749
|
+
const remoteRefFull = fullRef(remoteRef);
|
|
750
|
+
const refs = await custodyLsRefs(ledger, remoteRefFull);
|
|
751
|
+
const tip = refs[remoteRefFull] || null;
|
|
752
|
+
if (!tip) return null;
|
|
753
|
+
if (tip !== have) applyPackInto(eng, ws, await fetchPack(ledger.remote, ledger.headers || {}, { wants: [tip], haves: have ? [have] : [] }));
|
|
754
|
+
kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
|
|
755
|
+
return tip;
|
|
756
|
+
}
|
|
757
|
+
function offerIntent(eng, ws, bytes, opts) {
|
|
758
|
+
const name = `offer-in-${eng.seq++}.json`;
|
|
664
759
|
writeWork(eng, name, bytes);
|
|
665
|
-
|
|
760
|
+
const deferProjection = !!(opts && opts.defer);
|
|
761
|
+
const branch = opts && opts.branch || BRANCH;
|
|
762
|
+
return JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, intentFile: `/work/${name}`, branch, deferProjection }, eng.STDERR));
|
|
763
|
+
}
|
|
764
|
+
function offerAdmit(eng, ws, bytes, opts) {
|
|
765
|
+
const v = offerIntent(eng, ws, bytes, opts);
|
|
766
|
+
return v.outcome === "admitted" ? { ok: true, head: v.head } : { ok: false, error: v.verdict?.reason ?? v.error ?? "the offer was refused" };
|
|
767
|
+
}
|
|
768
|
+
function applyIntentBytes(eng, ws, bytes, opts) {
|
|
769
|
+
return offerAdmit(eng, ws, bytes, opts);
|
|
666
770
|
}
|
|
667
771
|
function verifyChainLocal(eng, ws) {
|
|
668
|
-
return JSON.parse(call(eng.ex, "
|
|
772
|
+
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng.STDERR));
|
|
669
773
|
}
|
|
670
|
-
var
|
|
774
|
+
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, cryptoUnwrapKey, count, sum;
|
|
671
775
|
var init_engine = __esm({
|
|
672
776
|
"vendor/engine/engine.mjs"() {
|
|
673
777
|
"use strict";
|
|
674
778
|
init_git_fs();
|
|
675
779
|
init_tree();
|
|
676
|
-
|
|
677
|
-
|
|
780
|
+
init_custody_transport();
|
|
781
|
+
enc3 = new TextEncoder();
|
|
782
|
+
dec3 = new TextDecoder();
|
|
678
783
|
BRANCH = "main";
|
|
784
|
+
RESULT_PACK_INLINE_MAX = 256 * 1024;
|
|
679
785
|
repoArgOf = (ws) => `/work/ws/${ws}`;
|
|
680
786
|
gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
|
|
681
787
|
unpack = (p) => {
|
|
682
788
|
const v = typeof p === "bigint" ? p : BigInt(p);
|
|
683
789
|
return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
|
|
684
790
|
};
|
|
791
|
+
repoArgOfGitdir = (gitdir) => gitdir.replace(/\/nomos\.git$/, "");
|
|
792
|
+
bytesFromB64 = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
793
|
+
kgit = {
|
|
794
|
+
// resolveRef → tip oid hex, or null if the ref is absent (replaces git.resolveRef(...).catch(()=>null)).
|
|
795
|
+
resolveRef: (eng, gitdir, ref) => JSON.parse(call(eng.ex, "resolve_ref", { repoArg: repoArgOfGitdir(gitdir), ref }, eng.STDERR)).oid,
|
|
796
|
+
// readBlob → the blob bytes at `path` inside commit `oid`, or null if not found (no throw).
|
|
797
|
+
readBlob: (eng, gitdir, oid, path) => {
|
|
798
|
+
const r = JSON.parse(call(eng.ex, "read_blob", { repoArg: repoArgOfGitdir(gitdir), oid, path }, eng.STDERR));
|
|
799
|
+
return r.ok ? bytesFromB64(r.blobB64) : null;
|
|
800
|
+
},
|
|
801
|
+
// intentsAbove → [{ oid, intentB64 }] oldest→newest over (base, head] (replaces log + readBlob intent.json).
|
|
802
|
+
// `limit` (0 = all) caps to the most-recent N intent commits (replaces git.log depth).
|
|
803
|
+
intentsAbove: (eng, gitdir, head, base = "", limit = 0) => JSON.parse(call(eng.ex, "intents_above", { repoArg: repoArgOfGitdir(gitdir), head, base, limit }, eng.STDERR)).intents,
|
|
804
|
+
// listRefs → [{ ref, oid }] under prefix (replaces git.listRefs for LOCAL refs).
|
|
805
|
+
listRefs: (eng, gitdir, prefix) => JSON.parse(call(eng.ex, "list_refs", { repoArg: repoArgOfGitdir(gitdir), prefix }, eng.STDERR)).refs,
|
|
806
|
+
// writeRef → force-point a ref (plain oid or `ref:`-symbolic) (replaces git.writeRef).
|
|
807
|
+
writeRef: (eng, gitdir, ref, value) => JSON.parse(call(eng.ex, "write_ref", { repoArg: repoArgOfGitdir(gitdir), ref, value }, eng.STDERR)),
|
|
808
|
+
// deleteRef → remove a local ref, idempotent (replaces git.deleteRef).
|
|
809
|
+
deleteRef: (eng, gitdir, ref) => JSON.parse(call(eng.ex, "delete_ref", { repoArg: repoArgOfGitdir(gitdir), ref }, eng.STDERR)),
|
|
810
|
+
// writeMetaCommit → blobs→tree→commit→ref for a CUSTODY-META fact (checkpoint/refused/outbox/shape).
|
|
811
|
+
// `files`: [{ path, bytes:Uint8Array }]; returns the commit oid (replaces writeBlob+writeTree+writeCommit[+writeRef]).
|
|
812
|
+
writeMetaCommit: (eng, gitdir, { parents = [], files, message = "nomos custody meta", timeSecs = 0, ref = "" }) => JSON.parse(call(eng.ex, "write_meta_commit", {
|
|
813
|
+
repoArg: repoArgOfGitdir(gitdir),
|
|
814
|
+
parents,
|
|
815
|
+
files: files.map((f) => ({ path: f.path, blobB64: b64Bytes(f.bytes) })),
|
|
816
|
+
message,
|
|
817
|
+
timeSecs,
|
|
818
|
+
ref
|
|
819
|
+
}, eng.STDERR)).oid
|
|
820
|
+
};
|
|
685
821
|
installPayload = (hash, usda, installedBy, dispositions) => ({ domainHash: hash, packageUsda: usda, installedBy, dependencies: [], finalizers: [], ...dispositions ? { dispositions } : {} });
|
|
686
|
-
|
|
687
|
-
|
|
822
|
+
fullRef = (r) => r === "HEAD" || r.startsWith("refs/") ? r : `refs/heads/${r}`;
|
|
823
|
+
applyPackInto = (eng, ws, pack) => {
|
|
824
|
+
if (!pack || !pack.length) return;
|
|
825
|
+
const r = JSON.parse(call(eng.ex, "apply_pack", { repoArg: repoArgOf(ws), packB64: b64Bytes(pack) }, eng.STDERR));
|
|
826
|
+
if (!r || r.ok !== true) throw new Error(`apply_pack: ${r && r.error}`);
|
|
827
|
+
};
|
|
828
|
+
custodyLsRefs = (ledger, prefix = null) => lsRefs(ledger.remote, ledger.headers || {}, { service: "git-upload-pack", prefix });
|
|
829
|
+
qById = (eng, ws, id, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "byId", aggregateId: id }), principal, keys, branch: BRANCH }, eng.STDERR));
|
|
830
|
+
query = (eng, ws, queryId, paramsJson, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ kind: "query", queryId, paramsJson }), principal, keys, branch: BRANCH }, eng.STDERR));
|
|
688
831
|
cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "crypto_unwrap_key", { secret, hpkeEpk, ct }, eng.STDERR)).scopeKey;
|
|
689
|
-
count = (eng, ws, countId, groupKey) => JSON.parse(call(eng.ex, "
|
|
690
|
-
sum = (eng, ws, sumId, groupKey) => JSON.parse(call(eng.ex, "
|
|
832
|
+
count = (eng, ws, countId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "count", countId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
|
|
833
|
+
sum = (eng, ws, sumId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "sum", sumId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
|
|
691
834
|
}
|
|
692
835
|
});
|
|
693
836
|
|
|
@@ -776,21 +919,21 @@ function sealedIntentOf(eng, ws, res) {
|
|
|
776
919
|
}
|
|
777
920
|
function printVerdict(v) {
|
|
778
921
|
if (v["valid"] === true) {
|
|
779
|
-
|
|
780
|
-
|
|
922
|
+
out2(`\u2713 chain VALID \u2014 head ${String(v["head"]).slice(0, 12)}\u2026, ${v["intents"]} intent(s), ${v["plansRerun"]} plan(s) re-run`);
|
|
923
|
+
out2(` controller ${String(v["controllerHash"]).slice(0, 12)}\u2026 | installed: ${v["installedDomains"]?.map((h) => h.slice(0, 12) + "\u2026").join(", ") ?? "\u2014"}`);
|
|
781
924
|
return true;
|
|
782
925
|
}
|
|
783
|
-
|
|
926
|
+
err2(`chain INVALID at ${v["check"]}${v["atIndex"] !== void 0 ? ` (intent ${v["atIndex"]})` : ""}: ${v["error"]}`);
|
|
784
927
|
return false;
|
|
785
928
|
}
|
|
786
|
-
var
|
|
929
|
+
var out2, err2;
|
|
787
930
|
var init_local_holon = __esm({
|
|
788
931
|
"src/local_holon.ts"() {
|
|
789
932
|
"use strict";
|
|
790
933
|
init_engine();
|
|
791
934
|
init_cloud();
|
|
792
|
-
|
|
793
|
-
|
|
935
|
+
out2 = (s) => void process.stdout.write(s + "\n");
|
|
936
|
+
err2 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
794
937
|
}
|
|
795
938
|
});
|
|
796
939
|
|
|
@@ -939,7 +1082,7 @@ var init_proof_offline = __esm({
|
|
|
939
1082
|
|
|
940
1083
|
// src/cli.ts
|
|
941
1084
|
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
942
|
-
import { spawnSync as
|
|
1085
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
943
1086
|
import { createRequire as createRequire2 } from "node:module";
|
|
944
1087
|
import { dirname as dirname5, join as join10, resolve as resolve2 } from "node:path";
|
|
945
1088
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
@@ -1122,171 +1265,11 @@ Re-run with --force to replace it.`
|
|
|
1122
1265
|
return { path, contents };
|
|
1123
1266
|
}
|
|
1124
1267
|
|
|
1125
|
-
// src/capability.ts
|
|
1126
|
-
init_cloud();
|
|
1127
|
-
import { spawnSync } from "node:child_process";
|
|
1128
|
-
var out2 = (s) => void process.stdout.write(s + "\n");
|
|
1129
|
-
var err2 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1130
|
-
var CAP_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
1131
|
-
function readStdin() {
|
|
1132
|
-
if (process.stdin.isTTY === true) {
|
|
1133
|
-
process.stderr.write(
|
|
1134
|
-
"paste the credential value, then Ctrl-D (it never rides argv \u2014 ps/history are hosts too):\n"
|
|
1135
|
-
);
|
|
1136
|
-
}
|
|
1137
|
-
return new Promise((resolveValue) => {
|
|
1138
|
-
let buf = "";
|
|
1139
|
-
process.stdin.setEncoding("utf8");
|
|
1140
|
-
process.stdin.on("data", (chunk) => {
|
|
1141
|
-
buf += chunk;
|
|
1142
|
-
});
|
|
1143
|
-
process.stdin.on("end", () => resolveValue(buf.trim()));
|
|
1144
|
-
process.stdin.resume();
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
function readGcloud(name) {
|
|
1148
|
-
const r = spawnSync("gcloud", ["secrets", "versions", "access", "latest", `--secret=${name}`], {
|
|
1149
|
-
encoding: "utf8",
|
|
1150
|
-
maxBuffer: 1024 * 1024
|
|
1151
|
-
});
|
|
1152
|
-
if (r.error !== void 0 || r.status !== 0) {
|
|
1153
|
-
throw new Error(
|
|
1154
|
-
`gcloud secret read failed (${r.error?.message ?? r.stderr?.trim() ?? `exit ${r.status}`}) \u2014 is gcloud installed + authed, and does secret '${name}' exist?`
|
|
1155
|
-
);
|
|
1156
|
-
}
|
|
1157
|
-
const v = r.stdout.trim();
|
|
1158
|
-
if (v === "") throw new Error(`gcloud secret '${name}' is empty`);
|
|
1159
|
-
return v;
|
|
1160
|
-
}
|
|
1161
|
-
function parseExpiresAt(s) {
|
|
1162
|
-
const n = /^\d+$/.test(s) ? Number(s) : Date.parse(s);
|
|
1163
|
-
if (!Number.isInteger(n) || n <= 0) throw new Error(`--expires-at '${s}' is neither epoch-ms nor an ISO date`);
|
|
1164
|
-
return n;
|
|
1165
|
-
}
|
|
1166
|
-
async function bindLane(lane, ws, capability, opts) {
|
|
1167
|
-
const cloud = cloudBase(opts.cloud);
|
|
1168
|
-
if (!CAP_NAME.test(capability)) {
|
|
1169
|
-
err2(`capability must be a lowercase law name matching [a-z0-9][a-z0-9._-]{0,63} \u2014 e.g. cf-analytics, openai`);
|
|
1170
|
-
return 1;
|
|
1171
|
-
}
|
|
1172
|
-
const secret = getSecret(cloud, ws);
|
|
1173
|
-
if (secret === void 0) {
|
|
1174
|
-
err2(
|
|
1175
|
-
`no stored secret for ${ws} on ${cloud} \u2014 capability ${lane} is owner-gated
|
|
1176
|
-
githolon secret set ${ws} <secret> (import the workspaceSecret you hold)`
|
|
1177
|
-
);
|
|
1178
|
-
return 1;
|
|
1179
|
-
}
|
|
1180
|
-
let value;
|
|
1181
|
-
try {
|
|
1182
|
-
value = opts.fromGcloud !== void 0 ? readGcloud(opts.fromGcloud) : await readStdin();
|
|
1183
|
-
} catch (e) {
|
|
1184
|
-
err2(e.message);
|
|
1185
|
-
return 1;
|
|
1186
|
-
}
|
|
1187
|
-
if (value === "") {
|
|
1188
|
-
err2("no credential value arrived \u2014 pipe it on stdin, or pass --from-gcloud <secret-name>");
|
|
1189
|
-
return 1;
|
|
1190
|
-
}
|
|
1191
|
-
let expiresAt;
|
|
1192
|
-
try {
|
|
1193
|
-
expiresAt = opts.expiresAt !== void 0 ? parseExpiresAt(opts.expiresAt) : void 0;
|
|
1194
|
-
} catch (e) {
|
|
1195
|
-
err2(e.message);
|
|
1196
|
-
return 1;
|
|
1197
|
-
}
|
|
1198
|
-
const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/${lane}`, {
|
|
1199
|
-
method: "POST",
|
|
1200
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
|
|
1201
|
-
body: JSON.stringify({
|
|
1202
|
-
capability,
|
|
1203
|
-
secret: value,
|
|
1204
|
-
...opts.scope !== void 0 ? { scope: opts.scope } : {},
|
|
1205
|
-
...expiresAt !== void 0 ? { expiresAt } : {}
|
|
1206
|
-
})
|
|
1207
|
-
});
|
|
1208
|
-
const d = await r.json().catch(() => void 0);
|
|
1209
|
-
if (r.status === 401) {
|
|
1210
|
-
err2(`${lane} refused (401) \u2014 the stored secret for ${ws} is not the workspace's. githolon secret set ${ws} <secret>`);
|
|
1211
|
-
return 1;
|
|
1212
|
-
}
|
|
1213
|
-
if (!r.ok || d?.ok !== true) {
|
|
1214
|
-
err2(`capability ${lane} '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
1215
|
-
return 1;
|
|
1216
|
-
}
|
|
1217
|
-
out2(`\u2713 ${capability} ${lane === "rotate" ? "rotated" : "bound"} on ${ws} (epoch ${d.epoch})`);
|
|
1218
|
-
out2(` the VALUE lives host-side only (the workspace's holon) \u2014 it never enters the ledger`);
|
|
1219
|
-
out2(` the law fact: keyHash ${(d.keyHash ?? "").slice(0, 16)}\u2026 status ${d.status}`);
|
|
1220
|
-
return 0;
|
|
1221
|
-
}
|
|
1222
|
-
async function capabilityBind(ws, capability, opts) {
|
|
1223
|
-
return bindLane("bind", ws, capability, opts);
|
|
1224
|
-
}
|
|
1225
|
-
async function capabilityRotate(ws, capability, opts) {
|
|
1226
|
-
return bindLane("rotate", ws, capability, opts);
|
|
1227
|
-
}
|
|
1228
|
-
async function capabilityList(ws, opts) {
|
|
1229
|
-
const cloud = cloudBase(opts.cloud);
|
|
1230
|
-
const secret = getSecret(cloud, ws);
|
|
1231
|
-
if (secret === void 0) {
|
|
1232
|
-
err2(`no stored secret for ${ws} on ${cloud} \u2014 the binding facts are owner-gated
|
|
1233
|
-
githolon secret set ${ws} <secret>`);
|
|
1234
|
-
return 1;
|
|
1235
|
-
}
|
|
1236
|
-
const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities`, {
|
|
1237
|
-
headers: { authorization: `Bearer ${secret}` }
|
|
1238
|
-
});
|
|
1239
|
-
const d = await r.json().catch(() => void 0);
|
|
1240
|
-
if (!r.ok || d?.ok !== true) {
|
|
1241
|
-
err2(`capability list '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
1242
|
-
return 1;
|
|
1243
|
-
}
|
|
1244
|
-
const bindings = d.bindings ?? [];
|
|
1245
|
-
if (bindings.length === 0) {
|
|
1246
|
-
out2(`no capability bindings on ${ws} \u2014 bind one: githolon capability bind ${ws} <capability>`);
|
|
1247
|
-
return 0;
|
|
1248
|
-
}
|
|
1249
|
-
for (const b of bindings) {
|
|
1250
|
-
const bits = [
|
|
1251
|
-
`epoch ${b.epoch}`,
|
|
1252
|
-
`${b.status}`,
|
|
1253
|
-
`keyHash ${(b.keyHash ?? "").slice(0, 16)}\u2026`,
|
|
1254
|
-
...b.scope !== void 0 ? [`scope ${b.scope}`] : [],
|
|
1255
|
-
...b.expiresAt !== void 0 ? [`expires ${new Date(b.expiresAt).toISOString()}`] : [],
|
|
1256
|
-
...b.held !== void 0 ? [b.held ? "value held" : "VALUE MISSING (rotate to restore)"] : []
|
|
1257
|
-
];
|
|
1258
|
-
out2(`${b.capability} ${bits.join(" \xB7 ")}`);
|
|
1259
|
-
}
|
|
1260
|
-
out2(`(facts about authority \u2014 the values live host-side and are never listed)`);
|
|
1261
|
-
return 0;
|
|
1262
|
-
}
|
|
1263
|
-
async function capabilityUnbind(ws, capability, opts) {
|
|
1264
|
-
const cloud = cloudBase(opts.cloud);
|
|
1265
|
-
const secret = getSecret(cloud, ws);
|
|
1266
|
-
if (secret === void 0) {
|
|
1267
|
-
err2(`no stored secret for ${ws} on ${cloud} \u2014 unbind is owner-gated
|
|
1268
|
-
githolon secret set ${ws} <secret>`);
|
|
1269
|
-
return 1;
|
|
1270
|
-
}
|
|
1271
|
-
const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/unbind`, {
|
|
1272
|
-
method: "POST",
|
|
1273
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
|
|
1274
|
-
body: JSON.stringify({ capability })
|
|
1275
|
-
});
|
|
1276
|
-
const d = await r.json().catch(() => void 0);
|
|
1277
|
-
if (!r.ok || d?.ok !== true) {
|
|
1278
|
-
err2(`capability unbind '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
1279
|
-
return 1;
|
|
1280
|
-
}
|
|
1281
|
-
out2(`\u2713 ${capability} unbound on ${ws} \u2014 the law fact is revoked (the record stays) and the host forgot the value`);
|
|
1282
|
-
return 0;
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
1268
|
// src/cli.ts
|
|
1286
1269
|
init_cloud();
|
|
1287
1270
|
|
|
1288
1271
|
// src/compile.ts
|
|
1289
|
-
import { spawn, spawnSync
|
|
1272
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
1290
1273
|
import { createRequire } from "node:module";
|
|
1291
1274
|
import { dirname, join as join3 } from "node:path";
|
|
1292
1275
|
import { pathToFileURL } from "node:url";
|
|
@@ -1310,7 +1293,7 @@ function runCompile(args) {
|
|
|
1310
1293
|
`);
|
|
1311
1294
|
return 1;
|
|
1312
1295
|
}
|
|
1313
|
-
const r =
|
|
1296
|
+
const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
|
|
1314
1297
|
return r.status ?? 1;
|
|
1315
1298
|
}
|
|
1316
1299
|
function compileAsync(cwd, args = []) {
|
|
@@ -1321,10 +1304,10 @@ function compileAsync(cwd, args = []) {
|
|
|
1321
1304
|
}
|
|
1322
1305
|
return new Promise((resolveRun) => {
|
|
1323
1306
|
const chunks = [];
|
|
1324
|
-
const
|
|
1307
|
+
const dec4 = new TextDecoder();
|
|
1325
1308
|
const child = spawn(process.execPath, [launcher, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
1326
|
-
child.stdout?.on("data", (c) => chunks.push(
|
|
1327
|
-
child.stderr?.on("data", (c) => chunks.push(
|
|
1309
|
+
child.stdout?.on("data", (c) => chunks.push(dec4.decode(c)));
|
|
1310
|
+
child.stderr?.on("data", (c) => chunks.push(dec4.decode(c)));
|
|
1328
1311
|
child.on("error", (e) => resolveRun({ ok: false, ms: performance.now() - t0, output: String(e) }));
|
|
1329
1312
|
child.on(
|
|
1330
1313
|
"close",
|
|
@@ -1429,8 +1412,8 @@ async function resolveWorkspace(explicit, cwd, target, usageHint) {
|
|
|
1429
1412
|
|
|
1430
1413
|
// src/dev.ts
|
|
1431
1414
|
init_proof_offline();
|
|
1432
|
-
var
|
|
1433
|
-
var
|
|
1415
|
+
var out3 = (s) => void process.stdout.write(s + "\n");
|
|
1416
|
+
var err3 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1434
1417
|
var WS = "dev";
|
|
1435
1418
|
var DEFAULT_PORT = 7700;
|
|
1436
1419
|
var DEBOUNCE_MS = 200;
|
|
@@ -1490,8 +1473,8 @@ function startServer(s) {
|
|
|
1490
1473
|
req.on("data", (c) => chunks.push(c));
|
|
1491
1474
|
req.on("end", () => {
|
|
1492
1475
|
try {
|
|
1493
|
-
const
|
|
1494
|
-
const body = chunks.map((c) =>
|
|
1476
|
+
const dec4 = new TextDecoder();
|
|
1477
|
+
const body = chunks.map((c) => dec4.decode(c)).join("") || "{}";
|
|
1495
1478
|
const r = author(s.eng, WS, decodeURIComponent(dispatch[1]), decodeURIComponent(dispatch[2]), JSON.parse(body), s.lawHash);
|
|
1496
1479
|
if (r["ok"] === false) return respond(422, { ok: false, error: r["error"] });
|
|
1497
1480
|
s.intents++;
|
|
@@ -1553,21 +1536,21 @@ async function dev(opts) {
|
|
|
1553
1536
|
const cwd = process.cwd();
|
|
1554
1537
|
const cloud = cloudBase(opts.cloud);
|
|
1555
1538
|
const project = await loadProject(cwd).catch((e) => {
|
|
1556
|
-
|
|
1539
|
+
err3(e.message);
|
|
1557
1540
|
return void 0;
|
|
1558
1541
|
});
|
|
1559
1542
|
if (project === void 0) {
|
|
1560
1543
|
if (!existsSync6(join7(cwd, CONFIG_FILE))) {
|
|
1561
|
-
|
|
1544
|
+
err3(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
|
|
1562
1545
|
}
|
|
1563
1546
|
return 1;
|
|
1564
1547
|
}
|
|
1565
1548
|
const creds = loadCreds();
|
|
1566
1549
|
const installedBy = creds.session?.uid ?? creds.principal ?? "githolon-dev";
|
|
1567
|
-
|
|
1550
|
+
out3(`githolon dev \u2014 ${project.name}: compiling\u2026`);
|
|
1568
1551
|
const firstCompile = await compileAsync(cwd);
|
|
1569
1552
|
if (!firstCompile.ok) {
|
|
1570
|
-
|
|
1553
|
+
err3(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
|
|
1571
1554
|
${firstCompile.output}`);
|
|
1572
1555
|
return 1;
|
|
1573
1556
|
}
|
|
@@ -1575,14 +1558,14 @@ ${firstCompile.output}`);
|
|
|
1575
1558
|
try {
|
|
1576
1559
|
deployBody = readDeployBody(cwd, project.name);
|
|
1577
1560
|
} catch (e) {
|
|
1578
|
-
|
|
1561
|
+
err3(e.message);
|
|
1579
1562
|
return 1;
|
|
1580
1563
|
}
|
|
1581
1564
|
let engBoot;
|
|
1582
1565
|
try {
|
|
1583
1566
|
engBoot = await holonEngine(cloud, deployBody, installedBy);
|
|
1584
1567
|
} catch (e) {
|
|
1585
|
-
|
|
1568
|
+
err3(`cannot boot the local holon: ${String(e.message ?? e)}
|
|
1586
1569
|
the runtime rides ${cloud}/v1/runtime/* and is cached in ~/.holon/runtime after one fetch \u2014 connect once, then dev runs offline`);
|
|
1587
1570
|
return 1;
|
|
1588
1571
|
}
|
|
@@ -1605,19 +1588,19 @@ ${firstCompile.output}`);
|
|
|
1605
1588
|
s.intents++;
|
|
1606
1589
|
const installed = await installLaw(s, deployBody, installedBy);
|
|
1607
1590
|
if (!installed.ok) {
|
|
1608
|
-
|
|
1591
|
+
err3(installed.error ?? "law install failed");
|
|
1609
1592
|
return 1;
|
|
1610
1593
|
}
|
|
1611
1594
|
s.proof = await runProofCycle(s, cwd, deployBody, installedBy);
|
|
1612
1595
|
if (opts.once === true) {
|
|
1613
|
-
|
|
1596
|
+
out3(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
|
|
1614
1597
|
return s.proof === void 0 || s.proof.ok ? 0 : 1;
|
|
1615
1598
|
}
|
|
1616
1599
|
let server;
|
|
1617
1600
|
try {
|
|
1618
1601
|
server = await startServer(s);
|
|
1619
1602
|
} catch (e) {
|
|
1620
|
-
|
|
1603
|
+
err3(e.message);
|
|
1621
1604
|
return 1;
|
|
1622
1605
|
}
|
|
1623
1606
|
const moduleDirs = /* @__PURE__ */ new Set();
|
|
@@ -1645,7 +1628,7 @@ ${firstCompile.output}`);
|
|
|
1645
1628
|
s.compileMs = run.ms;
|
|
1646
1629
|
if (!run.ok) {
|
|
1647
1630
|
s.lastError = run.output.trim() || "compile failed";
|
|
1648
|
-
|
|
1631
|
+
out3(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
|
|
1649
1632
|
${run.output}`);
|
|
1650
1633
|
} else {
|
|
1651
1634
|
s.lastError = void 0;
|
|
@@ -1654,16 +1637,16 @@ ${run.output}`);
|
|
|
1654
1637
|
const inst = await installLaw(s, body, installedBy);
|
|
1655
1638
|
if (!inst.ok) {
|
|
1656
1639
|
s.lastError = inst.error ?? "install failed";
|
|
1657
|
-
|
|
1640
|
+
out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
|
|
1658
1641
|
} else {
|
|
1659
1642
|
s.proof = await runProofCycle(s, cwd, body, installedBy);
|
|
1660
1643
|
const total = performance.now() - t0;
|
|
1661
|
-
|
|
1662
|
-
|
|
1644
|
+
out3(statusScreen(s));
|
|
1645
|
+
out3(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
|
|
1663
1646
|
}
|
|
1664
1647
|
} catch (e) {
|
|
1665
1648
|
s.lastError = String(e.message ?? e);
|
|
1666
|
-
|
|
1649
|
+
out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
|
|
1667
1650
|
}
|
|
1668
1651
|
}
|
|
1669
1652
|
cycling = false;
|
|
@@ -1689,8 +1672,8 @@ ${run.output}`);
|
|
|
1689
1672
|
})
|
|
1690
1673
|
);
|
|
1691
1674
|
s.watchRoots.push(CONFIG_FILE);
|
|
1692
|
-
|
|
1693
|
-
|
|
1675
|
+
out3(statusScreen(s));
|
|
1676
|
+
out3(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
|
|
1694
1677
|
return new Promise((resolveExit) => {
|
|
1695
1678
|
const stop = () => {
|
|
1696
1679
|
for (const w of watchers) w.close();
|
|
@@ -1704,13 +1687,13 @@ ${run.output}`);
|
|
|
1704
1687
|
|
|
1705
1688
|
// src/git.ts
|
|
1706
1689
|
init_cloud();
|
|
1707
|
-
import { spawnSync as
|
|
1690
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1708
1691
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1709
1692
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1710
|
-
var
|
|
1711
|
-
var
|
|
1712
|
-
function
|
|
1713
|
-
const r =
|
|
1693
|
+
var out4 = (s) => void process.stdout.write(s + "\n");
|
|
1694
|
+
var err4 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1695
|
+
function git(args, opts = {}) {
|
|
1696
|
+
const r = spawnSync2("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
|
|
1714
1697
|
return r.status ?? 1;
|
|
1715
1698
|
}
|
|
1716
1699
|
function ensureSecret(cloud, ws) {
|
|
@@ -1730,7 +1713,7 @@ function parseCredentialInput(input) {
|
|
|
1730
1713
|
return kv;
|
|
1731
1714
|
}
|
|
1732
1715
|
function workspaceFromPath(path) {
|
|
1733
|
-
const m = (path ?? "").match(/^
|
|
1716
|
+
const m = (path ?? "").match(/^v[12]\/workspaces\/([^/]+)\/git(\/|$)/);
|
|
1734
1717
|
return m?.[1];
|
|
1735
1718
|
}
|
|
1736
1719
|
async function gitCredential(action) {
|
|
@@ -1740,40 +1723,40 @@ async function gitCredential(action) {
|
|
|
1740
1723
|
if (kv["protocol"] !== "https" || ws === void 0) return 0;
|
|
1741
1724
|
const cloud = `https://${kv["host"]}`;
|
|
1742
1725
|
const token = await sessionToken().catch(() => void 0);
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1726
|
+
out4(`username=${token ?? "nomos"}`);
|
|
1727
|
+
out4(`password=${ensureSecret(cloud, ws)}`);
|
|
1728
|
+
out4("");
|
|
1746
1729
|
return 0;
|
|
1747
1730
|
}
|
|
1748
1731
|
async function gitSetup(opts) {
|
|
1749
1732
|
const cloud = cloudBase(opts.cloud);
|
|
1750
1733
|
const self = process.argv[1];
|
|
1751
1734
|
if (self === void 0) {
|
|
1752
|
-
|
|
1735
|
+
err4("cannot locate the githolon CLI entry to install as a credential helper");
|
|
1753
1736
|
return 1;
|
|
1754
1737
|
}
|
|
1755
1738
|
const helper = `!"${process.execPath}" "${self}" git-credential`;
|
|
1756
|
-
if (
|
|
1757
|
-
|
|
1739
|
+
if (git(["config", "--global", `credential.${cloud}.helper`, helper]) !== 0 || git(["config", "--global", `credential.${cloud}.useHttpPath`, "true"]) !== 0) {
|
|
1740
|
+
err4("git config failed \u2014 is git installed?");
|
|
1758
1741
|
return 1;
|
|
1759
1742
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1743
|
+
out4(`\u2713 git credential helper installed for ${cloud} (global gitconfig)`);
|
|
1744
|
+
out4(` pushes/clones of ${cloud}/v2/workspaces/<ws>/git now authenticate from ~/.holon`);
|
|
1762
1745
|
return 0;
|
|
1763
1746
|
}
|
|
1764
1747
|
async function gitRemote(ws, name, opts) {
|
|
1765
1748
|
const cloud = cloudBase(opts.cloud);
|
|
1766
|
-
if (
|
|
1767
|
-
|
|
1749
|
+
if (git(["rev-parse", "--git-dir"], { quiet: true }) !== 0) {
|
|
1750
|
+
err4("not a git repository \u2014 run inside your repo (git init first)");
|
|
1768
1751
|
return 1;
|
|
1769
1752
|
}
|
|
1770
1753
|
const setupRc = await gitSetup(opts);
|
|
1771
1754
|
if (setupRc !== 0) return setupRc;
|
|
1772
1755
|
ensureSecret(cloud, ws);
|
|
1773
|
-
const url = `${cloud}/
|
|
1774
|
-
if (
|
|
1775
|
-
if (
|
|
1776
|
-
|
|
1756
|
+
const url = `${cloud}/v2/workspaces/${ws}/git`;
|
|
1757
|
+
if (git(["remote", "add", name, url], { quiet: true }) !== 0) {
|
|
1758
|
+
if (git(["remote", "set-url", name, url], { quiet: true }) !== 0) {
|
|
1759
|
+
err4(`could not add or update remote '${name}'`);
|
|
1777
1760
|
return 1;
|
|
1778
1761
|
}
|
|
1779
1762
|
}
|
|
@@ -1781,21 +1764,21 @@ async function gitRemote(ws, name, opts) {
|
|
|
1781
1764
|
if (token === void 0) {
|
|
1782
1765
|
const principal = loadCreds().principal;
|
|
1783
1766
|
if (principal === void 0) {
|
|
1784
|
-
|
|
1767
|
+
err4(
|
|
1785
1768
|
`remote '${name}' \u2192 ${url} is wired, but a BIRTH push needs a principal:
|
|
1786
1769
|
githolon login --agent (then just push)
|
|
1787
1770
|
\u2014 or re-run after githolon ws create/login so a principal is on file`
|
|
1788
1771
|
);
|
|
1789
1772
|
return 1;
|
|
1790
1773
|
}
|
|
1791
|
-
if (
|
|
1792
|
-
|
|
1774
|
+
if (git(["config", `http.${url}.extraHeader`, `x-nomos-principal: ${principal}`]) !== 0) {
|
|
1775
|
+
err4("git config failed setting the principal header");
|
|
1793
1776
|
return 1;
|
|
1794
1777
|
}
|
|
1795
1778
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1779
|
+
out4(`\u2713 remote '${name}' \u2192 ${url}`);
|
|
1780
|
+
out4(` workspace secret on file; ${token !== void 0 ? "births ride your session (verified)" : "births ride x-nomos-principal (bare uid)"}`);
|
|
1781
|
+
out4(` birth/deploy by push: git push ${name} main`);
|
|
1799
1782
|
return 0;
|
|
1800
1783
|
}
|
|
1801
1784
|
|
|
@@ -1899,22 +1882,6 @@ var HELP = {
|
|
|
1899
1882
|
what: "The workspace-secret store (~/.holon/credentials.json, 0600). `set` imports one you already\nhold; `rotate` mints a fresh one (and claims a legacy workspace).",
|
|
1900
1883
|
examples: ["githolon secret set my-ws nws_v1_\u2026", "githolon secret rotate my-ws"]
|
|
1901
1884
|
},
|
|
1902
|
-
capability: {
|
|
1903
|
-
usage: "githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws> [--from-gcloud <name>] [--scope <s>] [--expires-at <iso|ms>] [--cloud <url>]",
|
|
1904
|
-
what: "Capability credentials (architecture/capability_credentials.md): the ledger holds FACTS ABOUT\nAUTHORITY (the CapabilityBinding record \u2014 keyHash/scope/expiry/epoch), the credential VALUE\nlives at exactly ONE host (the workspace's holon). `bind` ships the value over the owner-gated\nlane ONCE \u2014 from stdin (pipe it) or Google Secret Manager, NEVER argv (ps/history are hosts\ntoo). `rotate` = revoke + re-bind at epoch+1; `unbind` revokes the fact and the host forgets\nthe value; `list` shows facts only.",
|
|
1905
|
-
flags: [
|
|
1906
|
-
["--from-gcloud <name>", "read the value from `gcloud secrets versions access latest --secret=<name>`"],
|
|
1907
|
-
["--scope <s>", "a narrowing label recorded on the law fact (e.g. read-only)"],
|
|
1908
|
-
["--expires-at <t>", "expiry recorded on the law fact (ISO date/datetime or epoch-ms; fail-closed at the host)"],
|
|
1909
|
-
["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
|
|
1910
|
-
],
|
|
1911
|
-
examples: [
|
|
1912
|
-
"cat token.txt | githolon capability bind nomos-usage cf-analytics",
|
|
1913
|
-
"githolon capability bind my-ws openai --from-gcloud openai-api-key --scope read-only",
|
|
1914
|
-
"githolon capability list nomos-usage",
|
|
1915
|
-
"githolon capability rotate nomos-usage cf-analytics --from-gcloud cf-analytics-token"
|
|
1916
|
-
]
|
|
1917
|
-
},
|
|
1918
1885
|
git: {
|
|
1919
1886
|
usage: "githolon git <setup | remote [<ws>] [<remoteName>]> [--cloud <url>] [--target <name>]",
|
|
1920
1887
|
what: "Stock git push as deploy/birth. `setup` installs the credential helper for the cloud host;\n`remote` wires this repo to a workspace (then: git push nomos main). With no <ws>, the\nproject's `workspace` binding resolves it.",
|
|
@@ -1946,23 +1913,23 @@ function commandHelp(cmd) {
|
|
|
1946
1913
|
init_engine();
|
|
1947
1914
|
init_cloud();
|
|
1948
1915
|
init_local_holon();
|
|
1949
|
-
import { spawnSync as
|
|
1916
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1950
1917
|
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1951
1918
|
import { join as join8 } from "node:path";
|
|
1952
|
-
var
|
|
1953
|
-
var
|
|
1919
|
+
var out5 = (s) => void process.stdout.write(s + "\n");
|
|
1920
|
+
var err5 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1954
1921
|
var WS2 = "local";
|
|
1955
1922
|
async function ledgerInit(dir, opts) {
|
|
1956
1923
|
const cloud = cloudBase(opts.cloud);
|
|
1957
1924
|
if (existsSync7(dir) && readdirSync3(dir).length > 0) {
|
|
1958
|
-
|
|
1925
|
+
err5(`refusing to mint into non-empty directory: ${dir}`);
|
|
1959
1926
|
return 1;
|
|
1960
1927
|
}
|
|
1961
1928
|
let deployFile;
|
|
1962
1929
|
try {
|
|
1963
1930
|
deployFile = discoverDeployJson(process.cwd(), opts.file);
|
|
1964
1931
|
} catch (e) {
|
|
1965
|
-
|
|
1932
|
+
err5(e.message);
|
|
1966
1933
|
return 1;
|
|
1967
1934
|
}
|
|
1968
1935
|
const deployBody = JSON.parse(readFileSync6(deployFile, "utf8"));
|
|
@@ -1970,10 +1937,10 @@ async function ledgerInit(dir, opts) {
|
|
|
1970
1937
|
const c = loadCreds();
|
|
1971
1938
|
const principal = await sessionToken().catch(() => void 0) !== void 0 ? c.session.uid : c.principal;
|
|
1972
1939
|
if (principal === void 0) {
|
|
1973
|
-
|
|
1940
|
+
err5("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
|
|
1974
1941
|
return 1;
|
|
1975
1942
|
}
|
|
1976
|
-
|
|
1943
|
+
out5(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
|
|
1977
1944
|
const { eng } = await holonEngine(cloud, deployBody, principal);
|
|
1978
1945
|
await mountFresh(eng, WS2);
|
|
1979
1946
|
author(eng, WS2, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, principal), "");
|
|
@@ -1985,17 +1952,17 @@ async function ledgerInit(dir, opts) {
|
|
|
1985
1952
|
writeTreeToDisk(gitTree, gitDir);
|
|
1986
1953
|
const cfgPath = join8(gitDir, "config");
|
|
1987
1954
|
writeFileSync4(cfgPath, readFileSync6(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1955
|
+
spawnSync3("git", ["-C", dir, "reset", "--hard", "main"], { stdio: "ignore" });
|
|
1956
|
+
out5(`\u2713 holon written \u2192 ${dir} (a normal git repo; git log IS the audit trail)`);
|
|
1957
|
+
out5(`
|
|
1991
1958
|
Birth it on Nomos Cloud (the cloud re-derives this exact verdict):`);
|
|
1992
|
-
|
|
1959
|
+
out5(` cd ${dir} && npx githolon git remote <ws> && git push nomos main`);
|
|
1993
1960
|
return 0;
|
|
1994
1961
|
}
|
|
1995
1962
|
async function ledgerVerify(dir, opts) {
|
|
1996
1963
|
const gitDir = existsSync7(join8(dir, ".git")) ? join8(dir, ".git") : dir;
|
|
1997
1964
|
if (!existsSync7(join8(gitDir, "HEAD"))) {
|
|
1998
|
-
|
|
1965
|
+
err5(`${dir} is not a git repo (no HEAD)`);
|
|
1999
1966
|
return 1;
|
|
2000
1967
|
}
|
|
2001
1968
|
const { eng } = await holonEngine(cloudBase(opts.cloud), void 0, "verify");
|
|
@@ -2012,9 +1979,9 @@ init_cloud();
|
|
|
2012
1979
|
init_local_holon();
|
|
2013
1980
|
import { existsSync as existsSync8 } from "node:fs";
|
|
2014
1981
|
import { join as join9 } from "node:path";
|
|
2015
|
-
import
|
|
2016
|
-
var
|
|
2017
|
-
var
|
|
1982
|
+
import git2 from "isomorphic-git";
|
|
1983
|
+
var out6 = (s) => void process.stdout.write(s + "\n");
|
|
1984
|
+
var err6 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2018
1985
|
var SOURCE = "source";
|
|
2019
1986
|
var REPLAY = "replay";
|
|
2020
1987
|
function summarizePayload(payload, max = 100) {
|
|
@@ -2050,9 +2017,9 @@ function capJsonStrings(v, max = 2048) {
|
|
|
2050
2017
|
if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
|
|
2051
2018
|
if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
|
|
2052
2019
|
if (typeof v === "object" && v !== null) {
|
|
2053
|
-
const
|
|
2054
|
-
for (const [k, x] of Object.entries(v))
|
|
2055
|
-
return
|
|
2020
|
+
const out9 = {};
|
|
2021
|
+
for (const [k, x] of Object.entries(v)) out9[k] = capJsonStrings(x, max);
|
|
2022
|
+
return out9;
|
|
2056
2023
|
}
|
|
2057
2024
|
return v;
|
|
2058
2025
|
}
|
|
@@ -2067,12 +2034,12 @@ function typeOf(ev) {
|
|
|
2067
2034
|
}
|
|
2068
2035
|
async function walkChain(eng, ws) {
|
|
2069
2036
|
const gitdir = gitdirOf(ws);
|
|
2070
|
-
const
|
|
2037
|
+
const dec4 = new TextDecoder();
|
|
2071
2038
|
const entries = [];
|
|
2072
|
-
for (const c of await
|
|
2039
|
+
for (const c of await git2.log({ fs: eng.fs, gitdir, ref: "main" })) {
|
|
2073
2040
|
try {
|
|
2074
|
-
const { blob } = await
|
|
2075
|
-
entries.push({ oid: c.oid, bytes: blob, doc: JSON.parse(
|
|
2041
|
+
const { blob } = await git2.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" });
|
|
2042
|
+
entries.push({ oid: c.oid, bytes: blob, doc: JSON.parse(dec4.decode(blob)) });
|
|
2076
2043
|
} catch {
|
|
2077
2044
|
}
|
|
2078
2045
|
}
|
|
@@ -2099,12 +2066,12 @@ function readKey() {
|
|
|
2099
2066
|
async function replay(target, opts) {
|
|
2100
2067
|
const cloud = cloudBase(opts.cloud);
|
|
2101
2068
|
const json = opts.json === true;
|
|
2102
|
-
const emit = (o) =>
|
|
2069
|
+
const emit = (o) => out6(JSON.stringify(o));
|
|
2103
2070
|
let eng;
|
|
2104
2071
|
try {
|
|
2105
2072
|
({ eng } = await holonEngine(cloud, void 0, "githolon-replay"));
|
|
2106
2073
|
} catch (e) {
|
|
2107
|
-
|
|
2074
|
+
err6(`cannot boot the local holon: ${String(e.message ?? e)}
|
|
2108
2075
|
the runtime rides ${cloud}/v1/runtime/* (cached in ~/.holon/runtime after one fetch)`);
|
|
2109
2076
|
return 1;
|
|
2110
2077
|
}
|
|
@@ -2112,15 +2079,15 @@ async function replay(target, opts) {
|
|
|
2112
2079
|
if (isDir) {
|
|
2113
2080
|
const gitDir = existsSync8(join9(target, ".git")) ? join9(target, ".git") : target;
|
|
2114
2081
|
if (!existsSync8(join9(gitDir, "HEAD"))) {
|
|
2115
|
-
|
|
2082
|
+
err6(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
|
|
2116
2083
|
return 1;
|
|
2117
2084
|
}
|
|
2118
2085
|
await mountFresh(eng, SOURCE);
|
|
2119
2086
|
eng.preopen.dir.contents.get("ws").contents.get(SOURCE).contents.set("nomos.git", readTreeFromDisk(gitDir));
|
|
2120
2087
|
} else {
|
|
2121
|
-
const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/
|
|
2088
|
+
const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/v2/workspaces/${target}/git`, headers: {} });
|
|
2122
2089
|
if (m.restored !== true) {
|
|
2123
|
-
|
|
2090
|
+
err6(
|
|
2124
2091
|
`workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to replay` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "") + `
|
|
2125
2092
|
check the name (githolon ws status ${target}), or pass a local holon directory instead`
|
|
2126
2093
|
);
|
|
@@ -2129,7 +2096,7 @@ async function replay(target, opts) {
|
|
|
2129
2096
|
}
|
|
2130
2097
|
const chain = await walkChain(eng, SOURCE);
|
|
2131
2098
|
if (chain.length === 0) {
|
|
2132
|
-
|
|
2099
|
+
err6(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
|
|
2133
2100
|
return 1;
|
|
2134
2101
|
}
|
|
2135
2102
|
await mountFresh(eng, REPLAY);
|
|
@@ -2138,8 +2105,8 @@ async function replay(target, opts) {
|
|
|
2138
2105
|
const tallies = /* @__PURE__ */ new Map();
|
|
2139
2106
|
let autoRun = !interactive;
|
|
2140
2107
|
if (!json) {
|
|
2141
|
-
|
|
2142
|
-
if (interactive)
|
|
2108
|
+
out6(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014 every step re-admitted through the wasm gate`);
|
|
2109
|
+
if (interactive) out6(" keys: enter = next intent \xB7 a = run to the end \xB7 q = quit\n");
|
|
2143
2110
|
}
|
|
2144
2111
|
for (let i = 0; i < chain.length; i++) {
|
|
2145
2112
|
const { oid, bytes, doc } = chain[i];
|
|
@@ -2153,8 +2120,8 @@ async function replay(target, opts) {
|
|
|
2153
2120
|
if (res.ok === false) {
|
|
2154
2121
|
const msg = `replay REFUSED at intent ${i} (${domain}/${directive}, ${oid.slice(0, 10)}): ${res.error ?? "unknown"}`;
|
|
2155
2122
|
if (json) emit({ step: i, oid, domain, directive, refused: true, error: res.error ?? "unknown" });
|
|
2156
|
-
else
|
|
2157
|
-
|
|
2123
|
+
else err6(msg);
|
|
2124
|
+
err6("the chain's own gate rejects this entry on a fresh fold \u2014 the source it came from would refuse it too (verify below names the check)");
|
|
2158
2125
|
printVerdict(verifyChainLocal(eng, SOURCE));
|
|
2159
2126
|
return 1;
|
|
2160
2127
|
}
|
|
@@ -2175,31 +2142,31 @@ async function replay(target, opts) {
|
|
|
2175
2142
|
tallies: Object.fromEntries(tallies)
|
|
2176
2143
|
});
|
|
2177
2144
|
} else {
|
|
2178
|
-
|
|
2179
|
-
|
|
2145
|
+
out6(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)}, ${ms.toFixed(0)} ms)`);
|
|
2146
|
+
out6(` payload ${summarizePayload(doc.payload?.payload)}`);
|
|
2180
2147
|
for (const ev of doc.events ?? []) {
|
|
2181
2148
|
const id = ev.aggregate ?? "?";
|
|
2182
2149
|
const was = before.get(id);
|
|
2183
2150
|
const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" : was === void 0 ? "~ touched" : "~ updated";
|
|
2184
|
-
|
|
2151
|
+
out6(` ${verb} ${id}`);
|
|
2185
2152
|
const ops = summarizeOps(ev);
|
|
2186
|
-
if (ops.length > 0)
|
|
2153
|
+
if (ops.length > 0) out6(` ${ops}`);
|
|
2187
2154
|
}
|
|
2188
2155
|
const tallyLine = [...tallies.entries()].map(([t, n]) => `${t}:${n}`).join(" ");
|
|
2189
2156
|
const showTally = stepEvery > 0 ? (i + 1) % stepEvery === 0 || i === chain.length - 1 : true;
|
|
2190
|
-
if (showTally && tallyLine.length > 0)
|
|
2157
|
+
if (showTally && tallyLine.length > 0) out6(` rows ${tallyLine}`);
|
|
2191
2158
|
}
|
|
2192
2159
|
if (interactive && !autoRun && i < chain.length - 1) {
|
|
2193
2160
|
const key = await readKey();
|
|
2194
2161
|
if (key === "quit") {
|
|
2195
|
-
|
|
2162
|
+
out6(`
|
|
2196
2163
|
stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below covers the WHOLE chain as delivered`);
|
|
2197
2164
|
break;
|
|
2198
2165
|
}
|
|
2199
2166
|
if (key === "all") autoRun = true;
|
|
2200
2167
|
}
|
|
2201
2168
|
}
|
|
2202
|
-
if (!json)
|
|
2169
|
+
if (!json) out6("");
|
|
2203
2170
|
const verdict = verifyChainLocal(eng, SOURCE);
|
|
2204
2171
|
if (json) {
|
|
2205
2172
|
emit({ finale: true, verdict });
|
|
@@ -2213,40 +2180,40 @@ init_local_holon();
|
|
|
2213
2180
|
init_cloud();
|
|
2214
2181
|
init_engine();
|
|
2215
2182
|
var SOURCE2 = "source";
|
|
2216
|
-
var
|
|
2183
|
+
var out7 = (s) => {
|
|
2217
2184
|
process.stdout.write(s + "\n");
|
|
2218
2185
|
};
|
|
2219
|
-
var
|
|
2186
|
+
var err7 = (s) => {
|
|
2220
2187
|
process.stderr.write(`error: ${s}
|
|
2221
2188
|
`);
|
|
2222
2189
|
};
|
|
2223
2190
|
async function decrypt(ws, opts) {
|
|
2224
2191
|
const cloud = cloudBase(opts.cloud);
|
|
2225
2192
|
if (!opts.as) {
|
|
2226
|
-
|
|
2193
|
+
err7("--as <principal> is required (who to decrypt as)");
|
|
2227
2194
|
return 1;
|
|
2228
2195
|
}
|
|
2229
2196
|
if (!opts.secret) {
|
|
2230
|
-
|
|
2197
|
+
err7("--secret <base64 X25519 device secret> is required");
|
|
2231
2198
|
return 1;
|
|
2232
2199
|
}
|
|
2233
2200
|
if (!opts.query) {
|
|
2234
|
-
|
|
2201
|
+
err7("--query <queryId> is required (the read to decrypt)");
|
|
2235
2202
|
return 1;
|
|
2236
2203
|
}
|
|
2237
2204
|
let eng;
|
|
2238
2205
|
try {
|
|
2239
2206
|
({ eng } = await holonEngine(cloud, opts.overlay, "githolon-decrypt"));
|
|
2240
2207
|
} catch (e) {
|
|
2241
|
-
|
|
2208
|
+
err7(`cannot boot the local holon: ${String(e.message ?? e)}`);
|
|
2242
2209
|
return 1;
|
|
2243
2210
|
}
|
|
2244
2211
|
const m = await mountWorkspace(eng, SOURCE2, {
|
|
2245
|
-
remote: `${cloud}/
|
|
2212
|
+
remote: `${cloud}/v2/workspaces/${ws}/git`,
|
|
2246
2213
|
headers: { "x-nomos-principal": opts.as }
|
|
2247
2214
|
});
|
|
2248
2215
|
if (m.restored !== true) {
|
|
2249
|
-
|
|
2216
|
+
err7(`workspace '${ws}' has no main on ${cloud}, or its clone was refused for '${opts.as}'` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : ""));
|
|
2250
2217
|
return 1;
|
|
2251
2218
|
}
|
|
2252
2219
|
const wraps = query(eng, SOURCE2, "wrappedKeysByRecipient", JSON.stringify({ recipientPrincipal: opts.as }), opts.as);
|
|
@@ -2261,14 +2228,14 @@ async function decrypt(ws, opts) {
|
|
|
2261
2228
|
}
|
|
2262
2229
|
}
|
|
2263
2230
|
}
|
|
2264
|
-
if (opts.json !== true)
|
|
2231
|
+
if (opts.json !== true) out7(`# resolved ${keys.length} scope key(s) for '${opts.as}': ${keys.map((k) => `${k.scope}@${k.epoch}`).join(", ") || "(none)"}`);
|
|
2265
2232
|
const rows = query(eng, SOURCE2, opts.query, JSON.stringify(opts.params ?? {}), opts.as, keys);
|
|
2266
2233
|
if (rows && !Array.isArray(rows) && rows.ok === false) {
|
|
2267
|
-
|
|
2234
|
+
err7(`the read was refused: ${rows.error}` + (String(rows.error).startsWith("read-forbidden") ? `
|
|
2268
2235
|
'${opts.as}' is not granted this scope's read capability` : ""));
|
|
2269
2236
|
return 1;
|
|
2270
2237
|
}
|
|
2271
|
-
|
|
2238
|
+
out7(JSON.stringify(rows, null, opts.json === true ? 0 : 2));
|
|
2272
2239
|
return 0;
|
|
2273
2240
|
}
|
|
2274
2241
|
|
|
@@ -2276,8 +2243,8 @@ async function decrypt(ws, opts) {
|
|
|
2276
2243
|
init_engine();
|
|
2277
2244
|
init_cloud();
|
|
2278
2245
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
2279
|
-
var
|
|
2280
|
-
var
|
|
2246
|
+
var out8 = (s) => void process.stdout.write(s + "\n");
|
|
2247
|
+
var err8 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2281
2248
|
function lawDiffVerdict(localHash, installed, ws) {
|
|
2282
2249
|
const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
|
|
2283
2250
|
const currents = active.filter((d) => d.current === true);
|
|
@@ -2315,7 +2282,7 @@ async function status(wsArg, opts) {
|
|
|
2315
2282
|
try {
|
|
2316
2283
|
ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
|
|
2317
2284
|
} catch (e) {
|
|
2318
|
-
|
|
2285
|
+
err8(e.message);
|
|
2319
2286
|
return 1;
|
|
2320
2287
|
}
|
|
2321
2288
|
let localHash;
|
|
@@ -2325,37 +2292,37 @@ async function status(wsArg, opts) {
|
|
|
2325
2292
|
} catch {
|
|
2326
2293
|
localHash = void 0;
|
|
2327
2294
|
}
|
|
2328
|
-
const r = await fetch(`${cloud}/
|
|
2295
|
+
const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).catch(() => void 0);
|
|
2329
2296
|
if (r === void 0) {
|
|
2330
|
-
|
|
2297
|
+
err8(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
|
|
2331
2298
|
return 1;
|
|
2332
2299
|
}
|
|
2333
2300
|
const d = await r.json().catch(() => void 0);
|
|
2334
2301
|
if (!r.ok || d?.ok !== true) {
|
|
2335
|
-
|
|
2302
|
+
err8(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
|
|
2336
2303
|
is the workspace born? githolon ws create ${ws}`);
|
|
2337
2304
|
return 1;
|
|
2338
2305
|
}
|
|
2339
|
-
|
|
2306
|
+
out8(`workspace ${ws} on ${cloud}`);
|
|
2340
2307
|
const allDomains = d.domains ?? [];
|
|
2341
2308
|
const controlPlaneHashes = new Set(CONTROL_PLANE_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
|
|
2342
2309
|
const domains = allDomains.filter((dm) => !controlPlaneHashes.has(dm.domainHash));
|
|
2343
2310
|
const controllerCount = allDomains.length - domains.length;
|
|
2344
|
-
if (allDomains.length === 0)
|
|
2311
|
+
if (allDomains.length === 0) out8(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
|
|
2345
2312
|
for (const dom of domains) {
|
|
2346
2313
|
const mark = dom.current === true ? " \u2190 current (the holon resolves dispatch here)" : "";
|
|
2347
|
-
|
|
2314
|
+
out8(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
|
|
2348
2315
|
}
|
|
2349
|
-
if (controllerCount > 0)
|
|
2316
|
+
if (controllerCount > 0) out8(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
|
|
2350
2317
|
const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
|
|
2351
|
-
if (tenantActive > 1)
|
|
2318
|
+
if (tenantActive > 1) out8(` (${tenantActive} active tenant installs \u2014 install-beside keeps every prior deploy Active; the holon serves the current one per domain)`);
|
|
2352
2319
|
if (localHash === void 0) {
|
|
2353
|
-
|
|
2320
|
+
out8(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
|
|
2354
2321
|
return 0;
|
|
2355
2322
|
}
|
|
2356
|
-
|
|
2323
|
+
out8(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
|
|
2357
2324
|
const verdict = lawDiffVerdict(localHash, domains, ws);
|
|
2358
|
-
for (const line of verdict.lines)
|
|
2325
|
+
for (const line of verdict.lines) out8(line);
|
|
2359
2326
|
return verdict.inSync ? 0 : 2;
|
|
2360
2327
|
}
|
|
2361
2328
|
|
|
@@ -2399,15 +2366,6 @@ a bare <ws> resolves from nomos.package.mjs's optional \`workspace\` binding):
|
|
|
2399
2366
|
githolon secret set <ws> <secret> import a secret you already hold
|
|
2400
2367
|
githolon secret rotate <ws> rotate (and re-save) the workspace secret
|
|
2401
2368
|
|
|
2402
|
-
Capability credentials (facts on the ledger, values host-side \u2014 the value NEVER rides argv;
|
|
2403
|
-
stdin or --from-gcloud only; architecture/capability_credentials.md):
|
|
2404
|
-
githolon capability bind <ws> <capability> bind a credential: value from stdin (or
|
|
2405
|
-
--from-gcloud <name>) \u2192 the workspace's holon;
|
|
2406
|
-
sha256(value) \u2192 the law-state binding fact
|
|
2407
|
-
githolon capability list <ws> the binding FACTS (never the values)
|
|
2408
|
-
githolon capability rotate <ws> <capability> revoke + re-bind at epoch+1 (new value)
|
|
2409
|
-
githolon capability unbind <ws> <capability> revoke the fact; the host forgets the value
|
|
2410
|
-
|
|
2411
2369
|
Git lane (stock git push as deploy/birth \u2014 see also: push-to-create):
|
|
2412
2370
|
githolon git setup install the credential helper for the cloud host
|
|
2413
2371
|
githolon git remote [<ws>] [<remoteName>] wire this repo to a workspace (default name: nomos)
|
|
@@ -2486,38 +2444,6 @@ ${USAGE}`);
|
|
|
2486
2444
|
if (sub === "rotate" && ws !== void 0) return secretRotate(ws, opts);
|
|
2487
2445
|
return usageFail("expected: githolon secret <set <ws> <secret> | rotate <ws>>");
|
|
2488
2446
|
}
|
|
2489
|
-
async function runCapability(argv) {
|
|
2490
|
-
const usageFail = (m) => {
|
|
2491
|
-
process.stderr.write(`error: ${m}
|
|
2492
|
-
|
|
2493
|
-
${commandHelp("capability")}`);
|
|
2494
|
-
return 1;
|
|
2495
|
-
};
|
|
2496
|
-
const pos = [];
|
|
2497
|
-
const opts = {};
|
|
2498
|
-
const rest = argv.slice(1);
|
|
2499
|
-
for (let i = 0; i < rest.length; i++) {
|
|
2500
|
-
const a = rest[i];
|
|
2501
|
-
if (a === "--cloud" || a === "--from-gcloud" || a === "--scope" || a === "--expires-at") {
|
|
2502
|
-
const v = rest[++i];
|
|
2503
|
-
if (v === void 0) return usageFail(`${a} requires a value`);
|
|
2504
|
-
if (a === "--cloud") opts.cloud = v;
|
|
2505
|
-
else if (a === "--from-gcloud") opts.fromGcloud = v;
|
|
2506
|
-
else if (a === "--scope") opts.scope = v;
|
|
2507
|
-
else opts.expiresAt = v;
|
|
2508
|
-
} else if (a.startsWith("--")) {
|
|
2509
|
-
return usageFail(`unknown flag '${a}'`);
|
|
2510
|
-
} else {
|
|
2511
|
-
pos.push(a);
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
const [sub, ws, capability] = pos;
|
|
2515
|
-
if (sub === "bind" && ws !== void 0 && capability !== void 0) return capabilityBind(ws, capability, opts);
|
|
2516
|
-
if (sub === "rotate" && ws !== void 0 && capability !== void 0) return capabilityRotate(ws, capability, opts);
|
|
2517
|
-
if (sub === "unbind" && ws !== void 0 && capability !== void 0) return capabilityUnbind(ws, capability, opts);
|
|
2518
|
-
if (sub === "list" && ws !== void 0) return capabilityList(ws, opts);
|
|
2519
|
-
return usageFail("expected: githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws>");
|
|
2520
|
-
}
|
|
2521
2447
|
function isKind(s) {
|
|
2522
2448
|
return s !== void 0 && KINDS.includes(s);
|
|
2523
2449
|
}
|
|
@@ -2597,7 +2523,7 @@ async function runProof(args) {
|
|
|
2597
2523
|
const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
|
|
2598
2524
|
const tsxCli = join10(tsxDir, tsxBinRel ?? "dist/cli.mjs");
|
|
2599
2525
|
console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
|
|
2600
|
-
const r =
|
|
2526
|
+
const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
|
|
2601
2527
|
stdio: "inherit",
|
|
2602
2528
|
cwd: process.cwd(),
|
|
2603
2529
|
env: { ...process.env, ...keep ? { PROOF_KEEP: "1" } : {} }
|
|
@@ -2608,7 +2534,7 @@ function parseArgs(argv) {
|
|
|
2608
2534
|
const [command, ...rest] = argv;
|
|
2609
2535
|
if (command !== "generate" && command !== "g") {
|
|
2610
2536
|
throw new Error(
|
|
2611
|
-
`Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | secret
|
|
2537
|
+
`Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | secret.`
|
|
2612
2538
|
);
|
|
2613
2539
|
}
|
|
2614
2540
|
let outDir = DEFAULT_OUT;
|
|
@@ -2774,9 +2700,6 @@ ${commandHelp("status")}`);
|
|
|
2774
2700
|
if (argv[0] === "ws" || argv[0] === "deploy" || argv[0] === "secret") {
|
|
2775
2701
|
return runCloud(argv);
|
|
2776
2702
|
}
|
|
2777
|
-
if (argv[0] === "capability") {
|
|
2778
|
-
return runCapability(argv);
|
|
2779
|
-
}
|
|
2780
2703
|
if (argv[0] === "login") {
|
|
2781
2704
|
const agent = argv.includes("--agent");
|
|
2782
2705
|
const tokenAt = argv.indexOf("--token");
|
|
@@ -2833,8 +2756,8 @@ ${USAGE}`);
|
|
|
2833
2756
|
let parsed;
|
|
2834
2757
|
try {
|
|
2835
2758
|
parsed = parseArgs(argv);
|
|
2836
|
-
} catch (
|
|
2837
|
-
process.stderr.write(`error: ${
|
|
2759
|
+
} catch (err9) {
|
|
2760
|
+
process.stderr.write(`error: ${err9.message}
|
|
2838
2761
|
|
|
2839
2762
|
${USAGE}`);
|
|
2840
2763
|
return 1;
|
|
@@ -2853,8 +2776,8 @@ ${USAGE}`);
|
|
|
2853
2776
|
process.stdout.write(`created ${path}
|
|
2854
2777
|
`);
|
|
2855
2778
|
return 0;
|
|
2856
|
-
} catch (
|
|
2857
|
-
process.stderr.write(`error: ${
|
|
2779
|
+
} catch (err9) {
|
|
2780
|
+
process.stderr.write(`error: ${err9.message}
|
|
2858
2781
|
`);
|
|
2859
2782
|
return 1;
|
|
2860
2783
|
}
|