githolon 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +124 -11
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -924,20 +924,45 @@ async function wsRetire(ws, opts) {
|
|
|
924
924
|
const cloud = cloudBase(opts.cloud);
|
|
925
925
|
const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
926
926
|
const parent = opts.parent ?? "root";
|
|
927
|
+
let record;
|
|
928
|
+
try {
|
|
929
|
+
const q = await fetch(`${cloud}/v2/workspaces/${parent}/workspaceByName?name=${encodeURIComponent(ws)}`);
|
|
930
|
+
const qd = await q.json().catch(() => void 0);
|
|
931
|
+
const rows = qd?.ok === true ? qd.rows ?? [] : [];
|
|
932
|
+
record = rows.find((r) => r.data?.status !== "retired" && r.data?.status !== "reclaimed") ?? rows[0];
|
|
933
|
+
} catch {
|
|
934
|
+
}
|
|
935
|
+
const recordId = record?.id;
|
|
936
|
+
if (recordId === void 0) {
|
|
937
|
+
err3(
|
|
938
|
+
`retire '${ws}': no CloudWorkspace record named '${ws}' in ${parent}'s registry \u2014 nothing lawful to retire.
|
|
939
|
+
(a workspace born via the OPEN lane \u2014 bare POST /v2/workspaces/${ws} \u2014 has no registry row;
|
|
940
|
+
registered births ride \`githolon ws create ${ws} --via ${parent}\`. An unregistered leak needs a custodian sweep.)`
|
|
941
|
+
);
|
|
942
|
+
return 1;
|
|
943
|
+
}
|
|
927
944
|
let v;
|
|
928
945
|
try {
|
|
929
946
|
const principal = resolveGovernancePrincipal2(opts);
|
|
930
|
-
const payload = { workspaceId:
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
947
|
+
const payload = { workspaceId: recordId, retiredBy: principal, retiredAt: Date.now() };
|
|
948
|
+
const bare = (s) => s.startsWith("user:") ? s.slice(5) : s;
|
|
949
|
+
const creator = record?.data?.createdBy ?? record?.data?.principal ?? "";
|
|
950
|
+
const own = creator !== "" && bare(creator) === bare(principal);
|
|
951
|
+
v = await signAndPostGovernance2(parent, own ? "retireOwnWorkspace" : "retireWorkspace", payload, opts);
|
|
935
952
|
} catch (e) {
|
|
936
953
|
err3(e.message);
|
|
937
954
|
return 1;
|
|
938
955
|
}
|
|
939
956
|
if (!v.ok) {
|
|
940
957
|
err3(`retire '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
958
|
+
if (JSON.stringify(v.body ?? "").includes("retire-own-only")) {
|
|
959
|
+
err3(
|
|
960
|
+
`note: you appear to BE '${ws}'s creator \u2014 the parent's deployed governance law may predate the
|
|
961
|
+
0.34.1 retireOwnWorkspace creator-normalization fix (the 'user:' subject prefix vs the bare uid).
|
|
962
|
+
A governance re-deploy to ${parent} fixes the self-service lane; meanwhile an ADMIN can retire it:
|
|
963
|
+
githolon ws retire ${ws} --as <admin-uid>`
|
|
964
|
+
);
|
|
965
|
+
}
|
|
941
966
|
return 1;
|
|
942
967
|
}
|
|
943
968
|
out3(`\u2713 workspace ${ws} retired via signed governance offer to ${parent} on ${cloud}`);
|
|
@@ -1515,11 +1540,35 @@ function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}
|
|
|
1515
1540
|
capPorts.principal_verified = !!opts.principalVerified;
|
|
1516
1541
|
}
|
|
1517
1542
|
const envelope = { payload: { domain, directiveId, payload }, captured_ports: capPorts, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
|
|
1543
|
+
if (opts.attestations !== void 0) envelope.attestations = opts.attestations;
|
|
1518
1544
|
writeWork(eng, `payload-${seq}.json`, enc3.encode(JSON.stringify(payload)));
|
|
1519
1545
|
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
1520
1546
|
const genesis = !controllerHash;
|
|
1547
|
+
const attestorSecretFor = (from) => {
|
|
1548
|
+
const s = opts.attestorSecrets;
|
|
1549
|
+
if (!s) return null;
|
|
1550
|
+
if (typeof s === "string") return s;
|
|
1551
|
+
return typeof s[from] === "string" ? s[from] : null;
|
|
1552
|
+
};
|
|
1521
1553
|
const defer = opts.deferProjection !== false;
|
|
1522
|
-
const
|
|
1554
|
+
const offerOnce = () => 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 ?? "", ...opts.authToken ? { authToken: opts.authToken } : {}, 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));
|
|
1555
|
+
let v = offerOnce();
|
|
1556
|
+
for (let pass = 0; pass < 8 && v.outcome === "refused"; pass++) {
|
|
1557
|
+
const need = parseAttestedReadNeed(v.verdict?.reason ?? v.error);
|
|
1558
|
+
if (!need || !eng.mounted.has(need.from)) break;
|
|
1559
|
+
const secret = attestorSecretFor(need.from);
|
|
1560
|
+
if (!secret) break;
|
|
1561
|
+
const served = attestedRead(eng, need.from, {
|
|
1562
|
+
queryId: need.queryId,
|
|
1563
|
+
paramsJson: JSON.stringify(need.args),
|
|
1564
|
+
attestorSecret: secret,
|
|
1565
|
+
nowMs: opts.attestNowMs ?? Date.now()
|
|
1566
|
+
});
|
|
1567
|
+
if (!served || served.ok !== true) break;
|
|
1568
|
+
envelope.attestations = [...envelope.attestations ?? [], { queryId: need.queryId, result: served.rows, attestation: served.attestation }];
|
|
1569
|
+
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
1570
|
+
v = offerOnce();
|
|
1571
|
+
}
|
|
1523
1572
|
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, ...v.born ? { born: v.born } : {} } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
1524
1573
|
if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
1525
1574
|
return res;
|
|
@@ -1552,6 +1601,19 @@ async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", lo
|
|
|
1552
1601
|
kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
|
|
1553
1602
|
return tip;
|
|
1554
1603
|
}
|
|
1604
|
+
function parseAttestedReadNeed(error) {
|
|
1605
|
+
const m = /attested-read-unreachable: read\('([^']+)', \{from: '([^']+)'\}\) args=(.*?) has no injected attestation/.exec(String(error || ""));
|
|
1606
|
+
if (!m) return null;
|
|
1607
|
+
let blob = m[3];
|
|
1608
|
+
for (let i = 0; i < 4; i++) {
|
|
1609
|
+
try {
|
|
1610
|
+
return { queryId: m[1], from: m[2], args: JSON.parse(blob) };
|
|
1611
|
+
} catch {
|
|
1612
|
+
blob = blob.replace(/\\(["\\])/g, "$1");
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return null;
|
|
1616
|
+
}
|
|
1555
1617
|
function offerIntent(eng, ws, bytes, opts) {
|
|
1556
1618
|
const name = `offer-in-${eng.seq++}.json`;
|
|
1557
1619
|
writeWork(eng, name, bytes);
|
|
@@ -1569,7 +1631,7 @@ function applyIntentBytes(eng, ws, bytes, opts) {
|
|
|
1569
1631
|
function verifyChainLocal(eng, ws) {
|
|
1570
1632
|
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng.STDERR));
|
|
1571
1633
|
}
|
|
1572
|
-
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, cryptoUnwrapKey, count, sum;
|
|
1634
|
+
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, attestedRead, cryptoUnwrapKey, count, sum;
|
|
1573
1635
|
var init_engine = __esm({
|
|
1574
1636
|
"vendor/engine/engine.mjs"() {
|
|
1575
1637
|
"use strict";
|
|
@@ -1626,6 +1688,12 @@ var init_engine = __esm({
|
|
|
1626
1688
|
custodyLsRefs = (ledger, prefix = null) => lsRefs(ledger.remote, ledger.headers || {}, { service: "git-upload-pack", prefix });
|
|
1627
1689
|
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));
|
|
1628
1690
|
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));
|
|
1691
|
+
attestedRead = (eng, ws, { queryId, paramsJson, attestorSecret, nowMs }) => JSON.parse(call(eng.ex, "query", {
|
|
1692
|
+
repoArg: repoArgOf(ws),
|
|
1693
|
+
workspace: ws,
|
|
1694
|
+
branch: BRANCH,
|
|
1695
|
+
queryBytes: b64Json({ op: "attestedRead", queryId, paramsJson: paramsJson || "{}", attestorSecret, nowMs: nowMs ?? Date.now(), sourceWorkspace: ws })
|
|
1696
|
+
}, eng.STDERR));
|
|
1629
1697
|
cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "query", { queryBytes: b64Json({ op: "cryptoUnwrapKey", secret, hpkeEpk, ct }) }, eng.STDERR)).scopeKey;
|
|
1630
1698
|
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));
|
|
1631
1699
|
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));
|
|
@@ -4014,7 +4082,7 @@ var HELP = {
|
|
|
4014
4082
|
},
|
|
4015
4083
|
proof: {
|
|
4016
4084
|
usage: "githolon proof [build/<name>.proof.mts] [--domain <key>] [--live] [--keep]",
|
|
4017
|
-
what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence)
|
|
4085
|
+
what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence). The throwaway is\ncreated REGISTERED (a signed createWorkspace at root \u2014 your principal needs creation authority\nthere) and RETIRED on every exit via the lawful retireOwnWorkspace; --keep keeps it. ALL GREEN or it names the jam. The offline\nlegs also rerun on every save in `githolon dev`.",
|
|
4018
4086
|
examples: ["githolon proof", "githolon proof --domain co2_platform", "githolon proof --live", "githolon proof --live --keep"]
|
|
4019
4087
|
},
|
|
4020
4088
|
dev: {
|
|
@@ -4899,13 +4967,58 @@ async function runProof(args) {
|
|
|
4899
4967
|
const tsxPkg = JSON.parse(readFileSync11(join11(tsxDir, "package.json"), "utf8"));
|
|
4900
4968
|
const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
|
|
4901
4969
|
const tsxCli = join11(tsxDir, tsxBinRel ?? "dist/cli.mjs");
|
|
4902
|
-
console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept
|
|
4970
|
+
console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept" : "") + ")");
|
|
4971
|
+
const { wsCreate: wsCreate2, wsRetire: wsRetire2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
|
|
4972
|
+
const { resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
4973
|
+
const { login: login2, activePrincipal: activePrincipal2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
|
|
4974
|
+
let principal = process.env.NOMOS_PRINCIPAL;
|
|
4975
|
+
try {
|
|
4976
|
+
principal ??= resolveGovernancePrincipal2({});
|
|
4977
|
+
} catch {
|
|
4978
|
+
const lr = await login2({ agent: true });
|
|
4979
|
+
principal = lr === 0 ? activePrincipal2() : void 0;
|
|
4980
|
+
}
|
|
4981
|
+
if (principal === void 0) {
|
|
4982
|
+
return refuse("proof --live needs a principal \u2014 githolon login --agent (self-onboarding) failed; try it directly or pass NOMOS_PRINCIPAL");
|
|
4983
|
+
}
|
|
4984
|
+
const proofSlug = proofPath.split("/").pop().replace(/\.proof\.mts$/, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
|
|
4985
|
+
const ws = process.env.NOMOS_WS ?? `${proofSlug}-proof-${Math.random().toString(36).slice(2, 8)}`;
|
|
4986
|
+
if (process.env.NOMOS_WS === void 0) {
|
|
4987
|
+
const cr = await wsCreate2(ws, { via: "root", principal });
|
|
4988
|
+
if (cr !== 0) {
|
|
4989
|
+
return refuse(
|
|
4990
|
+
`proof --live: registered create refused at root \u2014 principal ${principal} lacks creation authority, and the open birth lane would leak an unretirable workspace.
|
|
4991
|
+
remedies:
|
|
4992
|
+
\u2022 ask an operator: githolon grant creation ${principal} (then rerun)
|
|
4993
|
+
\u2022 or pre-create a REGISTERED workspace yourself and hand it over: NOMOS_WS=<ws> githolon proof --live
|
|
4994
|
+
\u2022 or develop offline (no cloud workspace at all): githolon proof`
|
|
4995
|
+
);
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4903
4998
|
const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
|
|
4904
4999
|
stdio: "inherit",
|
|
4905
5000
|
cwd: process.cwd(),
|
|
4906
|
-
env: {
|
|
5001
|
+
env: {
|
|
5002
|
+
...process.env,
|
|
5003
|
+
NOMOS_WS: ws,
|
|
5004
|
+
NOMOS_PRINCIPAL: principal,
|
|
5005
|
+
NOMOS_PROOF_MANAGED: "1",
|
|
5006
|
+
// the proof skips its own (dead) cleanup lane; the CLI retires below
|
|
5007
|
+
...keep ? { PROOF_KEEP: "1" } : {}
|
|
5008
|
+
}
|
|
4907
5009
|
});
|
|
4908
|
-
|
|
5010
|
+
const proofStatus = r.status ?? 1;
|
|
5011
|
+
if (keep) {
|
|
5012
|
+
console.log(`workspace ${ws} KEPT (--keep) \u2014 retire it when done: githolon ws retire ${ws}`);
|
|
5013
|
+
return proofStatus;
|
|
5014
|
+
}
|
|
5015
|
+
const rr = await wsRetire2(ws, { principal }).catch(() => 1);
|
|
5016
|
+
if (rr !== 0) {
|
|
5017
|
+
console.error(`\u26A0 THROWAWAY WORKSPACE NOT RETIRED: ${ws}`);
|
|
5018
|
+
console.error(`\u26A0 retire it manually: githolon ws retire ${ws}`);
|
|
5019
|
+
console.error(`\u26A0 (if it has no root registry row it was born unregistered and needs a custodian sweep)`);
|
|
5020
|
+
}
|
|
5021
|
+
return proofStatus;
|
|
4909
5022
|
}
|
|
4910
5023
|
function parseArgs(argv) {
|
|
4911
5024
|
const [command, ...rest] = argv;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
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",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@bjorn3/browser_wasi_shim": "0.4.2",
|
|
32
|
-
"@githolon/client": "^0.
|
|
33
|
-
"@githolon/dsl": "^0.
|
|
32
|
+
"@githolon/client": "^0.35.0",
|
|
33
|
+
"@githolon/dsl": "^0.35.0",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|