githolon 0.23.1 → 0.24.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +127 -27
  2. package/package.json +5 -3
package/dist/cli.mjs CHANGED
@@ -20,14 +20,19 @@ __export(governance_exports, {
20
20
  keyImport: () => keyImport,
21
21
  keyShow: () => keyShow,
22
22
  resolveGovernancePrincipal: () => resolveGovernancePrincipal,
23
- signAndPostGovernance: () => signAndPostGovernance
23
+ signAndPostGovernance: () => signAndPostGovernance,
24
+ signAndPostOffer: () => signAndPostOffer
24
25
  });
25
26
  import { createHash, createPrivateKey, createPublicKey } from "node:crypto";
26
27
  import { readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
27
- import { connect } from "@githolon/client";
28
+ async function clientConnect() {
29
+ const mod = await import("@githolon/client");
30
+ return mod.connect;
31
+ }
28
32
  async function connectGovernance(cloud, parent) {
29
33
  const authToken = await sessionToken().catch(() => void 0);
30
34
  const clientId = `githolon-cli-${Date.now().toString(16)}`;
35
+ const connect = await clientConnect();
31
36
  return await connect({
32
37
  cloud,
33
38
  workspace: parent,
@@ -56,20 +61,24 @@ function resolveGovernancePrincipal(opts) {
56
61
  }
57
62
  return principal;
58
63
  }
59
- async function signAndPostGovernance(parent, directiveId, payload, opts) {
64
+ async function signAndPostOffer(ws, domain, directiveId, payload, opts) {
60
65
  const cloud = cloudBase(opts.cloud);
61
66
  const principal = resolveGovernancePrincipal(opts);
62
- const holon = await connectGovernance(cloud, parent);
67
+ const holon = await connectGovernance(cloud, ws);
63
68
  const device = await ensureDeviceKey(holon, principal);
64
69
  const sealed = await holon.signGovernanceOffer({
70
+ domain,
65
71
  directiveId,
66
72
  payload,
67
73
  authorSecret: device.secret,
68
74
  actor: `user:${principal}`
69
75
  });
70
- const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws: parent });
76
+ const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws });
71
77
  return { ...verdict, principal };
72
78
  }
79
+ async function signAndPostGovernance(parent, directiveId, payload, opts) {
80
+ return signAndPostOffer(parent, "workspaces", directiveId, payload, opts);
81
+ }
73
82
  async function grantOrRevoke(verb, kind, subject, opts) {
74
83
  const parent = opts.parent ?? "root";
75
84
  const now = Date.now();
@@ -367,6 +376,7 @@ __export(cloud_exports, {
367
376
  wsStatus: () => wsStatus
368
377
  });
369
378
  import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
379
+ import { createHash as createHash2 } from "node:crypto";
370
380
  import { join as join2 } from "node:path";
371
381
  import { homedir } from "node:os";
372
382
  function cloudBase(flag) {
@@ -645,21 +655,68 @@ async function deploy(ws, opts) {
645
655
  err2(e.message);
646
656
  return 1;
647
657
  }
658
+ const fileName = file.split("/").pop();
648
659
  const before = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((br) => br.json()).then((bd) => bd.ok === true ? bd.domains ?? [] : []).catch(() => []);
649
- const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`, {
650
- method: "POST",
651
- headers: { "content-type": "application/json" },
652
- body: readFileSync2(file, "utf8")
653
- });
654
- const d = await r.json().catch(() => void 0);
655
- if (!r.ok || d?.ok !== true) {
656
- err2(`deploy '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
657
- return 1;
660
+ const principal = opts.principal ?? currentPrincipal();
661
+ const signed = opts.principal !== void 0 || principal !== void 0 && getDeviceKey(principal) !== void 0;
662
+ let phaseSuffix = "";
663
+ let deployedHash;
664
+ if (signed) {
665
+ let body;
666
+ try {
667
+ body = JSON.parse(readFileSync2(file, "utf8"));
668
+ } catch (e) {
669
+ err2(`deploy body ${file} is not valid JSON \u2014 recompile: ${e.message}`);
670
+ return 1;
671
+ }
672
+ const packageUsda = body.packageUsda;
673
+ if (typeof packageUsda !== "string" || packageUsda === "") {
674
+ err2(`deploy body ${file} is missing packageUsda \u2014 run \`githolon compile\` again`);
675
+ return 1;
676
+ }
677
+ const targetHash = createHash2("sha256").update(Buffer.from(packageUsda, "utf8")).digest("hex");
678
+ const { signAndPostOffer: signAndPostOffer2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
679
+ let v;
680
+ try {
681
+ const who = resolveGovernancePrincipal2(opts);
682
+ const installPayload2 = {
683
+ domainHash: targetHash,
684
+ packageUsda,
685
+ installedBy: `user:${who}`,
686
+ dependencies: [],
687
+ finalizers: [],
688
+ ...body.dispositions !== void 0 && body.dispositions !== null ? { dispositions: body.dispositions } : {}
689
+ };
690
+ v = await signAndPostOffer2(ws, "nomos", "installDomain", installPayload2, opts);
691
+ } catch (e) {
692
+ err2(e.message);
693
+ return 1;
694
+ }
695
+ if (!v.ok) {
696
+ err2(`signed deploy '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
697
+ return 1;
698
+ }
699
+ deployedHash = targetHash;
700
+ } else {
701
+ const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`, {
702
+ method: "POST",
703
+ headers: { "content-type": "application/json" },
704
+ body: readFileSync2(file, "utf8")
705
+ });
706
+ const d = await r.json().catch(() => void 0);
707
+ if (!r.ok || d?.ok !== true) {
708
+ const txt = d ? JSON.stringify(d) : "no body";
709
+ err2(`deploy '${ws}' failed (${r.status}): ${txt}`);
710
+ if (r.status === 401 || r.status === 403 || r.status === 422 || /warrant|signed|signer|unsigned|authorship|forged|relation|installDomain/i.test(txt)) {
711
+ err2(` this workspace looks WARRANTED \u2014 re-run with --as <principal> to author a SIGNED install offer`);
712
+ err2(` (import the admin signing key first if needed: githolon key import --principal <uid> --secret <key>)`);
713
+ }
714
+ return 1;
715
+ }
716
+ const phase = d.installation?.[0]?.data?.["status.phase"];
717
+ phaseSuffix = typeof phase === "string" ? ` (${phase})` : "";
718
+ deployedHash = typeof d.domainHash === "string" ? d.domainHash : void 0;
658
719
  }
659
- const phase = d.installation?.[0]?.data?.["status.phase"];
660
- const fileName = file.split("/").pop();
661
- const phaseSuffix = typeof phase === "string" ? ` (${phase})` : "";
662
- const deployedHash = typeof d.domainHash === "string" ? d.domainHash : void 0;
663
720
  const after = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((ar) => ar.json()).then((ad) => ad.ok === true ? ad : {}).catch(() => ({}));
664
721
  const currentHashes = new Set(Object.values(after.currentLaw ?? {}));
665
722
  const hasCurrencyData = Object.keys(after.currentLaw ?? {}).length > 0;
@@ -1440,7 +1497,43 @@ function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
1440
1497
  if (cb.lawHashField !== void 0) payload[cb.lawHashField] = lawHash;
1441
1498
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
1442
1499
  for (const f of cb.birthAutoStamped) payload[f] = stamp;
1443
- const res = author(eng, ws, cb.parentDomain, cb.birthDirectiveId, payload, lawHash);
1500
+ const actor = actorFromPayload(payload, cb.birthActorField);
1501
+ if (cb.birthRequiresRelation !== void 0 && actor === void 0) {
1502
+ return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} is gated by .requires("${cb.birthRequiresRelation}") but the proof could not discover an actor field from the birth recipe \u2014 route the genesis step actor from a payload subject field`);
1503
+ }
1504
+ let parentWs = ws;
1505
+ if (cb.parentSetup !== void 0) {
1506
+ const setup = cb.parentSetup;
1507
+ const setupPayload = { ...setup.birthPayload };
1508
+ if (setup.frameworkHashField !== void 0) setupPayload[setup.frameworkHashField] = fw;
1509
+ if (setup.lawHashField !== void 0) setupPayload[setup.lawHashField] = lawHash;
1510
+ for (const f of setup.birthAutoStamped) setupPayload[f] = stamp;
1511
+ if (setup.birthActorField !== void 0 && cb.birthActorField !== void 0 && payload[cb.birthActorField] !== void 0) {
1512
+ setupPayload[setup.birthActorField] = payload[cb.birthActorField];
1513
+ }
1514
+ const setupActor = actorFromPayload(setupPayload, setup.birthActorField) ?? actor;
1515
+ const setupOpts = setupActor !== void 0 ? { actor: setupActor } : {};
1516
+ const setupRes = author(
1517
+ eng,
1518
+ ws,
1519
+ setup.parentDomain,
1520
+ setup.birthDirectiveId,
1521
+ setupPayload,
1522
+ lawHash,
1523
+ setupOpts
1524
+ );
1525
+ if (setupRes.ok === false) {
1526
+ return done(false, `parent setup ${setup.parentDomain}/${setup.birthDirectiveId} refused: ${setupRes.error ?? "unknown"} \u2014 the proof could not birth the parent workspace needed before gated child birth`);
1527
+ }
1528
+ const bornParent = (setupRes.born ?? []).map((b) => b?.workspace).find((w) => typeof w === "string" && w.length > 0);
1529
+ if (bornParent === void 0) {
1530
+ return done(false, `parent setup ${setup.parentDomain}/${setup.birthDirectiveId} admitted but spawned no parent workspace (born=${JSON.stringify(setupRes.born ?? [])})`);
1531
+ }
1532
+ parentWs = bornParent;
1533
+ lines.push(`\u2713 parent setup ${setup.parentDomain}/${setup.birthDirectiveId} \u2014 ${parentWs} born before gated child birth`);
1534
+ }
1535
+ const opts = actor !== void 0 ? { actor } : {};
1536
+ const res = author(eng, parentWs, cb.parentDomain, cb.birthDirectiveId, payload, lawHash, opts);
1444
1537
  if (res.ok === false) {
1445
1538
  return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} refused: ${res.error ?? "unknown"} \u2014 the parent's own gate rejected the birth offer (check the .births() plan + payload)`);
1446
1539
  }
@@ -1494,6 +1587,11 @@ function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
1494
1587
  lines.push(`\u2713 verify_chain GREEN on the born child ${childWs} (${verdict.plansRerun ?? "?"} plans re-run \u2014 it self-validates from intent 0 through its OWN gate)`);
1495
1588
  return done(true);
1496
1589
  }
1590
+ function actorFromPayload(payload, field) {
1591
+ if (field === void 0) return void 0;
1592
+ const value = payload[field];
1593
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1594
+ }
1497
1595
  async function runOfflineProofStandalone(proofPath, cloud, domain) {
1498
1596
  const src = readFileSync4(proofPath, "utf8");
1499
1597
  const packageName = basename(proofPath).replace(/\.proof\.mts$/, "");
@@ -1736,7 +1834,6 @@ Re-run with --force to replace it.`
1736
1834
 
1737
1835
  // src/cli.ts
1738
1836
  init_cloud();
1739
- init_governance();
1740
1837
 
1741
1838
  // src/compile.ts
1742
1839
  import { spawn, spawnSync } from "node:child_process";
@@ -3279,6 +3376,7 @@ ${commandHelp("status")}`);
3279
3376
  return runCloud(argv);
3280
3377
  }
3281
3378
  if (argv[0] === "key") {
3379
+ const { keyEnroll: keyEnroll2, keyExport: keyExport2, keyImport: keyImport2, keyShow: keyShow2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
3282
3380
  const { pos, opts, bad } = cloudArgs(argv.slice(1));
3283
3381
  if (bad !== void 0) {
3284
3382
  process.stderr.write(`error: ${bad}
@@ -3287,16 +3385,17 @@ ${USAGE}`);
3287
3385
  return 1;
3288
3386
  }
3289
3387
  const [sub] = pos;
3290
- if (sub === "enroll") return keyEnroll(opts);
3291
- if (sub === "show") return keyShow(opts);
3292
- if (sub === "import") return keyImport(opts);
3293
- if (sub === "export") return keyExport(opts);
3388
+ if (sub === "enroll") return keyEnroll2(opts);
3389
+ if (sub === "show") return keyShow2(opts);
3390
+ if (sub === "import") return keyImport2(opts);
3391
+ if (sub === "export") return keyExport2(opts);
3294
3392
  process.stderr.write(`error: expected: githolon key <enroll|show|import|export> [--principal <uid>]
3295
3393
 
3296
3394
  ${USAGE}`);
3297
3395
  return 1;
3298
3396
  }
3299
3397
  if (argv[0] === "gov") {
3398
+ const { governanceOffer: governanceOffer2, governanceRelay: governanceRelay2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
3300
3399
  const { pos, opts, bad } = cloudArgs(argv.slice(1));
3301
3400
  if (bad !== void 0) {
3302
3401
  process.stderr.write(`error: ${bad}
@@ -3312,7 +3411,7 @@ ${commandHelp("gov") ?? USAGE}`);
3312
3411
  ${commandHelp("gov") ?? USAGE}`);
3313
3412
  return 1;
3314
3413
  }
3315
- return governanceRelay(ws2, file, opts);
3414
+ return governanceRelay2(ws2, file, opts);
3316
3415
  }
3317
3416
  const [ws, directiveId] = pos;
3318
3417
  if (ws === void 0 || directiveId === void 0) {
@@ -3321,9 +3420,10 @@ ${commandHelp("gov") ?? USAGE}`);
3321
3420
  ${commandHelp("gov") ?? USAGE}`);
3322
3421
  return 1;
3323
3422
  }
3324
- return governanceOffer(ws, directiveId, opts);
3423
+ return governanceOffer2(ws, directiveId, opts);
3325
3424
  }
3326
3425
  if (argv[0] === "grant" || argv[0] === "revoke") {
3426
+ const { grantOrRevoke: grantOrRevoke2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
3327
3427
  const verb = argv[0];
3328
3428
  const { pos, opts, bad } = cloudArgs(argv.slice(1));
3329
3429
  if (bad !== void 0) {
@@ -3339,7 +3439,7 @@ ${USAGE}`);
3339
3439
  ${USAGE}`);
3340
3440
  return 1;
3341
3441
  }
3342
- return grantOrRevoke(verb, kind, subject, opts);
3442
+ return grantOrRevoke2(verb, kind, subject, opts);
3343
3443
  }
3344
3444
  if (argv[0] === "login") {
3345
3445
  const agent = argv.includes("--agent");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.23.1",
3
+ "version": "0.24.1",
4
4
  "type": "module",
5
5
  "description": "githolon — the Nomos developer CLI: Rails-style generators for @githolon/dsl domains + the package compiler. Kernel-independent.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -21,14 +21,16 @@
21
21
  "scripts": {
22
22
  "build": "node build.mjs",
23
23
  "prepare": "npm run build",
24
+ "pretest": "npm run build",
24
25
  "holon": "tsx src/cli.ts",
25
26
  "test": "vitest run",
27
+ "pretypecheck": "npm run build",
26
28
  "typecheck": "tsc --noEmit"
27
29
  },
28
30
  "dependencies": {
29
31
  "@bjorn3/browser_wasi_shim": "0.4.2",
30
- "@githolon/client": "^0.23.1",
31
- "@githolon/dsl": "^0.23.1",
32
+ "@githolon/client": "^0.24.1",
33
+ "@githolon/dsl": "^0.24.1",
32
34
  "isomorphic-git": "^1.38.4"
33
35
  },
34
36
  "devDependencies": {