githolon 0.74.0 → 0.75.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +226 -64
  2. package/package.json +3 -3
package/dist/cli.mjs CHANGED
@@ -113,14 +113,14 @@ var init_git_fs = __esm({
113
113
  });
114
114
 
115
115
  // vendor/engine/tree.mjs
116
- function flatten(dir, prefix, out12) {
116
+ function flatten(dir, prefix, out13) {
117
117
  for (const [name, inode] of dir.contents) {
118
118
  const path = prefix ? prefix + "/" + name : name;
119
119
  if (inode.contents instanceof Map) {
120
- out12.push({ path, dir: true });
121
- flatten(inode, path, out12);
120
+ out13.push({ path, dir: true });
121
+ flatten(inode, path, out13);
122
122
  } else {
123
- out12.push({ path, dir: false, bytes: inode.data ?? new Uint8Array(0) });
123
+ out13.push({ path, dir: false, bytes: inode.data ?? new Uint8Array(0) });
124
124
  }
125
125
  }
126
126
  }
@@ -143,13 +143,13 @@ function serializeTree(workTree) {
143
143
  }
144
144
  let total = 0;
145
145
  for (const c of chunks) total += c.length;
146
- const out12 = new Uint8Array(total);
146
+ const out13 = new Uint8Array(total);
147
147
  let off = 0;
148
148
  for (const c of chunks) {
149
- out12.set(c, off);
149
+ out13.set(c, off);
150
150
  off += c.length;
151
151
  }
152
- return out12;
152
+ return out13;
153
153
  }
154
154
  var enc, dec;
155
155
  var init_tree = __esm({
@@ -164,13 +164,13 @@ var init_tree = __esm({
164
164
  function concatBytes(chunks) {
165
165
  let n = 0;
166
166
  for (const c of chunks) n += c.length;
167
- const out12 = new Uint8Array(n);
167
+ const out13 = new Uint8Array(n);
168
168
  let o = 0;
169
169
  for (const c of chunks) {
170
- out12.set(c, o);
170
+ out13.set(c, o);
171
171
  o += c.length;
172
172
  }
173
- return out12;
173
+ return out13;
174
174
  }
175
175
  function pkt(payload) {
176
176
  const body = typeof payload === "string" ? enc2.encode(payload) : payload;
@@ -418,6 +418,7 @@ __export(engine_exports, {
418
418
  readShape: () => readShape,
419
419
  rebirthCompact: () => rebirthCompact,
420
420
  refreshProjection: () => refreshProjection,
421
+ refusedSummary: () => refusedSummary,
421
422
  replicateIntents: () => replicateIntents,
422
423
  repoArgOf: () => repoArgOf,
423
424
  restoreCheckpointFromCustody: () => restoreCheckpointFromCustody,
@@ -461,13 +462,13 @@ async function sha256hex(t) {
461
462
  function osEntropyBuffer(n) {
462
463
  const bytes = new Uint8Array(n * 8);
463
464
  for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
464
- const out12 = new Array(n), T = 2 ** 53;
465
+ const out13 = new Array(n), T = 2 ** 53;
465
466
  for (let i = 0; i < n; i++) {
466
467
  let z = 0n;
467
468
  for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
468
- out12[i] = Number(z >> 11n) / T;
469
+ out13[i] = Number(z >> 11n) / T;
469
470
  }
470
- return out12;
471
+ return out13;
471
472
  }
472
473
  function stringifyBig(o) {
473
474
  return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
@@ -914,15 +915,15 @@ function contribution(read, cur) {
914
915
  return { group, value: typeof raw === "number" ? raw : 0 };
915
916
  }
916
917
  function contributionDiff(read, before, after) {
917
- const out12 = [];
918
+ const out13 = [];
918
919
  if (before && after && before.group === after.group) {
919
920
  const d = after.value - before.value;
920
- if (d !== 0) out12.push({ readId: read.id, group: after.group, delta: d });
921
- return out12;
921
+ if (d !== 0) out13.push({ readId: read.id, group: after.group, delta: d });
922
+ return out13;
922
923
  }
923
- if (before) out12.push({ readId: read.id, group: before.group, delta: -before.value });
924
- if (after) out12.push({ readId: read.id, group: after.group, delta: after.value });
925
- return out12.filter((d) => d.delta !== 0);
924
+ if (before) out13.push({ readId: read.id, group: before.group, delta: -before.value });
925
+ if (after) out13.push({ readId: read.id, group: after.group, delta: after.value });
926
+ return out13.filter((d) => d.delta !== 0);
926
927
  }
927
928
  function countTenantRows(eng, ws) {
928
929
  let total = 0;
@@ -935,16 +936,16 @@ async function countTenantRowsByHome(eng, ws, homeKeys) {
935
936
  const keySet = new Set(homeKeys);
936
937
  const tagOf = /* @__PURE__ */ new Map();
937
938
  for (const hk of homeKeys) tagOf.set(await routeTagHexOfHomeKey(hk), hk);
938
- const out12 = /* @__PURE__ */ new Map();
939
- for (const hk of homeKeys) out12.set(hk, 0);
939
+ const out13 = /* @__PURE__ */ new Map();
940
+ for (const hk of homeKeys) out13.set(hk, 0);
940
941
  for (const type of tenantTypes(eng)) {
941
942
  for (const r of query(eng, ws, "aggregatesByType", JSON.stringify({ __type: type }))) {
942
943
  const id = String(r.id || "");
943
944
  const hk = keySet.has(id) ? id : tagOf.get(routeTagHexOfMintedId(id) ?? "");
944
- if (hk !== void 0) out12.set(hk, (out12.get(hk) ?? 0) + 1);
945
+ if (hk !== void 0) out13.set(hk, (out13.get(hk) ?? 0) + 1);
945
946
  }
946
947
  }
947
- return out12;
948
+ return out13;
948
949
  }
949
950
  async function collectHomedIntents(eng, ws, homeKeys) {
950
951
  const routes = directiveRoutes(eng);
@@ -969,7 +970,7 @@ async function collectHomedIntents(eng, ws, homeKeys) {
969
970
  strikeParity.set(target, (strikeParity.get(target) ?? 0) + 1);
970
971
  }
971
972
  }
972
- const out12 = [];
973
+ const out13 = [];
973
974
  for (const { oid, blob, doc } of docs) {
974
975
  const id = doc && typeof doc.id === "string" ? doc.id : null;
975
976
  if (!id || (strikeParity.get(id) ?? 0) % 2 === 1) continue;
@@ -985,9 +986,9 @@ async function collectHomedIntents(eng, ws, homeKeys) {
985
986
  const tag = routeTagHexOfMintedId(value);
986
987
  homeKey = tag !== void 0 ? tagOf.get(tag) ?? null : null;
987
988
  }
988
- if (homeKey !== null) out12.push({ oid, id, homeKey, blob });
989
+ if (homeKey !== null) out13.push({ oid, id, homeKey, blob });
989
990
  }
990
- return out12;
991
+ return out13;
991
992
  }
992
993
  async function migrateHomes(eng, ws, ledger, sourceWs, homeKeys, { onlyIds = null } = {}) {
993
994
  const wanted = onlyIds ? new Set(onlyIds) : null;
@@ -1096,7 +1097,7 @@ async function collectGlobalIntents(eng, ws) {
1096
1097
  strikeParity.set(target, (strikeParity.get(target) ?? 0) + 1);
1097
1098
  }
1098
1099
  }
1099
- const out12 = [];
1100
+ const out13 = [];
1100
1101
  for (const { oid, blob, doc } of docs) {
1101
1102
  const id = doc && typeof doc.id === "string" ? doc.id : null;
1102
1103
  let touches = false;
@@ -1107,9 +1108,9 @@ async function collectGlobalIntents(eng, ws) {
1107
1108
  if (t !== void 0 && globals.has(t)) touches = true;
1108
1109
  }
1109
1110
  if (!touches || !id || (strikeParity.get(id) ?? 0) % 2 === 1) continue;
1110
- out12.push({ oid, id, blob });
1111
+ out13.push({ oid, id, blob });
1111
1112
  }
1112
- return out12;
1113
+ return out13;
1113
1114
  }
1114
1115
  async function replicateIntents(eng, ws, ledger, intents, { push = true } = {}) {
1115
1116
  const main2 = await mainIntentIds(eng, ws);
@@ -1382,8 +1383,8 @@ function writeWork(eng, name, bytes) {
1382
1383
  eng.preopen.dir.contents.set(name, new File(bytes));
1383
1384
  }
1384
1385
  function currentLaw(eng, ws) {
1385
- const out12 = JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "currentLaw" }), branch: BRANCH }, eng.STDERR));
1386
- return out12.keyMap || {};
1386
+ const out13 = JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "currentLaw" }), branch: BRANCH }, eng.STDERR));
1387
+ return out13.keyMap || {};
1387
1388
  }
1388
1389
  function installedLaw(eng, ws) {
1389
1390
  return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "installedLaw" }), branch: BRANCH }, eng.STDERR));
@@ -1392,8 +1393,8 @@ function domainPackage(eng, ws, hash) {
1392
1393
  return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "domainPackage", hash }), branch: BRANCH }, eng.STDERR));
1393
1394
  }
1394
1395
  function declaredCapabilities(eng, ws) {
1395
- const out12 = JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "declaredCapabilities" }), branch: BRANCH }, eng.STDERR));
1396
- return out12.capabilities || [];
1396
+ const out13 = JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "declaredCapabilities" }), branch: BRANCH }, eng.STDERR));
1397
+ return out13.capabilities || [];
1397
1398
  }
1398
1399
  function reactorScan(eng, ws) {
1399
1400
  return JSON.parse(call(eng.ex, "outbox", { repoArg: repoArgOf(ws), branch: BRANCH }, eng.STDERR));
@@ -1494,13 +1495,13 @@ async function streamAll(stream) {
1494
1495
  }
1495
1496
  let len = 0;
1496
1497
  for (const c of chunks) len += c.length;
1497
- const out12 = new Uint8Array(len);
1498
+ const out13 = new Uint8Array(len);
1498
1499
  let at = 0;
1499
1500
  for (const c of chunks) {
1500
- out12.set(c, at);
1501
+ out13.set(c, at);
1501
1502
  at += c.length;
1502
1503
  }
1503
- return out12;
1504
+ return out13;
1504
1505
  }
1505
1506
  async function gzip(bytes) {
1506
1507
  const cs = new CompressionStream("gzip");
@@ -2078,13 +2079,16 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
2078
2079
  _projDone();
2079
2080
  }
2080
2081
  }
2082
+ let unparkedSessions = /* @__PURE__ */ new Set();
2081
2083
  if (deadLetters.length) {
2082
2084
  try {
2083
- await materializeRefused(eng, ws, ledger, deadLetters);
2085
+ unparkedSessions = (await materializeRefused(eng, ws, ledger, deadLetters)).unparkedSessions;
2084
2086
  } catch (e) {
2087
+ unparkedSessions = new Set(deadLetters.map((dl) => dl.session).filter(Boolean));
2085
2088
  log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
2086
2089
  }
2087
2090
  }
2091
+ const prunable = unparkedSessions.size ? consumed.filter((c) => !unparkedSessions.has(c.cid)) : consumed;
2088
2092
  const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
2089
2093
  const deferDurability = defer && !hasCoordination;
2090
2094
  let _sealed = true;
@@ -2102,7 +2106,7 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
2102
2106
  }
2103
2107
  }
2104
2108
  if (_sealed && !deferDurability) {
2105
- for (const { ref } of consumed) {
2109
+ for (const { ref } of prunable) {
2106
2110
  try {
2107
2111
  await custodyPush(eng, ws, ledger, { remoteRef: ref, del: true });
2108
2112
  } catch {
@@ -2125,7 +2129,7 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
2125
2129
  ...globalsOut.length ? { globals: globalsOut } : {},
2126
2130
  // The adapter's backup lane consumes these: `deferred` ⇒ resident main carries totalAdmitted
2127
2131
  // un-backed-up commits and `consumed` are the session branches to delete AFTER the push lands.
2128
- ...deferDurability && totalAdmitted ? { deferred: true, consumed, admitted: totalAdmitted } : {}
2132
+ ...deferDurability && totalAdmitted ? { deferred: true, consumed: prunable, admitted: totalAdmitted } : {}
2129
2133
  };
2130
2134
  }
2131
2135
  async function backupHead(eng, ws, ledger, head) {
@@ -2308,11 +2312,23 @@ async function archiveRefusedMain(eng, ws, ledger, label) {
2308
2312
  async function writeRefusedRef(eng, ws, dl) {
2309
2313
  const gitdir = gitdirOf(ws);
2310
2314
  const blob = dl.blob instanceof Uint8Array ? dl.blob : enc3.encode(String(dl.blob || ""));
2315
+ const refForStamp = refusedRefName(dl.intentId || dl.oid);
2316
+ let parkedAt = (/* @__PURE__ */ new Date()).toISOString();
2317
+ try {
2318
+ const prevOid = kgit.resolveRef(eng, gitdir, refForStamp);
2319
+ if (prevOid) {
2320
+ const prev = JSON.parse(dec3.decode(kgit.readBlob(eng, gitdir, prevOid, "verdict.json")));
2321
+ if (prev && typeof prev.parkedAt === "string") parkedAt = prev.parkedAt;
2322
+ }
2323
+ } catch {
2324
+ }
2311
2325
  const verdict = enc3.encode(JSON.stringify({
2312
2326
  offerId: dl.intentId || null,
2313
2327
  oid: dl.oid || null,
2314
2328
  domain: dl.domain || null,
2315
2329
  directiveId: dl.directiveId || null,
2330
+ session: dl.session || null,
2331
+ parkedAt,
2316
2332
  verdict: { reason: String(dl.error || "refused") }
2317
2333
  }));
2318
2334
  const ref = refusedRefName(dl.intentId || dl.oid);
@@ -2328,31 +2344,46 @@ async function writeRefusedRef(eng, ws, dl) {
2328
2344
  async function listRefusedRefs(eng, ws) {
2329
2345
  const gitdir = gitdirOf(ws);
2330
2346
  const refs = kgit.listRefs(eng, gitdir, "refs/heads/refused/");
2331
- const out12 = [];
2347
+ const out13 = [];
2332
2348
  for (const { ref, oid } of refs) {
2333
2349
  try {
2334
2350
  const vb = kgit.readBlob(eng, gitdir, oid, "verdict.json");
2335
2351
  const ib = kgit.readBlob(eng, gitdir, oid, "intent.json");
2336
2352
  const v = JSON.parse(dec3.decode(vb));
2337
- out12.push({ ref, ...v, intentB64: b64Bytes(ib) });
2353
+ out13.push({ ref, ...v, intentB64: b64Bytes(ib) });
2338
2354
  } catch {
2339
2355
  }
2340
2356
  }
2341
- return out12;
2357
+ return out13;
2342
2358
  }
2343
- async function materializeRefused(eng, ws, ledger, deadLetters) {
2359
+ async function refusedSummary(eng, ws) {
2344
2360
  const gitdir = gitdirOf(ws);
2361
+ const refs = kgit.listRefs(eng, gitdir, "refs/heads/refused/");
2362
+ let count2 = 0, latestParkedAt = null;
2363
+ for (const { oid } of refs) {
2364
+ try {
2365
+ const v = JSON.parse(dec3.decode(kgit.readBlob(eng, gitdir, oid, "verdict.json")));
2366
+ count2++;
2367
+ if (v && typeof v.parkedAt === "string" && (!latestParkedAt || v.parkedAt > latestParkedAt)) latestParkedAt = v.parkedAt;
2368
+ } catch {
2369
+ }
2370
+ }
2371
+ return { count: count2, latestParkedAt };
2372
+ }
2373
+ async function materializeRefused(eng, ws, ledger, deadLetters) {
2345
2374
  const refs = [];
2375
+ const unparkedSessions = /* @__PURE__ */ new Set();
2346
2376
  for (const dl of deadLetters || []) {
2347
2377
  try {
2348
2378
  const ref = await writeRefusedRef(eng, ws, dl);
2349
2379
  await custodyPush(eng, ws, ledger, { remoteRef: ref });
2350
2380
  refs.push(ref);
2351
2381
  } catch (e) {
2382
+ if (dl && dl.session) unparkedSessions.add(dl.session);
2352
2383
  log("refused-ref-materialize-failed", { ws, intentId: dl?.intentId, error: String(e).slice(0, 200) });
2353
2384
  }
2354
2385
  }
2355
- return refs;
2386
+ return { refs, unparkedSessions };
2356
2387
  }
2357
2388
  async function deleteRefusedRef(eng, ws, ledger, offerId) {
2358
2389
  const gitdir = gitdirOf(ws);
@@ -2501,9 +2532,9 @@ async function ledgerStats(eng, ws, ledger = null) {
2501
2532
  const mounted = eng.mounted.get(ws);
2502
2533
  if (mounted?.ledgerStats?.head === head) return mounted.ledgerStats;
2503
2534
  const commits = kgit.intentsAbove(eng, gitdir, head).length;
2504
- const out12 = { head, commits };
2505
- if (mounted) mounted.ledgerStats = out12;
2506
- return out12;
2535
+ const out13 = { head, commits };
2536
+ if (mounted) mounted.ledgerStats = out13;
2537
+ return out13;
2507
2538
  }
2508
2539
  async function countRemoteCommits(eng, ledger) {
2509
2540
  const head = (await custodyLsRefs(ledger, "refs/heads/main"))["refs/heads/main"] || null;
@@ -2976,6 +3007,7 @@ var governance_exports = {};
2976
3007
  __export(governance_exports, {
2977
3008
  HONEST_CEILING: () => HONEST_CEILING,
2978
3009
  ROOT_CUSTODIAN_SECRET: () => ROOT_CUSTODIAN_SECRET,
3010
+ STAGE_THRESHOLD_CHARS: () => STAGE_THRESHOLD_CHARS,
2979
3011
  buildCertificate: () => buildCertificate,
2980
3012
  buildRebirthPlan: () => buildRebirthPlan,
2981
3013
  certifyEra: () => certifyEra,
@@ -2996,6 +3028,7 @@ __export(governance_exports, {
2996
3028
  rebirthApply: () => rebirthApply,
2997
3029
  rebirthPlanCommand: () => rebirthPlanCommand,
2998
3030
  rebirthWorkspace: () => rebirthWorkspace,
3031
+ relaySealedOfferViaStage: () => relaySealedOfferViaStage,
2999
3032
  renderRebirthImpact: () => renderRebirthImpact,
3000
3033
  resolveGovernanceHash: () => resolveGovernanceHash,
3001
3034
  resolveGovernancePrincipal: () => resolveGovernancePrincipal,
@@ -3055,9 +3088,41 @@ async function signAndPostOffer(ws, domain, directiveId, payload, opts) {
3055
3088
  authorSecret: device.secret,
3056
3089
  actor: `user:${principal}`
3057
3090
  });
3091
+ if (sealed.intentBytes.length > STAGE_THRESHOLD_CHARS) {
3092
+ const staged = await relaySealedOfferViaStage(cloud, ws, directiveId, sealed.intentBytes, fetch);
3093
+ if (staged !== null) {
3094
+ out2(` staged ${staged.size} sealed bytes content-addressed (sha256 ${staged.sha256.slice(0, 16)}\u2026) \u2014 the signed offer names the hash`);
3095
+ return { ...staged.verdict, principal };
3096
+ }
3097
+ err2(`this cloud has no /v2/stage content-addressed lane \u2014 relaying the ${sealed.intentBytes.length}-char sealed offer inline (a 413 here means the worker needs upgrading to accept law this large signed)`);
3098
+ }
3058
3099
  const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws });
3059
3100
  return { ...verdict, principal };
3060
3101
  }
3102
+ async function relaySealedOfferViaStage(cloud, ws, directiveId, intentBytesB64, fetchImpl) {
3103
+ const raw = Buffer.from(intentBytesB64, "base64");
3104
+ const sha2562 = createHash("sha256").update(raw).digest("hex");
3105
+ let put;
3106
+ try {
3107
+ put = await fetchImpl(`${cloud}/v2/stage/${sha2562}`, { method: "PUT", body: raw });
3108
+ } catch {
3109
+ return null;
3110
+ }
3111
+ if (!put.ok) return null;
3112
+ const r = await fetchImpl(`${cloud}/v2/workspaces/${ws}/${directiveId}`, {
3113
+ method: "POST",
3114
+ headers: { "content-type": "application/json" },
3115
+ body: JSON.stringify({ intentRef: { sha256: sha2562, size: raw.length } })
3116
+ });
3117
+ const text = await r.text();
3118
+ let body;
3119
+ try {
3120
+ body = JSON.parse(text);
3121
+ } catch {
3122
+ body = text;
3123
+ }
3124
+ return { sha256: sha2562, size: raw.length, verdict: { ok: r.ok, status: r.status, body } };
3125
+ }
3061
3126
  async function signAndPostGovernance(parent, directiveId, payload, opts) {
3062
3127
  return signAndPostOffer(parent, "workspaces", directiveId, payload, opts);
3063
3128
  }
@@ -4273,13 +4338,14 @@ function readSealedIntentBytes(eng, ws, intentOut) {
4273
4338
  if (f?.data === void 0) return void 0;
4274
4339
  return Buffer.from(f.data).toString("base64");
4275
4340
  }
4276
- var out2, err2, HONEST_CEILING, ROOT_CUSTODIAN_SECRET, CLIENT_IMPACT;
4341
+ var out2, err2, STAGE_THRESHOLD_CHARS, HONEST_CEILING, ROOT_CUSTODIAN_SECRET, CLIENT_IMPACT;
4277
4342
  var init_governance = __esm({
4278
4343
  "src/governance.ts"() {
4279
4344
  "use strict";
4280
4345
  init_cloud();
4281
4346
  out2 = (s) => void process.stdout.write(s + "\n");
4282
4347
  err2 = (s) => void process.stderr.write("error: " + s + "\n");
4348
+ STAGE_THRESHOLD_CHARS = 4 * 1024 * 1024;
4283
4349
  HONEST_CEILING = `The operator controls root's law and could deploy law removing the consent check \u2014 but that act is itself a signed, ordered, permanent fact in root's chain, visible to every tenant running \`replay root\`, and grounds for exit-with-everything. Not "we can't" \u2014 "we can't secretly, and you can always leave."`;
4284
4350
  ROOT_CUSTODIAN_SECRET = "nomos-root-custodian-key";
4285
4351
  CLIENT_IMPACT = "every connected client raises CustodyBreak(superseded); unsynced client work is invisible to the cloud \u2014 drain/sync clients first";
@@ -6582,11 +6648,11 @@ function bareTypeLabel(t) {
6582
6648
  return i > 0 && i < t.length - 1 ? t.slice(i + 1) : t;
6583
6649
  }
6584
6650
  function geoFieldsOfInstalls(installs) {
6585
- const out12 = /* @__PURE__ */ new Map();
6651
+ const out13 = /* @__PURE__ */ new Map();
6586
6652
  const add2 = (type, field) => {
6587
- const set = out12.get(type) ?? /* @__PURE__ */ new Set();
6653
+ const set = out13.get(type) ?? /* @__PURE__ */ new Set();
6588
6654
  set.add(field);
6589
- out12.set(type, set);
6655
+ out13.set(type, set);
6590
6656
  };
6591
6657
  for (const inst of installs) {
6592
6658
  for (const layer of inst.layers) {
@@ -6602,7 +6668,7 @@ function geoFieldsOfInstalls(installs) {
6602
6668
  }
6603
6669
  }
6604
6670
  }
6605
- return out12;
6671
+ return out13;
6606
6672
  }
6607
6673
  function pointOfOpValue(setOp) {
6608
6674
  let v = setOp["Json"];
@@ -7360,6 +7426,65 @@ var init_scene = __esm({
7360
7426
  }
7361
7427
  });
7362
7428
 
7429
+ // src/dlq.ts
7430
+ var dlq_exports = {};
7431
+ __export(dlq_exports, {
7432
+ dlqSweep: () => dlqSweep,
7433
+ renderSweepNode: () => renderSweepNode
7434
+ });
7435
+ function renderSweepNode(n, indent = "") {
7436
+ const jammed = n.dlqCount > 0;
7437
+ const badge = n.error !== void 0 ? "?" : jammed ? "\u2717" : "\u2713";
7438
+ const detail = n.error !== void 0 ? `unreachable: ${n.error}` : jammed ? `${n.dlqCount} dead-letter${n.dlqCount === 1 ? "" : "s"} (latest ${n.latestParkedAt ?? "unknown"})` : "clean";
7439
+ const lines = [`${indent}${badge} ${n.workspace} \u2014 ${detail}`];
7440
+ for (const dl of n.deadLetters ?? []) {
7441
+ lines.push(`${indent} \xB7 ${dl.domain ?? "?"}/${dl.directiveId ?? "?"} [${dl.intentId ?? "?"}] ${String(dl.error ?? "").slice(0, 100)}`);
7442
+ }
7443
+ for (const c of n.children ?? []) lines.push(...renderSweepNode(c, indent + " "));
7444
+ if (n.truncatedChildren) lines.push(`${indent} \u2026 ${n.truncatedChildren} more child(ren) truncated \u2014 raise ?limit or sweep a subtree`);
7445
+ return lines;
7446
+ }
7447
+ async function dlqSweep(ws, opts = {}) {
7448
+ const cloud = cloudBase(opts.cloud);
7449
+ const q = new URLSearchParams();
7450
+ if (opts.depth !== void 0) q.set("depth", String(opts.depth));
7451
+ if (opts.full === true) q.set("full", "1");
7452
+ const url = `${cloud}/v2/workspaces/${encodeURIComponent(ws)}/dlq-sweep${q.size ? `?${q}` : ""}`;
7453
+ let body;
7454
+ try {
7455
+ const res = await fetch(url);
7456
+ body = await res.json();
7457
+ if (body.ok !== true || body.root === void 0) {
7458
+ err12(`dlq sweep refused: ${String(body.error ?? "no sweep in the response").slice(0, 300)}`);
7459
+ return 1;
7460
+ }
7461
+ } catch (e) {
7462
+ err12(`dlq sweep failed against ${cloud}: ${e.message}`);
7463
+ return 1;
7464
+ }
7465
+ if (opts.json === true) {
7466
+ out12(JSON.stringify(body, null, 2));
7467
+ return body.jammed ? 2 : 0;
7468
+ }
7469
+ out12(`DLQ sweep of '${ws}' \u2014 ${body.nodes} node(s), ${body.jammed} jammed`);
7470
+ for (const line of renderSweepNode(body.root)) out12(line);
7471
+ if (body.jammed) {
7472
+ out12("");
7473
+ out12("unjam: fix/deploy the law on the jammed workspace, then POST /v2/workspaces/<node>/dead-letters/retry");
7474
+ out12(" (or from the app: holon.retryDeadLetter(id); discard is the app's explicit choice)");
7475
+ }
7476
+ return body.jammed ? 2 : 0;
7477
+ }
7478
+ var out12, err12;
7479
+ var init_dlq = __esm({
7480
+ "src/dlq.ts"() {
7481
+ "use strict";
7482
+ init_cloud();
7483
+ out12 = (s) => void process.stdout.write(s + "\n");
7484
+ err12 = (s) => void process.stderr.write("error: " + s + "\n");
7485
+ }
7486
+ });
7487
+
7363
7488
  // src/cli.ts
7364
7489
  import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "node:fs";
7365
7490
  import { spawnSync as spawnSync6 } from "node:child_process";
@@ -8078,6 +8203,18 @@ var HELP = {
8078
8203
  ],
8079
8204
  examples: ["githolon status", "githolon status my-guestbook", "githolon status --target prod"]
8080
8205
  },
8206
+ dlq: {
8207
+ usage: "githolon dlq sweep <workspace> [--depth <n>] [--full] [--json] [--cloud <url>]",
8208
+ what: "THE FLEET/SUBTREE DLQ SWEEP (architecture/operator_dlq_sweep.md): one look across a platform's\ndead-letter queues. The cloud enumerates the subtree via the REGISTRY (the maintained\nworkspacesByParent projection index \u2014 never a chain walk), reads each node's O(1) DLQ summary\n({count, latestParkedAt}) and prints the tree; jammed nodes carry their parked entries under\n--full. Read-only; the unjam stays POST /v2/workspaces/<node>/dead-letters/retry after the law\nfix. Exit codes: 0 all clean \xB7 2 jams found \xB7 1 error.",
8209
+ flags: [
8210
+ ["<workspace>", "the subtree root \u2014 a platform, or any workspace (a non-governance node is a leaf)"],
8211
+ ["--depth <n>", "recursion depth (default 2, max 4; 1 = the node alone)"],
8212
+ ["--full", "include the parked entries for jammed nodes (clean nodes never pay a fetch)"],
8213
+ ["--json", "the raw sweep tree, machine-consumable"],
8214
+ ["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
8215
+ ],
8216
+ examples: ["githolon dlq sweep co2-platform", "githolon dlq sweep co2-platform --depth 3 --full", "githolon dlq sweep my-ws --json"]
8217
+ },
8081
8218
  compat: {
8082
8219
  usage: "githolon compat <ws-url | deploy.json | interface.json | dir> [--config <cfg>] [--workspace <ws>] [--json]",
8083
8220
  what: "THE CI GATE (the compat superpower): compare the CURRENT build's structural interface\n(build/<name>.interface.json \u2014 run `githolon compile` first) against a TARGET's installed law,\nPER MEMBER via #72 subtyping. Exits NON-ZERO on any incompatible member, so a client/law\nmismatch is caught before it ships. Target: a deploy.json (its staged .interface is the offer),\nan .interface.json / build dir, or a LIVE workspace URL (connect()s + reads the on-ledger offer).",
@@ -8374,9 +8511,9 @@ function capJsonStrings(v, max = 2048) {
8374
8511
  if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
8375
8512
  if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
8376
8513
  if (typeof v === "object" && v !== null) {
8377
- const out12 = {};
8378
- for (const [k, x] of Object.entries(v)) out12[k] = capJsonStrings(x, max);
8379
- return out12;
8514
+ const out13 = {};
8515
+ for (const [k, x] of Object.entries(v)) out13[k] = capJsonStrings(x, max);
8516
+ return out13;
8380
8517
  }
8381
8518
  return v;
8382
8519
  }
@@ -8834,6 +8971,8 @@ deploy is OPEN + kernel-gated \u2014 births return no host secret, ownership is
8834
8971
  [--explain-current-interface] lineage (active = served now)
8835
8972
  githolon domains retire <ws> <hashOrKey> retire an install (marks it retired \u2014 replayable,
8836
8973
  [--with <hashOrKey>] no longer served) via a signed installDomain offer
8974
+ githolon dlq sweep <ws> [--depth <n>] [--full] sweep a platform subtree's dead-letter queues
8975
+ (registry-enumerated; exit 2 when jams exist)
8837
8976
  githolon secret set <ws> <push-password> store a git push password (push-to-create birth lane)
8838
8977
  githolon secret rotate <ws> RETIRED \u2014 host holds no ownership credential (410)
8839
8978
 
@@ -8858,8 +8997,8 @@ Options:
8858
8997
  function cloudArgs(rest) {
8859
8998
  const pos = [];
8860
8999
  const opts = {};
8861
- const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with", "--out", "--reason", "--oid", "--seed-secret", "--gcp-secret", "--controller-hash", "--plan"];
8862
- const BOOL_FLAGS = ["--sha256", "--stamp", "--yes", "--clear", "--keep-beside", "--retire-replaced", "--explain-current-interface", "--dry-run", "--json"];
9000
+ const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with", "--out", "--reason", "--oid", "--seed-secret", "--gcp-secret", "--controller-hash", "--plan", "--depth"];
9001
+ const BOOL_FLAGS = ["--sha256", "--stamp", "--yes", "--clear", "--keep-beside", "--retire-replaced", "--explain-current-interface", "--dry-run", "--json", "--full"];
8863
9002
  for (let i = 0; i < rest.length; i++) {
8864
9003
  const a = rest[i];
8865
9004
  if (BOOL_FLAGS.includes(a)) {
@@ -8871,6 +9010,7 @@ function cloudArgs(rest) {
8871
9010
  else if (a === "--explain-current-interface") opts.explain = true;
8872
9011
  else if (a === "--dry-run") opts.dryRun = true;
8873
9012
  else if (a === "--json") opts.json = true;
9013
+ else if (a === "--full") opts.full = true;
8874
9014
  else opts.yes = true;
8875
9015
  } else if (VALUE_FLAGS.includes(a)) {
8876
9016
  const v = rest[++i];
@@ -8892,6 +9032,10 @@ function cloudArgs(rest) {
8892
9032
  const n = Number(v);
8893
9033
  if (!Number.isInteger(n) || n <= 0) return { pos, opts, bad: `--max requires a positive integer` };
8894
9034
  opts.max = n;
9035
+ } else if (a === "--depth") {
9036
+ const n = Number(v);
9037
+ if (!Number.isInteger(n) || n <= 0) return { pos, opts, bad: `--depth requires a positive integer` };
9038
+ opts.depth = n;
8895
9039
  } else opts.file = v;
8896
9040
  } else if (a.startsWith("--")) {
8897
9041
  return { pos, opts, bad: `unknown flag '${a}'` };
@@ -9155,10 +9299,10 @@ async function main(argv) {
9155
9299
  };
9156
9300
  const deployArg = pull("--deploy");
9157
9301
  const workspace = pull("--workspace") ?? "test-ws";
9158
- const out12 = pull("--out");
9302
+ const out13 = pull("--out");
9159
9303
  const runtime = pull("--runtime");
9160
9304
  const deploy3 = (deployArg ?? "").split(",").map((s) => s.trim()).filter(Boolean);
9161
- if (deploy3.length === 0 || out12 === void 0) {
9305
+ if (deploy3.length === 0 || out13 === void 0) {
9162
9306
  process.stderr.write(
9163
9307
  `error: usage: githolon harness-install --deploy <deploy.json[,deploy.json...]> --workspace <ws> --out <snapshotPath> [--runtime <dir>]
9164
9308
  `
@@ -9166,7 +9310,7 @@ async function main(argv) {
9166
9310
  return 1;
9167
9311
  }
9168
9312
  const { harnessInstall: harnessInstall2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
9169
- return harnessInstall2({ deploy: deploy3, workspace, out: out12, ...runtime !== void 0 ? { runtime } : {} });
9313
+ return harnessInstall2({ deploy: deploy3, workspace, out: out13, ...runtime !== void 0 ? { runtime } : {} });
9170
9314
  }
9171
9315
  if (argv[0] === "dev") {
9172
9316
  const rest = argv.slice(1);
@@ -9300,6 +9444,24 @@ ${commandHelp("status")}`);
9300
9444
  if (argv[0] === "ws" || argv[0] === "deploy" || argv[0] === "secret") {
9301
9445
  return runCloud(argv);
9302
9446
  }
9447
+ if (argv[0] === "dlq") {
9448
+ const { dlqSweep: dlqSweep2 } = await Promise.resolve().then(() => (init_dlq(), dlq_exports));
9449
+ const { pos, opts, bad } = cloudArgs(argv.slice(1));
9450
+ if (bad !== void 0) {
9451
+ process.stderr.write(`error: ${bad}
9452
+
9453
+ ${commandHelp("dlq") ?? USAGE}`);
9454
+ return 1;
9455
+ }
9456
+ const [sub2, ws] = pos;
9457
+ if (sub2 !== "sweep" || ws === void 0) {
9458
+ process.stderr.write(`error: expected: githolon dlq sweep <workspace> [--depth <n>] [--full] [--json]
9459
+
9460
+ ${commandHelp("dlq") ?? USAGE}`);
9461
+ return 1;
9462
+ }
9463
+ return dlqSweep2(ws, opts);
9464
+ }
9303
9465
  if (argv[0] === "domains") {
9304
9466
  const { domainsRetire: domainsRetire2, domainsStatus: domainsStatus2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
9305
9467
  const { pos, opts, bad } = cloudArgs(argv.slice(1));
@@ -9626,8 +9788,8 @@ ${USAGE}`);
9626
9788
  let parsed;
9627
9789
  try {
9628
9790
  parsed = parseArgs(argv);
9629
- } catch (err12) {
9630
- process.stderr.write(`error: ${err12.message}
9791
+ } catch (err13) {
9792
+ process.stderr.write(`error: ${err13.message}
9631
9793
 
9632
9794
  ${USAGE}`);
9633
9795
  return 1;
@@ -9646,8 +9808,8 @@ ${USAGE}`);
9646
9808
  process.stdout.write(`created ${path}
9647
9809
  `);
9648
9810
  return 0;
9649
- } catch (err12) {
9650
- process.stderr.write(`error: ${err12.message}
9811
+ } catch (err13) {
9812
+ process.stderr.write(`error: ${err13.message}
9651
9813
  `);
9652
9814
  return 1;
9653
9815
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.74.0",
3
+ "version": "0.75.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",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@bjorn3/browser_wasi_shim": "0.4.2",
32
- "@githolon/client": "^0.74.0",
33
- "@githolon/dsl": "^0.74.0",
32
+ "@githolon/client": "^0.75.1",
33
+ "@githolon/dsl": "^0.75.1",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {