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.
- package/dist/cli.mjs +127 -27
- 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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
|
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
|
|
3291
|
-
if (sub === "show") return
|
|
3292
|
-
if (sub === "import") return
|
|
3293
|
-
if (sub === "export") return
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
31
|
-
"@githolon/dsl": "^0.
|
|
32
|
+
"@githolon/client": "^0.24.1",
|
|
33
|
+
"@githolon/dsl": "^0.24.1",
|
|
32
34
|
"isomorphic-git": "^1.38.4"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|