githolon 0.16.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 +127 -346
- 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
|
});
|
|
@@ -453,13 +453,13 @@ var init_tree = __esm({
|
|
|
453
453
|
function concatBytes(chunks) {
|
|
454
454
|
let n = 0;
|
|
455
455
|
for (const c of chunks) n += c.length;
|
|
456
|
-
const
|
|
456
|
+
const out9 = new Uint8Array(n);
|
|
457
457
|
let o = 0;
|
|
458
458
|
for (const c of chunks) {
|
|
459
|
-
|
|
459
|
+
out9.set(c, o);
|
|
460
460
|
o += c.length;
|
|
461
461
|
}
|
|
462
|
-
return
|
|
462
|
+
return out9;
|
|
463
463
|
}
|
|
464
464
|
function pkt(payload) {
|
|
465
465
|
const body = typeof payload === "string" ? enc2.encode(payload) : payload;
|
|
@@ -612,13 +612,13 @@ async function sha256hex(t) {
|
|
|
612
612
|
function osEntropyBuffer(n) {
|
|
613
613
|
const bytes = new Uint8Array(n * 8);
|
|
614
614
|
for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
|
|
615
|
-
const
|
|
615
|
+
const out9 = new Array(n), T = 2 ** 53;
|
|
616
616
|
for (let i = 0; i < n; i++) {
|
|
617
617
|
let z = 0n;
|
|
618
618
|
for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
|
|
619
|
-
|
|
619
|
+
out9[i] = Number(z >> 11n) / T;
|
|
620
620
|
}
|
|
621
|
-
return
|
|
621
|
+
return out9;
|
|
622
622
|
}
|
|
623
623
|
function stringifyBig(o) {
|
|
624
624
|
return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
|
|
@@ -771,7 +771,7 @@ function applyIntentBytes(eng, ws, bytes, opts) {
|
|
|
771
771
|
function verifyChainLocal(eng, ws) {
|
|
772
772
|
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng.STDERR));
|
|
773
773
|
}
|
|
774
|
-
var enc3, dec3, BRANCH, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, cryptoUnwrapKey, count, sum;
|
|
774
|
+
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, cryptoUnwrapKey, count, sum;
|
|
775
775
|
var init_engine = __esm({
|
|
776
776
|
"vendor/engine/engine.mjs"() {
|
|
777
777
|
"use strict";
|
|
@@ -781,6 +781,7 @@ var init_engine = __esm({
|
|
|
781
781
|
enc3 = new TextEncoder();
|
|
782
782
|
dec3 = new TextDecoder();
|
|
783
783
|
BRANCH = "main";
|
|
784
|
+
RESULT_PACK_INLINE_MAX = 256 * 1024;
|
|
784
785
|
repoArgOf = (ws) => `/work/ws/${ws}`;
|
|
785
786
|
gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
|
|
786
787
|
unpack = (p) => {
|
|
@@ -918,21 +919,21 @@ function sealedIntentOf(eng, ws, res) {
|
|
|
918
919
|
}
|
|
919
920
|
function printVerdict(v) {
|
|
920
921
|
if (v["valid"] === true) {
|
|
921
|
-
|
|
922
|
-
|
|
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"}`);
|
|
923
924
|
return true;
|
|
924
925
|
}
|
|
925
|
-
|
|
926
|
+
err2(`chain INVALID at ${v["check"]}${v["atIndex"] !== void 0 ? ` (intent ${v["atIndex"]})` : ""}: ${v["error"]}`);
|
|
926
927
|
return false;
|
|
927
928
|
}
|
|
928
|
-
var
|
|
929
|
+
var out2, err2;
|
|
929
930
|
var init_local_holon = __esm({
|
|
930
931
|
"src/local_holon.ts"() {
|
|
931
932
|
"use strict";
|
|
932
933
|
init_engine();
|
|
933
934
|
init_cloud();
|
|
934
|
-
|
|
935
|
-
|
|
935
|
+
out2 = (s) => void process.stdout.write(s + "\n");
|
|
936
|
+
err2 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
936
937
|
}
|
|
937
938
|
});
|
|
938
939
|
|
|
@@ -1081,7 +1082,7 @@ var init_proof_offline = __esm({
|
|
|
1081
1082
|
|
|
1082
1083
|
// src/cli.ts
|
|
1083
1084
|
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
1084
|
-
import { spawnSync as
|
|
1085
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
1085
1086
|
import { createRequire as createRequire2 } from "node:module";
|
|
1086
1087
|
import { dirname as dirname5, join as join10, resolve as resolve2 } from "node:path";
|
|
1087
1088
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
@@ -1264,171 +1265,11 @@ Re-run with --force to replace it.`
|
|
|
1264
1265
|
return { path, contents };
|
|
1265
1266
|
}
|
|
1266
1267
|
|
|
1267
|
-
// src/capability.ts
|
|
1268
|
-
init_cloud();
|
|
1269
|
-
import { spawnSync } from "node:child_process";
|
|
1270
|
-
var out2 = (s) => void process.stdout.write(s + "\n");
|
|
1271
|
-
var err2 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1272
|
-
var CAP_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
1273
|
-
function readStdin() {
|
|
1274
|
-
if (process.stdin.isTTY === true) {
|
|
1275
|
-
process.stderr.write(
|
|
1276
|
-
"paste the credential value, then Ctrl-D (it never rides argv \u2014 ps/history are hosts too):\n"
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
return new Promise((resolveValue) => {
|
|
1280
|
-
let buf = "";
|
|
1281
|
-
process.stdin.setEncoding("utf8");
|
|
1282
|
-
process.stdin.on("data", (chunk) => {
|
|
1283
|
-
buf += chunk;
|
|
1284
|
-
});
|
|
1285
|
-
process.stdin.on("end", () => resolveValue(buf.trim()));
|
|
1286
|
-
process.stdin.resume();
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
function readGcloud(name) {
|
|
1290
|
-
const r = spawnSync("gcloud", ["secrets", "versions", "access", "latest", `--secret=${name}`], {
|
|
1291
|
-
encoding: "utf8",
|
|
1292
|
-
maxBuffer: 1024 * 1024
|
|
1293
|
-
});
|
|
1294
|
-
if (r.error !== void 0 || r.status !== 0) {
|
|
1295
|
-
throw new Error(
|
|
1296
|
-
`gcloud secret read failed (${r.error?.message ?? r.stderr?.trim() ?? `exit ${r.status}`}) \u2014 is gcloud installed + authed, and does secret '${name}' exist?`
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
const v = r.stdout.trim();
|
|
1300
|
-
if (v === "") throw new Error(`gcloud secret '${name}' is empty`);
|
|
1301
|
-
return v;
|
|
1302
|
-
}
|
|
1303
|
-
function parseExpiresAt(s) {
|
|
1304
|
-
const n = /^\d+$/.test(s) ? Number(s) : Date.parse(s);
|
|
1305
|
-
if (!Number.isInteger(n) || n <= 0) throw new Error(`--expires-at '${s}' is neither epoch-ms nor an ISO date`);
|
|
1306
|
-
return n;
|
|
1307
|
-
}
|
|
1308
|
-
async function bindLane(lane, ws, capability, opts) {
|
|
1309
|
-
const cloud = cloudBase(opts.cloud);
|
|
1310
|
-
if (!CAP_NAME.test(capability)) {
|
|
1311
|
-
err2(`capability must be a lowercase law name matching [a-z0-9][a-z0-9._-]{0,63} \u2014 e.g. cf-analytics, openai`);
|
|
1312
|
-
return 1;
|
|
1313
|
-
}
|
|
1314
|
-
const secret = getSecret(cloud, ws);
|
|
1315
|
-
if (secret === void 0) {
|
|
1316
|
-
err2(
|
|
1317
|
-
`no stored secret for ${ws} on ${cloud} \u2014 capability ${lane} is owner-gated
|
|
1318
|
-
githolon secret set ${ws} <secret> (import the workspaceSecret you hold)`
|
|
1319
|
-
);
|
|
1320
|
-
return 1;
|
|
1321
|
-
}
|
|
1322
|
-
let value;
|
|
1323
|
-
try {
|
|
1324
|
-
value = opts.fromGcloud !== void 0 ? readGcloud(opts.fromGcloud) : await readStdin();
|
|
1325
|
-
} catch (e) {
|
|
1326
|
-
err2(e.message);
|
|
1327
|
-
return 1;
|
|
1328
|
-
}
|
|
1329
|
-
if (value === "") {
|
|
1330
|
-
err2("no credential value arrived \u2014 pipe it on stdin, or pass --from-gcloud <secret-name>");
|
|
1331
|
-
return 1;
|
|
1332
|
-
}
|
|
1333
|
-
let expiresAt;
|
|
1334
|
-
try {
|
|
1335
|
-
expiresAt = opts.expiresAt !== void 0 ? parseExpiresAt(opts.expiresAt) : void 0;
|
|
1336
|
-
} catch (e) {
|
|
1337
|
-
err2(e.message);
|
|
1338
|
-
return 1;
|
|
1339
|
-
}
|
|
1340
|
-
const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/${lane}`, {
|
|
1341
|
-
method: "POST",
|
|
1342
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
|
|
1343
|
-
body: JSON.stringify({
|
|
1344
|
-
capability,
|
|
1345
|
-
secret: value,
|
|
1346
|
-
...opts.scope !== void 0 ? { scope: opts.scope } : {},
|
|
1347
|
-
...expiresAt !== void 0 ? { expiresAt } : {}
|
|
1348
|
-
})
|
|
1349
|
-
});
|
|
1350
|
-
const d = await r.json().catch(() => void 0);
|
|
1351
|
-
if (r.status === 401) {
|
|
1352
|
-
err2(`${lane} refused (401) \u2014 the stored secret for ${ws} is not the workspace's. githolon secret set ${ws} <secret>`);
|
|
1353
|
-
return 1;
|
|
1354
|
-
}
|
|
1355
|
-
if (!r.ok || d?.ok !== true) {
|
|
1356
|
-
err2(`capability ${lane} '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
1357
|
-
return 1;
|
|
1358
|
-
}
|
|
1359
|
-
out2(`\u2713 ${capability} ${lane === "rotate" ? "rotated" : "bound"} on ${ws} (epoch ${d.epoch})`);
|
|
1360
|
-
out2(` the VALUE lives host-side only (the workspace's holon) \u2014 it never enters the ledger`);
|
|
1361
|
-
out2(` the law fact: keyHash ${(d.keyHash ?? "").slice(0, 16)}\u2026 status ${d.status}`);
|
|
1362
|
-
return 0;
|
|
1363
|
-
}
|
|
1364
|
-
async function capabilityBind(ws, capability, opts) {
|
|
1365
|
-
return bindLane("bind", ws, capability, opts);
|
|
1366
|
-
}
|
|
1367
|
-
async function capabilityRotate(ws, capability, opts) {
|
|
1368
|
-
return bindLane("rotate", ws, capability, opts);
|
|
1369
|
-
}
|
|
1370
|
-
async function capabilityList(ws, opts) {
|
|
1371
|
-
const cloud = cloudBase(opts.cloud);
|
|
1372
|
-
const secret = getSecret(cloud, ws);
|
|
1373
|
-
if (secret === void 0) {
|
|
1374
|
-
err2(`no stored secret for ${ws} on ${cloud} \u2014 the binding facts are owner-gated
|
|
1375
|
-
githolon secret set ${ws} <secret>`);
|
|
1376
|
-
return 1;
|
|
1377
|
-
}
|
|
1378
|
-
const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities`, {
|
|
1379
|
-
headers: { authorization: `Bearer ${secret}` }
|
|
1380
|
-
});
|
|
1381
|
-
const d = await r.json().catch(() => void 0);
|
|
1382
|
-
if (!r.ok || d?.ok !== true) {
|
|
1383
|
-
err2(`capability list '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
1384
|
-
return 1;
|
|
1385
|
-
}
|
|
1386
|
-
const bindings = d.bindings ?? [];
|
|
1387
|
-
if (bindings.length === 0) {
|
|
1388
|
-
out2(`no capability bindings on ${ws} \u2014 bind one: githolon capability bind ${ws} <capability>`);
|
|
1389
|
-
return 0;
|
|
1390
|
-
}
|
|
1391
|
-
for (const b of bindings) {
|
|
1392
|
-
const bits = [
|
|
1393
|
-
`epoch ${b.epoch}`,
|
|
1394
|
-
`${b.status}`,
|
|
1395
|
-
`keyHash ${(b.keyHash ?? "").slice(0, 16)}\u2026`,
|
|
1396
|
-
...b.scope !== void 0 ? [`scope ${b.scope}`] : [],
|
|
1397
|
-
...b.expiresAt !== void 0 ? [`expires ${new Date(b.expiresAt).toISOString()}`] : [],
|
|
1398
|
-
...b.held !== void 0 ? [b.held ? "value held" : "VALUE MISSING (rotate to restore)"] : []
|
|
1399
|
-
];
|
|
1400
|
-
out2(`${b.capability} ${bits.join(" \xB7 ")}`);
|
|
1401
|
-
}
|
|
1402
|
-
out2(`(facts about authority \u2014 the values live host-side and are never listed)`);
|
|
1403
|
-
return 0;
|
|
1404
|
-
}
|
|
1405
|
-
async function capabilityUnbind(ws, capability, opts) {
|
|
1406
|
-
const cloud = cloudBase(opts.cloud);
|
|
1407
|
-
const secret = getSecret(cloud, ws);
|
|
1408
|
-
if (secret === void 0) {
|
|
1409
|
-
err2(`no stored secret for ${ws} on ${cloud} \u2014 unbind is owner-gated
|
|
1410
|
-
githolon secret set ${ws} <secret>`);
|
|
1411
|
-
return 1;
|
|
1412
|
-
}
|
|
1413
|
-
const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/unbind`, {
|
|
1414
|
-
method: "POST",
|
|
1415
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
|
|
1416
|
-
body: JSON.stringify({ capability })
|
|
1417
|
-
});
|
|
1418
|
-
const d = await r.json().catch(() => void 0);
|
|
1419
|
-
if (!r.ok || d?.ok !== true) {
|
|
1420
|
-
err2(`capability unbind '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
1421
|
-
return 1;
|
|
1422
|
-
}
|
|
1423
|
-
out2(`\u2713 ${capability} unbound on ${ws} \u2014 the law fact is revoked (the record stays) and the host forgot the value`);
|
|
1424
|
-
return 0;
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
1268
|
// src/cli.ts
|
|
1428
1269
|
init_cloud();
|
|
1429
1270
|
|
|
1430
1271
|
// src/compile.ts
|
|
1431
|
-
import { spawn, spawnSync
|
|
1272
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
1432
1273
|
import { createRequire } from "node:module";
|
|
1433
1274
|
import { dirname, join as join3 } from "node:path";
|
|
1434
1275
|
import { pathToFileURL } from "node:url";
|
|
@@ -1452,7 +1293,7 @@ function runCompile(args) {
|
|
|
1452
1293
|
`);
|
|
1453
1294
|
return 1;
|
|
1454
1295
|
}
|
|
1455
|
-
const r =
|
|
1296
|
+
const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
|
|
1456
1297
|
return r.status ?? 1;
|
|
1457
1298
|
}
|
|
1458
1299
|
function compileAsync(cwd, args = []) {
|
|
@@ -1571,8 +1412,8 @@ async function resolveWorkspace(explicit, cwd, target, usageHint) {
|
|
|
1571
1412
|
|
|
1572
1413
|
// src/dev.ts
|
|
1573
1414
|
init_proof_offline();
|
|
1574
|
-
var
|
|
1575
|
-
var
|
|
1415
|
+
var out3 = (s) => void process.stdout.write(s + "\n");
|
|
1416
|
+
var err3 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1576
1417
|
var WS = "dev";
|
|
1577
1418
|
var DEFAULT_PORT = 7700;
|
|
1578
1419
|
var DEBOUNCE_MS = 200;
|
|
@@ -1695,21 +1536,21 @@ async function dev(opts) {
|
|
|
1695
1536
|
const cwd = process.cwd();
|
|
1696
1537
|
const cloud = cloudBase(opts.cloud);
|
|
1697
1538
|
const project = await loadProject(cwd).catch((e) => {
|
|
1698
|
-
|
|
1539
|
+
err3(e.message);
|
|
1699
1540
|
return void 0;
|
|
1700
1541
|
});
|
|
1701
1542
|
if (project === void 0) {
|
|
1702
1543
|
if (!existsSync6(join7(cwd, CONFIG_FILE))) {
|
|
1703
|
-
|
|
1544
|
+
err3(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
|
|
1704
1545
|
}
|
|
1705
1546
|
return 1;
|
|
1706
1547
|
}
|
|
1707
1548
|
const creds = loadCreds();
|
|
1708
1549
|
const installedBy = creds.session?.uid ?? creds.principal ?? "githolon-dev";
|
|
1709
|
-
|
|
1550
|
+
out3(`githolon dev \u2014 ${project.name}: compiling\u2026`);
|
|
1710
1551
|
const firstCompile = await compileAsync(cwd);
|
|
1711
1552
|
if (!firstCompile.ok) {
|
|
1712
|
-
|
|
1553
|
+
err3(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
|
|
1713
1554
|
${firstCompile.output}`);
|
|
1714
1555
|
return 1;
|
|
1715
1556
|
}
|
|
@@ -1717,14 +1558,14 @@ ${firstCompile.output}`);
|
|
|
1717
1558
|
try {
|
|
1718
1559
|
deployBody = readDeployBody(cwd, project.name);
|
|
1719
1560
|
} catch (e) {
|
|
1720
|
-
|
|
1561
|
+
err3(e.message);
|
|
1721
1562
|
return 1;
|
|
1722
1563
|
}
|
|
1723
1564
|
let engBoot;
|
|
1724
1565
|
try {
|
|
1725
1566
|
engBoot = await holonEngine(cloud, deployBody, installedBy);
|
|
1726
1567
|
} catch (e) {
|
|
1727
|
-
|
|
1568
|
+
err3(`cannot boot the local holon: ${String(e.message ?? e)}
|
|
1728
1569
|
the runtime rides ${cloud}/v1/runtime/* and is cached in ~/.holon/runtime after one fetch \u2014 connect once, then dev runs offline`);
|
|
1729
1570
|
return 1;
|
|
1730
1571
|
}
|
|
@@ -1747,19 +1588,19 @@ ${firstCompile.output}`);
|
|
|
1747
1588
|
s.intents++;
|
|
1748
1589
|
const installed = await installLaw(s, deployBody, installedBy);
|
|
1749
1590
|
if (!installed.ok) {
|
|
1750
|
-
|
|
1591
|
+
err3(installed.error ?? "law install failed");
|
|
1751
1592
|
return 1;
|
|
1752
1593
|
}
|
|
1753
1594
|
s.proof = await runProofCycle(s, cwd, deployBody, installedBy);
|
|
1754
1595
|
if (opts.once === true) {
|
|
1755
|
-
|
|
1596
|
+
out3(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
|
|
1756
1597
|
return s.proof === void 0 || s.proof.ok ? 0 : 1;
|
|
1757
1598
|
}
|
|
1758
1599
|
let server;
|
|
1759
1600
|
try {
|
|
1760
1601
|
server = await startServer(s);
|
|
1761
1602
|
} catch (e) {
|
|
1762
|
-
|
|
1603
|
+
err3(e.message);
|
|
1763
1604
|
return 1;
|
|
1764
1605
|
}
|
|
1765
1606
|
const moduleDirs = /* @__PURE__ */ new Set();
|
|
@@ -1787,7 +1628,7 @@ ${firstCompile.output}`);
|
|
|
1787
1628
|
s.compileMs = run.ms;
|
|
1788
1629
|
if (!run.ok) {
|
|
1789
1630
|
s.lastError = run.output.trim() || "compile failed";
|
|
1790
|
-
|
|
1631
|
+
out3(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
|
|
1791
1632
|
${run.output}`);
|
|
1792
1633
|
} else {
|
|
1793
1634
|
s.lastError = void 0;
|
|
@@ -1796,16 +1637,16 @@ ${run.output}`);
|
|
|
1796
1637
|
const inst = await installLaw(s, body, installedBy);
|
|
1797
1638
|
if (!inst.ok) {
|
|
1798
1639
|
s.lastError = inst.error ?? "install failed";
|
|
1799
|
-
|
|
1640
|
+
out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
|
|
1800
1641
|
} else {
|
|
1801
1642
|
s.proof = await runProofCycle(s, cwd, body, installedBy);
|
|
1802
1643
|
const total = performance.now() - t0;
|
|
1803
|
-
|
|
1804
|
-
|
|
1644
|
+
out3(statusScreen(s));
|
|
1645
|
+
out3(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
|
|
1805
1646
|
}
|
|
1806
1647
|
} catch (e) {
|
|
1807
1648
|
s.lastError = String(e.message ?? e);
|
|
1808
|
-
|
|
1649
|
+
out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
|
|
1809
1650
|
}
|
|
1810
1651
|
}
|
|
1811
1652
|
cycling = false;
|
|
@@ -1831,8 +1672,8 @@ ${run.output}`);
|
|
|
1831
1672
|
})
|
|
1832
1673
|
);
|
|
1833
1674
|
s.watchRoots.push(CONFIG_FILE);
|
|
1834
|
-
|
|
1835
|
-
|
|
1675
|
+
out3(statusScreen(s));
|
|
1676
|
+
out3(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
|
|
1836
1677
|
return new Promise((resolveExit) => {
|
|
1837
1678
|
const stop = () => {
|
|
1838
1679
|
for (const w of watchers) w.close();
|
|
@@ -1846,13 +1687,13 @@ ${run.output}`);
|
|
|
1846
1687
|
|
|
1847
1688
|
// src/git.ts
|
|
1848
1689
|
init_cloud();
|
|
1849
|
-
import { spawnSync as
|
|
1690
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1850
1691
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1851
1692
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1852
|
-
var
|
|
1853
|
-
var
|
|
1693
|
+
var out4 = (s) => void process.stdout.write(s + "\n");
|
|
1694
|
+
var err4 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1854
1695
|
function git(args, opts = {}) {
|
|
1855
|
-
const r =
|
|
1696
|
+
const r = spawnSync2("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
|
|
1856
1697
|
return r.status ?? 1;
|
|
1857
1698
|
}
|
|
1858
1699
|
function ensureSecret(cloud, ws) {
|
|
@@ -1872,7 +1713,7 @@ function parseCredentialInput(input) {
|
|
|
1872
1713
|
return kv;
|
|
1873
1714
|
}
|
|
1874
1715
|
function workspaceFromPath(path) {
|
|
1875
|
-
const m = (path ?? "").match(/^
|
|
1716
|
+
const m = (path ?? "").match(/^v[12]\/workspaces\/([^/]+)\/git(\/|$)/);
|
|
1876
1717
|
return m?.[1];
|
|
1877
1718
|
}
|
|
1878
1719
|
async function gitCredential(action) {
|
|
@@ -1882,40 +1723,40 @@ async function gitCredential(action) {
|
|
|
1882
1723
|
if (kv["protocol"] !== "https" || ws === void 0) return 0;
|
|
1883
1724
|
const cloud = `https://${kv["host"]}`;
|
|
1884
1725
|
const token = await sessionToken().catch(() => void 0);
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1726
|
+
out4(`username=${token ?? "nomos"}`);
|
|
1727
|
+
out4(`password=${ensureSecret(cloud, ws)}`);
|
|
1728
|
+
out4("");
|
|
1888
1729
|
return 0;
|
|
1889
1730
|
}
|
|
1890
1731
|
async function gitSetup(opts) {
|
|
1891
1732
|
const cloud = cloudBase(opts.cloud);
|
|
1892
1733
|
const self = process.argv[1];
|
|
1893
1734
|
if (self === void 0) {
|
|
1894
|
-
|
|
1735
|
+
err4("cannot locate the githolon CLI entry to install as a credential helper");
|
|
1895
1736
|
return 1;
|
|
1896
1737
|
}
|
|
1897
1738
|
const helper = `!"${process.execPath}" "${self}" git-credential`;
|
|
1898
1739
|
if (git(["config", "--global", `credential.${cloud}.helper`, helper]) !== 0 || git(["config", "--global", `credential.${cloud}.useHttpPath`, "true"]) !== 0) {
|
|
1899
|
-
|
|
1740
|
+
err4("git config failed \u2014 is git installed?");
|
|
1900
1741
|
return 1;
|
|
1901
1742
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
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`);
|
|
1904
1745
|
return 0;
|
|
1905
1746
|
}
|
|
1906
1747
|
async function gitRemote(ws, name, opts) {
|
|
1907
1748
|
const cloud = cloudBase(opts.cloud);
|
|
1908
1749
|
if (git(["rev-parse", "--git-dir"], { quiet: true }) !== 0) {
|
|
1909
|
-
|
|
1750
|
+
err4("not a git repository \u2014 run inside your repo (git init first)");
|
|
1910
1751
|
return 1;
|
|
1911
1752
|
}
|
|
1912
1753
|
const setupRc = await gitSetup(opts);
|
|
1913
1754
|
if (setupRc !== 0) return setupRc;
|
|
1914
1755
|
ensureSecret(cloud, ws);
|
|
1915
|
-
const url = `${cloud}/
|
|
1756
|
+
const url = `${cloud}/v2/workspaces/${ws}/git`;
|
|
1916
1757
|
if (git(["remote", "add", name, url], { quiet: true }) !== 0) {
|
|
1917
1758
|
if (git(["remote", "set-url", name, url], { quiet: true }) !== 0) {
|
|
1918
|
-
|
|
1759
|
+
err4(`could not add or update remote '${name}'`);
|
|
1919
1760
|
return 1;
|
|
1920
1761
|
}
|
|
1921
1762
|
}
|
|
@@ -1923,7 +1764,7 @@ async function gitRemote(ws, name, opts) {
|
|
|
1923
1764
|
if (token === void 0) {
|
|
1924
1765
|
const principal = loadCreds().principal;
|
|
1925
1766
|
if (principal === void 0) {
|
|
1926
|
-
|
|
1767
|
+
err4(
|
|
1927
1768
|
`remote '${name}' \u2192 ${url} is wired, but a BIRTH push needs a principal:
|
|
1928
1769
|
githolon login --agent (then just push)
|
|
1929
1770
|
\u2014 or re-run after githolon ws create/login so a principal is on file`
|
|
@@ -1931,13 +1772,13 @@ async function gitRemote(ws, name, opts) {
|
|
|
1931
1772
|
return 1;
|
|
1932
1773
|
}
|
|
1933
1774
|
if (git(["config", `http.${url}.extraHeader`, `x-nomos-principal: ${principal}`]) !== 0) {
|
|
1934
|
-
|
|
1775
|
+
err4("git config failed setting the principal header");
|
|
1935
1776
|
return 1;
|
|
1936
1777
|
}
|
|
1937
1778
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
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`);
|
|
1941
1782
|
return 0;
|
|
1942
1783
|
}
|
|
1943
1784
|
|
|
@@ -2041,22 +1882,6 @@ var HELP = {
|
|
|
2041
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).",
|
|
2042
1883
|
examples: ["githolon secret set my-ws nws_v1_\u2026", "githolon secret rotate my-ws"]
|
|
2043
1884
|
},
|
|
2044
|
-
capability: {
|
|
2045
|
-
usage: "githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws> [--from-gcloud <name>] [--scope <s>] [--expires-at <iso|ms>] [--cloud <url>]",
|
|
2046
|
-
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.",
|
|
2047
|
-
flags: [
|
|
2048
|
-
["--from-gcloud <name>", "read the value from `gcloud secrets versions access latest --secret=<name>`"],
|
|
2049
|
-
["--scope <s>", "a narrowing label recorded on the law fact (e.g. read-only)"],
|
|
2050
|
-
["--expires-at <t>", "expiry recorded on the law fact (ISO date/datetime or epoch-ms; fail-closed at the host)"],
|
|
2051
|
-
["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
|
|
2052
|
-
],
|
|
2053
|
-
examples: [
|
|
2054
|
-
"cat token.txt | githolon capability bind nomos-usage cf-analytics",
|
|
2055
|
-
"githolon capability bind my-ws openai --from-gcloud openai-api-key --scope read-only",
|
|
2056
|
-
"githolon capability list nomos-usage",
|
|
2057
|
-
"githolon capability rotate nomos-usage cf-analytics --from-gcloud cf-analytics-token"
|
|
2058
|
-
]
|
|
2059
|
-
},
|
|
2060
1885
|
git: {
|
|
2061
1886
|
usage: "githolon git <setup | remote [<ws>] [<remoteName>]> [--cloud <url>] [--target <name>]",
|
|
2062
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.",
|
|
@@ -2088,23 +1913,23 @@ function commandHelp(cmd) {
|
|
|
2088
1913
|
init_engine();
|
|
2089
1914
|
init_cloud();
|
|
2090
1915
|
init_local_holon();
|
|
2091
|
-
import { spawnSync as
|
|
1916
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2092
1917
|
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2093
1918
|
import { join as join8 } from "node:path";
|
|
2094
|
-
var
|
|
2095
|
-
var
|
|
1919
|
+
var out5 = (s) => void process.stdout.write(s + "\n");
|
|
1920
|
+
var err5 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2096
1921
|
var WS2 = "local";
|
|
2097
1922
|
async function ledgerInit(dir, opts) {
|
|
2098
1923
|
const cloud = cloudBase(opts.cloud);
|
|
2099
1924
|
if (existsSync7(dir) && readdirSync3(dir).length > 0) {
|
|
2100
|
-
|
|
1925
|
+
err5(`refusing to mint into non-empty directory: ${dir}`);
|
|
2101
1926
|
return 1;
|
|
2102
1927
|
}
|
|
2103
1928
|
let deployFile;
|
|
2104
1929
|
try {
|
|
2105
1930
|
deployFile = discoverDeployJson(process.cwd(), opts.file);
|
|
2106
1931
|
} catch (e) {
|
|
2107
|
-
|
|
1932
|
+
err5(e.message);
|
|
2108
1933
|
return 1;
|
|
2109
1934
|
}
|
|
2110
1935
|
const deployBody = JSON.parse(readFileSync6(deployFile, "utf8"));
|
|
@@ -2112,10 +1937,10 @@ async function ledgerInit(dir, opts) {
|
|
|
2112
1937
|
const c = loadCreds();
|
|
2113
1938
|
const principal = await sessionToken().catch(() => void 0) !== void 0 ? c.session.uid : c.principal;
|
|
2114
1939
|
if (principal === void 0) {
|
|
2115
|
-
|
|
1940
|
+
err5("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
|
|
2116
1941
|
return 1;
|
|
2117
1942
|
}
|
|
2118
|
-
|
|
1943
|
+
out5(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
|
|
2119
1944
|
const { eng } = await holonEngine(cloud, deployBody, principal);
|
|
2120
1945
|
await mountFresh(eng, WS2);
|
|
2121
1946
|
author(eng, WS2, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, principal), "");
|
|
@@ -2127,17 +1952,17 @@ async function ledgerInit(dir, opts) {
|
|
|
2127
1952
|
writeTreeToDisk(gitTree, gitDir);
|
|
2128
1953
|
const cfgPath = join8(gitDir, "config");
|
|
2129
1954
|
writeFileSync4(cfgPath, readFileSync6(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
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(`
|
|
2133
1958
|
Birth it on Nomos Cloud (the cloud re-derives this exact verdict):`);
|
|
2134
|
-
|
|
1959
|
+
out5(` cd ${dir} && npx githolon git remote <ws> && git push nomos main`);
|
|
2135
1960
|
return 0;
|
|
2136
1961
|
}
|
|
2137
1962
|
async function ledgerVerify(dir, opts) {
|
|
2138
1963
|
const gitDir = existsSync7(join8(dir, ".git")) ? join8(dir, ".git") : dir;
|
|
2139
1964
|
if (!existsSync7(join8(gitDir, "HEAD"))) {
|
|
2140
|
-
|
|
1965
|
+
err5(`${dir} is not a git repo (no HEAD)`);
|
|
2141
1966
|
return 1;
|
|
2142
1967
|
}
|
|
2143
1968
|
const { eng } = await holonEngine(cloudBase(opts.cloud), void 0, "verify");
|
|
@@ -2155,8 +1980,8 @@ init_local_holon();
|
|
|
2155
1980
|
import { existsSync as existsSync8 } from "node:fs";
|
|
2156
1981
|
import { join as join9 } from "node:path";
|
|
2157
1982
|
import git2 from "isomorphic-git";
|
|
2158
|
-
var
|
|
2159
|
-
var
|
|
1983
|
+
var out6 = (s) => void process.stdout.write(s + "\n");
|
|
1984
|
+
var err6 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2160
1985
|
var SOURCE = "source";
|
|
2161
1986
|
var REPLAY = "replay";
|
|
2162
1987
|
function summarizePayload(payload, max = 100) {
|
|
@@ -2192,9 +2017,9 @@ function capJsonStrings(v, max = 2048) {
|
|
|
2192
2017
|
if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
|
|
2193
2018
|
if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
|
|
2194
2019
|
if (typeof v === "object" && v !== null) {
|
|
2195
|
-
const
|
|
2196
|
-
for (const [k, x] of Object.entries(v))
|
|
2197
|
-
return
|
|
2020
|
+
const out9 = {};
|
|
2021
|
+
for (const [k, x] of Object.entries(v)) out9[k] = capJsonStrings(x, max);
|
|
2022
|
+
return out9;
|
|
2198
2023
|
}
|
|
2199
2024
|
return v;
|
|
2200
2025
|
}
|
|
@@ -2241,12 +2066,12 @@ function readKey() {
|
|
|
2241
2066
|
async function replay(target, opts) {
|
|
2242
2067
|
const cloud = cloudBase(opts.cloud);
|
|
2243
2068
|
const json = opts.json === true;
|
|
2244
|
-
const emit = (o) =>
|
|
2069
|
+
const emit = (o) => out6(JSON.stringify(o));
|
|
2245
2070
|
let eng;
|
|
2246
2071
|
try {
|
|
2247
2072
|
({ eng } = await holonEngine(cloud, void 0, "githolon-replay"));
|
|
2248
2073
|
} catch (e) {
|
|
2249
|
-
|
|
2074
|
+
err6(`cannot boot the local holon: ${String(e.message ?? e)}
|
|
2250
2075
|
the runtime rides ${cloud}/v1/runtime/* (cached in ~/.holon/runtime after one fetch)`);
|
|
2251
2076
|
return 1;
|
|
2252
2077
|
}
|
|
@@ -2254,15 +2079,15 @@ async function replay(target, opts) {
|
|
|
2254
2079
|
if (isDir) {
|
|
2255
2080
|
const gitDir = existsSync8(join9(target, ".git")) ? join9(target, ".git") : target;
|
|
2256
2081
|
if (!existsSync8(join9(gitDir, "HEAD"))) {
|
|
2257
|
-
|
|
2082
|
+
err6(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
|
|
2258
2083
|
return 1;
|
|
2259
2084
|
}
|
|
2260
2085
|
await mountFresh(eng, SOURCE);
|
|
2261
2086
|
eng.preopen.dir.contents.get("ws").contents.get(SOURCE).contents.set("nomos.git", readTreeFromDisk(gitDir));
|
|
2262
2087
|
} else {
|
|
2263
|
-
const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/
|
|
2088
|
+
const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/v2/workspaces/${target}/git`, headers: {} });
|
|
2264
2089
|
if (m.restored !== true) {
|
|
2265
|
-
|
|
2090
|
+
err6(
|
|
2266
2091
|
`workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to replay` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "") + `
|
|
2267
2092
|
check the name (githolon ws status ${target}), or pass a local holon directory instead`
|
|
2268
2093
|
);
|
|
@@ -2271,7 +2096,7 @@ async function replay(target, opts) {
|
|
|
2271
2096
|
}
|
|
2272
2097
|
const chain = await walkChain(eng, SOURCE);
|
|
2273
2098
|
if (chain.length === 0) {
|
|
2274
|
-
|
|
2099
|
+
err6(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
|
|
2275
2100
|
return 1;
|
|
2276
2101
|
}
|
|
2277
2102
|
await mountFresh(eng, REPLAY);
|
|
@@ -2280,8 +2105,8 @@ async function replay(target, opts) {
|
|
|
2280
2105
|
const tallies = /* @__PURE__ */ new Map();
|
|
2281
2106
|
let autoRun = !interactive;
|
|
2282
2107
|
if (!json) {
|
|
2283
|
-
|
|
2284
|
-
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");
|
|
2285
2110
|
}
|
|
2286
2111
|
for (let i = 0; i < chain.length; i++) {
|
|
2287
2112
|
const { oid, bytes, doc } = chain[i];
|
|
@@ -2295,8 +2120,8 @@ async function replay(target, opts) {
|
|
|
2295
2120
|
if (res.ok === false) {
|
|
2296
2121
|
const msg = `replay REFUSED at intent ${i} (${domain}/${directive}, ${oid.slice(0, 10)}): ${res.error ?? "unknown"}`;
|
|
2297
2122
|
if (json) emit({ step: i, oid, domain, directive, refused: true, error: res.error ?? "unknown" });
|
|
2298
|
-
else
|
|
2299
|
-
|
|
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)");
|
|
2300
2125
|
printVerdict(verifyChainLocal(eng, SOURCE));
|
|
2301
2126
|
return 1;
|
|
2302
2127
|
}
|
|
@@ -2317,31 +2142,31 @@ async function replay(target, opts) {
|
|
|
2317
2142
|
tallies: Object.fromEntries(tallies)
|
|
2318
2143
|
});
|
|
2319
2144
|
} else {
|
|
2320
|
-
|
|
2321
|
-
|
|
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)}`);
|
|
2322
2147
|
for (const ev of doc.events ?? []) {
|
|
2323
2148
|
const id = ev.aggregate ?? "?";
|
|
2324
2149
|
const was = before.get(id);
|
|
2325
2150
|
const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" : was === void 0 ? "~ touched" : "~ updated";
|
|
2326
|
-
|
|
2151
|
+
out6(` ${verb} ${id}`);
|
|
2327
2152
|
const ops = summarizeOps(ev);
|
|
2328
|
-
if (ops.length > 0)
|
|
2153
|
+
if (ops.length > 0) out6(` ${ops}`);
|
|
2329
2154
|
}
|
|
2330
2155
|
const tallyLine = [...tallies.entries()].map(([t, n]) => `${t}:${n}`).join(" ");
|
|
2331
2156
|
const showTally = stepEvery > 0 ? (i + 1) % stepEvery === 0 || i === chain.length - 1 : true;
|
|
2332
|
-
if (showTally && tallyLine.length > 0)
|
|
2157
|
+
if (showTally && tallyLine.length > 0) out6(` rows ${tallyLine}`);
|
|
2333
2158
|
}
|
|
2334
2159
|
if (interactive && !autoRun && i < chain.length - 1) {
|
|
2335
2160
|
const key = await readKey();
|
|
2336
2161
|
if (key === "quit") {
|
|
2337
|
-
|
|
2162
|
+
out6(`
|
|
2338
2163
|
stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below covers the WHOLE chain as delivered`);
|
|
2339
2164
|
break;
|
|
2340
2165
|
}
|
|
2341
2166
|
if (key === "all") autoRun = true;
|
|
2342
2167
|
}
|
|
2343
2168
|
}
|
|
2344
|
-
if (!json)
|
|
2169
|
+
if (!json) out6("");
|
|
2345
2170
|
const verdict = verifyChainLocal(eng, SOURCE);
|
|
2346
2171
|
if (json) {
|
|
2347
2172
|
emit({ finale: true, verdict });
|
|
@@ -2355,40 +2180,40 @@ init_local_holon();
|
|
|
2355
2180
|
init_cloud();
|
|
2356
2181
|
init_engine();
|
|
2357
2182
|
var SOURCE2 = "source";
|
|
2358
|
-
var
|
|
2183
|
+
var out7 = (s) => {
|
|
2359
2184
|
process.stdout.write(s + "\n");
|
|
2360
2185
|
};
|
|
2361
|
-
var
|
|
2186
|
+
var err7 = (s) => {
|
|
2362
2187
|
process.stderr.write(`error: ${s}
|
|
2363
2188
|
`);
|
|
2364
2189
|
};
|
|
2365
2190
|
async function decrypt(ws, opts) {
|
|
2366
2191
|
const cloud = cloudBase(opts.cloud);
|
|
2367
2192
|
if (!opts.as) {
|
|
2368
|
-
|
|
2193
|
+
err7("--as <principal> is required (who to decrypt as)");
|
|
2369
2194
|
return 1;
|
|
2370
2195
|
}
|
|
2371
2196
|
if (!opts.secret) {
|
|
2372
|
-
|
|
2197
|
+
err7("--secret <base64 X25519 device secret> is required");
|
|
2373
2198
|
return 1;
|
|
2374
2199
|
}
|
|
2375
2200
|
if (!opts.query) {
|
|
2376
|
-
|
|
2201
|
+
err7("--query <queryId> is required (the read to decrypt)");
|
|
2377
2202
|
return 1;
|
|
2378
2203
|
}
|
|
2379
2204
|
let eng;
|
|
2380
2205
|
try {
|
|
2381
2206
|
({ eng } = await holonEngine(cloud, opts.overlay, "githolon-decrypt"));
|
|
2382
2207
|
} catch (e) {
|
|
2383
|
-
|
|
2208
|
+
err7(`cannot boot the local holon: ${String(e.message ?? e)}`);
|
|
2384
2209
|
return 1;
|
|
2385
2210
|
}
|
|
2386
2211
|
const m = await mountWorkspace(eng, SOURCE2, {
|
|
2387
|
-
remote: `${cloud}/
|
|
2212
|
+
remote: `${cloud}/v2/workspaces/${ws}/git`,
|
|
2388
2213
|
headers: { "x-nomos-principal": opts.as }
|
|
2389
2214
|
});
|
|
2390
2215
|
if (m.restored !== true) {
|
|
2391
|
-
|
|
2216
|
+
err7(`workspace '${ws}' has no main on ${cloud}, or its clone was refused for '${opts.as}'` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : ""));
|
|
2392
2217
|
return 1;
|
|
2393
2218
|
}
|
|
2394
2219
|
const wraps = query(eng, SOURCE2, "wrappedKeysByRecipient", JSON.stringify({ recipientPrincipal: opts.as }), opts.as);
|
|
@@ -2403,14 +2228,14 @@ async function decrypt(ws, opts) {
|
|
|
2403
2228
|
}
|
|
2404
2229
|
}
|
|
2405
2230
|
}
|
|
2406
|
-
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)"}`);
|
|
2407
2232
|
const rows = query(eng, SOURCE2, opts.query, JSON.stringify(opts.params ?? {}), opts.as, keys);
|
|
2408
2233
|
if (rows && !Array.isArray(rows) && rows.ok === false) {
|
|
2409
|
-
|
|
2234
|
+
err7(`the read was refused: ${rows.error}` + (String(rows.error).startsWith("read-forbidden") ? `
|
|
2410
2235
|
'${opts.as}' is not granted this scope's read capability` : ""));
|
|
2411
2236
|
return 1;
|
|
2412
2237
|
}
|
|
2413
|
-
|
|
2238
|
+
out7(JSON.stringify(rows, null, opts.json === true ? 0 : 2));
|
|
2414
2239
|
return 0;
|
|
2415
2240
|
}
|
|
2416
2241
|
|
|
@@ -2418,8 +2243,8 @@ async function decrypt(ws, opts) {
|
|
|
2418
2243
|
init_engine();
|
|
2419
2244
|
init_cloud();
|
|
2420
2245
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
2421
|
-
var
|
|
2422
|
-
var
|
|
2246
|
+
var out8 = (s) => void process.stdout.write(s + "\n");
|
|
2247
|
+
var err8 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2423
2248
|
function lawDiffVerdict(localHash, installed, ws) {
|
|
2424
2249
|
const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
|
|
2425
2250
|
const currents = active.filter((d) => d.current === true);
|
|
@@ -2457,7 +2282,7 @@ async function status(wsArg, opts) {
|
|
|
2457
2282
|
try {
|
|
2458
2283
|
ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
|
|
2459
2284
|
} catch (e) {
|
|
2460
|
-
|
|
2285
|
+
err8(e.message);
|
|
2461
2286
|
return 1;
|
|
2462
2287
|
}
|
|
2463
2288
|
let localHash;
|
|
@@ -2467,37 +2292,37 @@ async function status(wsArg, opts) {
|
|
|
2467
2292
|
} catch {
|
|
2468
2293
|
localHash = void 0;
|
|
2469
2294
|
}
|
|
2470
|
-
const r = await fetch(`${cloud}/
|
|
2295
|
+
const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).catch(() => void 0);
|
|
2471
2296
|
if (r === void 0) {
|
|
2472
|
-
|
|
2297
|
+
err8(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
|
|
2473
2298
|
return 1;
|
|
2474
2299
|
}
|
|
2475
2300
|
const d = await r.json().catch(() => void 0);
|
|
2476
2301
|
if (!r.ok || d?.ok !== true) {
|
|
2477
|
-
|
|
2302
|
+
err8(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
|
|
2478
2303
|
is the workspace born? githolon ws create ${ws}`);
|
|
2479
2304
|
return 1;
|
|
2480
2305
|
}
|
|
2481
|
-
|
|
2306
|
+
out8(`workspace ${ws} on ${cloud}`);
|
|
2482
2307
|
const allDomains = d.domains ?? [];
|
|
2483
2308
|
const controlPlaneHashes = new Set(CONTROL_PLANE_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
|
|
2484
2309
|
const domains = allDomains.filter((dm) => !controlPlaneHashes.has(dm.domainHash));
|
|
2485
2310
|
const controllerCount = allDomains.length - domains.length;
|
|
2486
|
-
if (allDomains.length === 0)
|
|
2311
|
+
if (allDomains.length === 0) out8(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
|
|
2487
2312
|
for (const dom of domains) {
|
|
2488
2313
|
const mark = dom.current === true ? " \u2190 current (the holon resolves dispatch here)" : "";
|
|
2489
|
-
|
|
2314
|
+
out8(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
|
|
2490
2315
|
}
|
|
2491
|
-
if (controllerCount > 0)
|
|
2316
|
+
if (controllerCount > 0) out8(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
|
|
2492
2317
|
const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
|
|
2493
|
-
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)`);
|
|
2494
2319
|
if (localHash === void 0) {
|
|
2495
|
-
|
|
2320
|
+
out8(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
|
|
2496
2321
|
return 0;
|
|
2497
2322
|
}
|
|
2498
|
-
|
|
2323
|
+
out8(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
|
|
2499
2324
|
const verdict = lawDiffVerdict(localHash, domains, ws);
|
|
2500
|
-
for (const line of verdict.lines)
|
|
2325
|
+
for (const line of verdict.lines) out8(line);
|
|
2501
2326
|
return verdict.inSync ? 0 : 2;
|
|
2502
2327
|
}
|
|
2503
2328
|
|
|
@@ -2541,15 +2366,6 @@ a bare <ws> resolves from nomos.package.mjs's optional \`workspace\` binding):
|
|
|
2541
2366
|
githolon secret set <ws> <secret> import a secret you already hold
|
|
2542
2367
|
githolon secret rotate <ws> rotate (and re-save) the workspace secret
|
|
2543
2368
|
|
|
2544
|
-
Capability credentials (facts on the ledger, values host-side \u2014 the value NEVER rides argv;
|
|
2545
|
-
stdin or --from-gcloud only; architecture/capability_credentials.md):
|
|
2546
|
-
githolon capability bind <ws> <capability> bind a credential: value from stdin (or
|
|
2547
|
-
--from-gcloud <name>) \u2192 the workspace's holon;
|
|
2548
|
-
sha256(value) \u2192 the law-state binding fact
|
|
2549
|
-
githolon capability list <ws> the binding FACTS (never the values)
|
|
2550
|
-
githolon capability rotate <ws> <capability> revoke + re-bind at epoch+1 (new value)
|
|
2551
|
-
githolon capability unbind <ws> <capability> revoke the fact; the host forgets the value
|
|
2552
|
-
|
|
2553
2369
|
Git lane (stock git push as deploy/birth \u2014 see also: push-to-create):
|
|
2554
2370
|
githolon git setup install the credential helper for the cloud host
|
|
2555
2371
|
githolon git remote [<ws>] [<remoteName>] wire this repo to a workspace (default name: nomos)
|
|
@@ -2628,38 +2444,6 @@ ${USAGE}`);
|
|
|
2628
2444
|
if (sub === "rotate" && ws !== void 0) return secretRotate(ws, opts);
|
|
2629
2445
|
return usageFail("expected: githolon secret <set <ws> <secret> | rotate <ws>>");
|
|
2630
2446
|
}
|
|
2631
|
-
async function runCapability(argv) {
|
|
2632
|
-
const usageFail = (m) => {
|
|
2633
|
-
process.stderr.write(`error: ${m}
|
|
2634
|
-
|
|
2635
|
-
${commandHelp("capability")}`);
|
|
2636
|
-
return 1;
|
|
2637
|
-
};
|
|
2638
|
-
const pos = [];
|
|
2639
|
-
const opts = {};
|
|
2640
|
-
const rest = argv.slice(1);
|
|
2641
|
-
for (let i = 0; i < rest.length; i++) {
|
|
2642
|
-
const a = rest[i];
|
|
2643
|
-
if (a === "--cloud" || a === "--from-gcloud" || a === "--scope" || a === "--expires-at") {
|
|
2644
|
-
const v = rest[++i];
|
|
2645
|
-
if (v === void 0) return usageFail(`${a} requires a value`);
|
|
2646
|
-
if (a === "--cloud") opts.cloud = v;
|
|
2647
|
-
else if (a === "--from-gcloud") opts.fromGcloud = v;
|
|
2648
|
-
else if (a === "--scope") opts.scope = v;
|
|
2649
|
-
else opts.expiresAt = v;
|
|
2650
|
-
} else if (a.startsWith("--")) {
|
|
2651
|
-
return usageFail(`unknown flag '${a}'`);
|
|
2652
|
-
} else {
|
|
2653
|
-
pos.push(a);
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
const [sub, ws, capability] = pos;
|
|
2657
|
-
if (sub === "bind" && ws !== void 0 && capability !== void 0) return capabilityBind(ws, capability, opts);
|
|
2658
|
-
if (sub === "rotate" && ws !== void 0 && capability !== void 0) return capabilityRotate(ws, capability, opts);
|
|
2659
|
-
if (sub === "unbind" && ws !== void 0 && capability !== void 0) return capabilityUnbind(ws, capability, opts);
|
|
2660
|
-
if (sub === "list" && ws !== void 0) return capabilityList(ws, opts);
|
|
2661
|
-
return usageFail("expected: githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws>");
|
|
2662
|
-
}
|
|
2663
2447
|
function isKind(s) {
|
|
2664
2448
|
return s !== void 0 && KINDS.includes(s);
|
|
2665
2449
|
}
|
|
@@ -2739,7 +2523,7 @@ async function runProof(args) {
|
|
|
2739
2523
|
const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
|
|
2740
2524
|
const tsxCli = join10(tsxDir, tsxBinRel ?? "dist/cli.mjs");
|
|
2741
2525
|
console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
|
|
2742
|
-
const r =
|
|
2526
|
+
const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
|
|
2743
2527
|
stdio: "inherit",
|
|
2744
2528
|
cwd: process.cwd(),
|
|
2745
2529
|
env: { ...process.env, ...keep ? { PROOF_KEEP: "1" } : {} }
|
|
@@ -2750,7 +2534,7 @@ function parseArgs(argv) {
|
|
|
2750
2534
|
const [command, ...rest] = argv;
|
|
2751
2535
|
if (command !== "generate" && command !== "g") {
|
|
2752
2536
|
throw new Error(
|
|
2753
|
-
`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.`
|
|
2754
2538
|
);
|
|
2755
2539
|
}
|
|
2756
2540
|
let outDir = DEFAULT_OUT;
|
|
@@ -2916,9 +2700,6 @@ ${commandHelp("status")}`);
|
|
|
2916
2700
|
if (argv[0] === "ws" || argv[0] === "deploy" || argv[0] === "secret") {
|
|
2917
2701
|
return runCloud(argv);
|
|
2918
2702
|
}
|
|
2919
|
-
if (argv[0] === "capability") {
|
|
2920
|
-
return runCapability(argv);
|
|
2921
|
-
}
|
|
2922
2703
|
if (argv[0] === "login") {
|
|
2923
2704
|
const agent = argv.includes("--agent");
|
|
2924
2705
|
const tokenAt = argv.indexOf("--token");
|
|
@@ -2975,8 +2756,8 @@ ${USAGE}`);
|
|
|
2975
2756
|
let parsed;
|
|
2976
2757
|
try {
|
|
2977
2758
|
parsed = parseArgs(argv);
|
|
2978
|
-
} catch (
|
|
2979
|
-
process.stderr.write(`error: ${
|
|
2759
|
+
} catch (err9) {
|
|
2760
|
+
process.stderr.write(`error: ${err9.message}
|
|
2980
2761
|
|
|
2981
2762
|
${USAGE}`);
|
|
2982
2763
|
return 1;
|
|
@@ -2995,8 +2776,8 @@ ${USAGE}`);
|
|
|
2995
2776
|
process.stdout.write(`created ${path}
|
|
2996
2777
|
`);
|
|
2997
2778
|
return 0;
|
|
2998
|
-
} catch (
|
|
2999
|
-
process.stderr.write(`error: ${
|
|
2779
|
+
} catch (err9) {
|
|
2780
|
+
process.stderr.write(`error: ${err9.message}
|
|
3000
2781
|
`);
|
|
3001
2782
|
return 1;
|
|
3002
2783
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bjorn3/browser_wasi_shim": "0.4.2",
|
|
30
|
-
"@githolon/dsl": "^0.16.
|
|
30
|
+
"@githolon/dsl": "^0.16.1",
|
|
31
31
|
"isomorphic-git": "^1.38.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|