githolon 0.5.1 → 0.7.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 +139 -27
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -460,13 +460,13 @@ async function sha256hex(t) {
|
|
|
460
460
|
function osEntropyBuffer(n) {
|
|
461
461
|
const bytes = new Uint8Array(n * 8);
|
|
462
462
|
for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
|
|
463
|
-
const
|
|
463
|
+
const out10 = new Array(n), T = 2 ** 53;
|
|
464
464
|
for (let i = 0; i < n; i++) {
|
|
465
465
|
let z = 0n;
|
|
466
466
|
for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
|
|
467
|
-
|
|
467
|
+
out10[i] = Number(z >> 11n) / T;
|
|
468
468
|
}
|
|
469
|
-
return
|
|
469
|
+
return out10;
|
|
470
470
|
}
|
|
471
471
|
function stringifyBig(o) {
|
|
472
472
|
return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
|
|
@@ -587,7 +587,7 @@ function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}
|
|
|
587
587
|
writeWork(eng, `envelope-${seq}.json`, enc2.encode(stringifyBig(envelope)));
|
|
588
588
|
const genesis = !controllerHash;
|
|
589
589
|
const defer = opts.deferProjection !== false;
|
|
590
|
-
const res = JSON.parse(call(eng.ex, "author", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: "", domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...defer ? { deferProjection: true } : {} }, eng.STDERR));
|
|
590
|
+
const res = JSON.parse(call(eng.ex, "author", { 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, ...defer ? { deferProjection: true } : {} }, eng.STDERR));
|
|
591
591
|
if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
592
592
|
return res;
|
|
593
593
|
}
|
|
@@ -599,7 +599,7 @@ function applyIntentBytes(eng, ws, bytes) {
|
|
|
599
599
|
function verifyChainLocal(eng, ws) {
|
|
600
600
|
return JSON.parse(call(eng.ex, "verify_chain", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng.STDERR));
|
|
601
601
|
}
|
|
602
|
-
var enc2, dec2, BRANCH, repoArgOf, gitdirOf, unpack, installPayload, qById, query, count, sum;
|
|
602
|
+
var enc2, dec2, BRANCH, repoArgOf, gitdirOf, unpack, installPayload, qById, query, cryptoUnwrapKey, count, sum;
|
|
603
603
|
var init_engine = __esm({
|
|
604
604
|
"vendor/engine/engine.mjs"() {
|
|
605
605
|
"use strict";
|
|
@@ -615,8 +615,9 @@ var init_engine = __esm({
|
|
|
615
615
|
return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
|
|
616
616
|
};
|
|
617
617
|
installPayload = (hash, usda, installedBy, dispositions) => ({ domainHash: hash, packageUsda: usda, installedBy, authorityScope: "workspace/root", dependencies: [], finalizers: [], ...dispositions ? { dispositions } : {} });
|
|
618
|
-
qById = (eng, ws, id) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, branch: BRANCH }, eng.STDERR));
|
|
619
|
-
query = (eng, ws, queryId, paramsJson) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, branch: BRANCH }, eng.STDERR));
|
|
618
|
+
qById = (eng, ws, id, principal = "", keys = []) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, principal, keys, branch: BRANCH }, eng.STDERR));
|
|
619
|
+
query = (eng, ws, queryId, paramsJson, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, principal, keys, branch: BRANCH }, eng.STDERR));
|
|
620
|
+
cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "crypto_unwrap_key", { secret, hpkeEpk, ct }, eng.STDERR)).scopeKey;
|
|
620
621
|
count = (eng, ws, countId, groupKey) => JSON.parse(call(eng.ex, "count", { repoArg: repoArgOf(ws), workspace: ws, countId, groupKey, branch: BRANCH }, eng.STDERR));
|
|
621
622
|
sum = (eng, ws, sumId, groupKey) => JSON.parse(call(eng.ex, "sum", { repoArg: repoArgOf(ws), workspace: ws, sumId, groupKey, branch: BRANCH }, eng.STDERR));
|
|
622
623
|
}
|
|
@@ -644,6 +645,13 @@ async function fetchRuntime(cloud) {
|
|
|
644
645
|
const cache = join4(configDir(), "runtime");
|
|
645
646
|
let wasmBytes;
|
|
646
647
|
const wasmCache = join4(cache, "holon.wasm");
|
|
648
|
+
const localWasm = process.env["NOMOS_LOCAL_WASM"];
|
|
649
|
+
if (localWasm) {
|
|
650
|
+
const bytes = readFileSync2(localWasm);
|
|
651
|
+
const manifests2 = await fetchJsonCached(`${cloud}/v1/runtime/manifests`, join4(cache, "manifests.json"));
|
|
652
|
+
const packages2 = await fetchJsonCached(`${cloud}/v1/runtime/packages`, join4(cache, "packages.json"));
|
|
653
|
+
return { wasmBytes: bytes, manifests: manifests2, packages: packages2 };
|
|
654
|
+
}
|
|
647
655
|
try {
|
|
648
656
|
const r = await fetch(`${cloud}/v1/runtime/holon.wasm`);
|
|
649
657
|
if (!r.ok) throw new Error(`runtime wasm \u2192 ${r.status}`);
|
|
@@ -1997,9 +2005,9 @@ function capJsonStrings(v, max = 2048) {
|
|
|
1997
2005
|
if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
|
|
1998
2006
|
if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
|
|
1999
2007
|
if (typeof v === "object" && v !== null) {
|
|
2000
|
-
const
|
|
2001
|
-
for (const [k, x] of Object.entries(v))
|
|
2002
|
-
return
|
|
2008
|
+
const out10 = {};
|
|
2009
|
+
for (const [k, x] of Object.entries(v)) out10[k] = capJsonStrings(x, max);
|
|
2010
|
+
return out10;
|
|
2003
2011
|
}
|
|
2004
2012
|
return v;
|
|
2005
2013
|
}
|
|
@@ -2161,12 +2169,76 @@ stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below cover
|
|
|
2161
2169
|
return printVerdict(verdict) ? 0 : 1;
|
|
2162
2170
|
}
|
|
2163
2171
|
|
|
2172
|
+
// src/decrypt.ts
|
|
2173
|
+
init_local_holon();
|
|
2174
|
+
init_cloud();
|
|
2175
|
+
init_engine();
|
|
2176
|
+
var SOURCE2 = "source";
|
|
2177
|
+
var out8 = (s) => {
|
|
2178
|
+
process.stdout.write(s + "\n");
|
|
2179
|
+
};
|
|
2180
|
+
var err8 = (s) => {
|
|
2181
|
+
process.stderr.write(`error: ${s}
|
|
2182
|
+
`);
|
|
2183
|
+
};
|
|
2184
|
+
async function decrypt(ws, opts) {
|
|
2185
|
+
const cloud = cloudBase(opts.cloud);
|
|
2186
|
+
if (!opts.as) {
|
|
2187
|
+
err8("--as <principal> is required (who to decrypt as)");
|
|
2188
|
+
return 1;
|
|
2189
|
+
}
|
|
2190
|
+
if (!opts.secret) {
|
|
2191
|
+
err8("--secret <base64 X25519 device secret> is required");
|
|
2192
|
+
return 1;
|
|
2193
|
+
}
|
|
2194
|
+
if (!opts.query) {
|
|
2195
|
+
err8("--query <queryId> is required (the read to decrypt)");
|
|
2196
|
+
return 1;
|
|
2197
|
+
}
|
|
2198
|
+
let eng;
|
|
2199
|
+
try {
|
|
2200
|
+
({ eng } = await holonEngine(cloud, opts.overlay, "githolon-decrypt"));
|
|
2201
|
+
} catch (e) {
|
|
2202
|
+
err8(`cannot boot the local holon: ${String(e.message ?? e)}`);
|
|
2203
|
+
return 1;
|
|
2204
|
+
}
|
|
2205
|
+
const m = await mountWorkspace(eng, SOURCE2, {
|
|
2206
|
+
remote: `${cloud}/v1/workspaces/${ws}/git`,
|
|
2207
|
+
headers: { "x-nomos-principal": opts.as }
|
|
2208
|
+
});
|
|
2209
|
+
if (m.restored !== true) {
|
|
2210
|
+
err8(`workspace '${ws}' has no main on ${cloud}, or its clone was refused for '${opts.as}'` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : ""));
|
|
2211
|
+
return 1;
|
|
2212
|
+
}
|
|
2213
|
+
const wraps = query(eng, SOURCE2, "wrappedKeysByRecipient", JSON.stringify({ recipientPrincipal: opts.as }), opts.as);
|
|
2214
|
+
const keys = [];
|
|
2215
|
+
if (Array.isArray(wraps)) {
|
|
2216
|
+
for (const w of wraps) {
|
|
2217
|
+
const d = w.data ?? {};
|
|
2218
|
+
try {
|
|
2219
|
+
const key = cryptoUnwrapKey(eng, { secret: opts.secret, hpkeEpk: d.hpkeEpk, ct: d.ct });
|
|
2220
|
+
keys.push({ scope: d.scope, epoch: Number(d.epoch), key });
|
|
2221
|
+
} catch {
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
if (opts.json !== true) out8(`# resolved ${keys.length} scope key(s) for '${opts.as}': ${keys.map((k) => `${k.scope}@${k.epoch}`).join(", ") || "(none)"}`);
|
|
2226
|
+
const rows = query(eng, SOURCE2, opts.query, JSON.stringify(opts.params ?? {}), opts.as, keys);
|
|
2227
|
+
if (rows && !Array.isArray(rows) && rows.ok === false) {
|
|
2228
|
+
err8(`the read was refused: ${rows.error}` + (String(rows.error).startsWith("read-forbidden") ? `
|
|
2229
|
+
'${opts.as}' is not granted this scope's read capability` : ""));
|
|
2230
|
+
return 1;
|
|
2231
|
+
}
|
|
2232
|
+
out8(JSON.stringify(rows, null, opts.json === true ? 0 : 2));
|
|
2233
|
+
return 0;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2164
2236
|
// src/status.ts
|
|
2165
2237
|
init_engine();
|
|
2166
2238
|
init_cloud();
|
|
2167
2239
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
2168
|
-
var
|
|
2169
|
-
var
|
|
2240
|
+
var out9 = (s) => void process.stdout.write(s + "\n");
|
|
2241
|
+
var err9 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2170
2242
|
function lawDiffVerdict(localHash, installed, ws) {
|
|
2171
2243
|
const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
|
|
2172
2244
|
const currents = active.filter((d) => d.current === true);
|
|
@@ -2204,7 +2276,7 @@ async function status(wsArg, opts) {
|
|
|
2204
2276
|
try {
|
|
2205
2277
|
ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
|
|
2206
2278
|
} catch (e) {
|
|
2207
|
-
|
|
2279
|
+
err9(e.message);
|
|
2208
2280
|
return 1;
|
|
2209
2281
|
}
|
|
2210
2282
|
let localHash;
|
|
@@ -2216,35 +2288,35 @@ async function status(wsArg, opts) {
|
|
|
2216
2288
|
}
|
|
2217
2289
|
const r = await fetch(`${cloud}/v1/workspaces/${ws}/domains`).catch(() => void 0);
|
|
2218
2290
|
if (r === void 0) {
|
|
2219
|
-
|
|
2291
|
+
err9(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
|
|
2220
2292
|
return 1;
|
|
2221
2293
|
}
|
|
2222
2294
|
const d = await r.json().catch(() => void 0);
|
|
2223
2295
|
if (!r.ok || d?.ok !== true) {
|
|
2224
|
-
|
|
2296
|
+
err9(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
|
|
2225
2297
|
is the workspace born? githolon ws create ${ws}`);
|
|
2226
2298
|
return 1;
|
|
2227
2299
|
}
|
|
2228
|
-
|
|
2300
|
+
out9(`workspace ${ws} on ${cloud}`);
|
|
2229
2301
|
const allDomains = d.domains ?? [];
|
|
2230
2302
|
const controlPlaneHashes = new Set(CONTROL_PLANE_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
|
|
2231
2303
|
const domains = allDomains.filter((dm) => !controlPlaneHashes.has(dm.domainHash));
|
|
2232
2304
|
const controllerCount = allDomains.length - domains.length;
|
|
2233
|
-
if (allDomains.length === 0)
|
|
2305
|
+
if (allDomains.length === 0) out9(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
|
|
2234
2306
|
for (const dom of domains) {
|
|
2235
2307
|
const mark = dom.current === true ? " \u2190 current (the holon resolves dispatch here)" : "";
|
|
2236
|
-
|
|
2308
|
+
out9(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
|
|
2237
2309
|
}
|
|
2238
|
-
if (controllerCount > 0)
|
|
2310
|
+
if (controllerCount > 0) out9(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
|
|
2239
2311
|
const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
|
|
2240
|
-
if (tenantActive > 1)
|
|
2312
|
+
if (tenantActive > 1) out9(` (${tenantActive} active tenant installs \u2014 install-beside keeps every prior deploy Active; the holon serves the current one per domain)`);
|
|
2241
2313
|
if (localHash === void 0) {
|
|
2242
|
-
|
|
2314
|
+
out9(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
|
|
2243
2315
|
return 0;
|
|
2244
2316
|
}
|
|
2245
|
-
|
|
2317
|
+
out9(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
|
|
2246
2318
|
const verdict = lawDiffVerdict(localHash, domains, ws);
|
|
2247
|
-
for (const line of verdict.lines)
|
|
2319
|
+
for (const line of verdict.lines) out9(line);
|
|
2248
2320
|
return verdict.inSync ? 0 : 2;
|
|
2249
2321
|
}
|
|
2250
2322
|
|
|
@@ -2260,6 +2332,8 @@ The inner loop:
|
|
|
2260
2332
|
githolon status [<ws>] local law hash vs the DEPLOYED law, remedy named
|
|
2261
2333
|
githolon replay <ws|dir> the ledger as film: step the chain intent-by-
|
|
2262
2334
|
intent; finale = the verify_chain verdict
|
|
2335
|
+
githolon decrypt <ws> --as <p> --secret <b64> clone + decrypt as a principal: unwrap their scope
|
|
2336
|
+
--query <id> [--param k=v] keys from the chain, read private fields in cleartext
|
|
2263
2337
|
|
|
2264
2338
|
Authoring:
|
|
2265
2339
|
githolon compile [compiler args] compile the package by ./nomos.package.mjs
|
|
@@ -2610,6 +2684,44 @@ ${commandHelp("replay")}`);
|
|
|
2610
2684
|
}
|
|
2611
2685
|
return replay(target, { ...opts, ...step !== void 0 ? { step } : {}, ...json ? { json: true } : {} });
|
|
2612
2686
|
}
|
|
2687
|
+
if (argv[0] === "decrypt") {
|
|
2688
|
+
const rest = argv.slice(1);
|
|
2689
|
+
const pull = (flag) => {
|
|
2690
|
+
const at = rest.indexOf(flag);
|
|
2691
|
+
return at >= 0 ? rest[at + 1] : void 0;
|
|
2692
|
+
};
|
|
2693
|
+
const as = pull("--as"), secret = pull("--secret"), q = pull("--query");
|
|
2694
|
+
const json = rest.includes("--json");
|
|
2695
|
+
const params = {};
|
|
2696
|
+
rest.forEach((a, i) => {
|
|
2697
|
+
if (a === "--param" && rest[i + 1]) {
|
|
2698
|
+
const [k, ...v] = rest[i + 1].split("=");
|
|
2699
|
+
params[k] = v.join("=");
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
2703
|
+
rest.forEach((a, i) => {
|
|
2704
|
+
if (["--as", "--secret", "--query", "--param"].includes(a)) {
|
|
2705
|
+
consumed.add(i);
|
|
2706
|
+
consumed.add(i + 1);
|
|
2707
|
+
} else if (a === "--json") consumed.add(i);
|
|
2708
|
+
});
|
|
2709
|
+
const cleaned = rest.filter((_, i) => !consumed.has(i));
|
|
2710
|
+
const { pos, opts, bad } = cloudArgs(cleaned);
|
|
2711
|
+
if (bad !== void 0) {
|
|
2712
|
+
process.stderr.write(`error: ${bad}
|
|
2713
|
+
|
|
2714
|
+
${commandHelp("decrypt") ?? ""}`);
|
|
2715
|
+
return 1;
|
|
2716
|
+
}
|
|
2717
|
+
const ws = pos[0];
|
|
2718
|
+
if (ws === void 0 || as === void 0 || secret === void 0 || q === void 0) {
|
|
2719
|
+
process.stderr.write(`error: usage: githolon decrypt <ws> --as <principal> --secret <b64> --query <id> [--param k=v ...]
|
|
2720
|
+
`);
|
|
2721
|
+
return 1;
|
|
2722
|
+
}
|
|
2723
|
+
return decrypt(ws, { ...opts, as, secret, query: q, params, ...json ? { json: true } : {} });
|
|
2724
|
+
}
|
|
2613
2725
|
if (argv[0] === "status") {
|
|
2614
2726
|
const { pos, opts, bad } = cloudArgs(argv.slice(1));
|
|
2615
2727
|
if (bad !== void 0) {
|
|
@@ -2682,8 +2794,8 @@ ${USAGE}`);
|
|
|
2682
2794
|
let parsed;
|
|
2683
2795
|
try {
|
|
2684
2796
|
parsed = parseArgs(argv);
|
|
2685
|
-
} catch (
|
|
2686
|
-
process.stderr.write(`error: ${
|
|
2797
|
+
} catch (err10) {
|
|
2798
|
+
process.stderr.write(`error: ${err10.message}
|
|
2687
2799
|
|
|
2688
2800
|
${USAGE}`);
|
|
2689
2801
|
return 1;
|
|
@@ -2702,8 +2814,8 @@ ${USAGE}`);
|
|
|
2702
2814
|
process.stdout.write(`created ${path}
|
|
2703
2815
|
`);
|
|
2704
2816
|
return 0;
|
|
2705
|
-
} catch (
|
|
2706
|
-
process.stderr.write(`error: ${
|
|
2817
|
+
} catch (err10) {
|
|
2818
|
+
process.stderr.write(`error: ${err10.message}
|
|
2707
2819
|
`);
|
|
2708
2820
|
return 1;
|
|
2709
2821
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bjorn3/browser_wasi_shim": "0.4.2",
|
|
30
|
-
"@githolon/dsl": "^0.
|
|
30
|
+
"@githolon/dsl": "^0.7.0",
|
|
31
31
|
"isomorphic-git": "^1.38.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|