githolon 0.84.1 → 0.86.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 CHANGED
@@ -1778,7 +1778,17 @@ function servePack(eng, ws, base = "") {
1778
1778
  try {
1779
1779
  r = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base, branch: BRANCH }, eng.STDERR));
1780
1780
  } catch (e) {
1781
- if (/nothing to pack|head == base/.test(String(e))) return { packB64: "", bytes: 0, upToDate: true, ...head ? { head } : {} };
1781
+ const msg = String(e);
1782
+ if (/nothing to pack|head == base/.test(msg)) return { packB64: "", bytes: 0, upToDate: true, ...head ? { head } : {} };
1783
+ if (base && /reached root|before base|not found|unknown|no (?:merge )?base|missing|invalid/i.test(msg)) {
1784
+ try {
1785
+ const full = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base: "", branch: BRANCH }, eng.STDERR));
1786
+ if (full && full.ok === true && typeof full.packB64 === "string") {
1787
+ return { packB64: full.packB64, bytes: full.bytes, divergent: true, ...head ? { head } : {} };
1788
+ }
1789
+ } catch {
1790
+ }
1791
+ }
1782
1792
  throw e;
1783
1793
  }
1784
1794
  if (!r || r.ok !== true || typeof r.packB64 !== "string") throw new Error("servePack: kernel produced no pack");
@@ -594,6 +594,126 @@ function summaryOfCapture(ctx, fromOid, toOid) {
594
594
  rowsByHome: Object.fromEntries([...ctx.rowsByHome.entries()].sort((a, b) => a[0] < b[0] ? -1 : 1))
595
595
  };
596
596
  }
597
+ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null, shardRouting = null, clientBase = null, defer = false } = {}) {
598
+ const main = await mainIntentIds(eng, ws);
599
+ const sessions = /* @__PURE__ */ new Map();
600
+ const deadLetters = [];
601
+ let admittedTotal = 0;
602
+ const born = [];
603
+ const birthOutcomes = [];
604
+ const timings = { spans: [], intents: 0, sessions: 0 };
605
+ const summaryCtx = shardRouting && shardRouting.selfLabel && scopedReads(eng).length > 0 ? { reads: scopedReads(eng), prevs: /* @__PURE__ */ new Map(), values: /* @__PURE__ */ new Map(), events: [], rowsDelta: 0, rederived: /* @__PURE__ */ new Set(), rowsByHome: /* @__PURE__ */ new Map(), homeTags: /* @__PURE__ */ new Map(), homeKeySet: /* @__PURE__ */ new Set() } : null;
606
+ if (summaryCtx) {
607
+ try {
608
+ for (const r of query(eng, ws, "aggregatesByType", JSON.stringify({ __type: "NomosHomeReceipt" }))) {
609
+ const hk = r.data && typeof r.data.homeKey === "string" ? r.data.homeKey : null;
610
+ if (!hk) continue;
611
+ summaryCtx.homeKeySet.add(hk);
612
+ summaryCtx.homeTags.set(await routeTagHexOfHomeKey(hk), hk);
613
+ }
614
+ } catch (e) {
615
+ log("home-tag-table-error", { ws, error: String(e).slice(0, 200) });
616
+ }
617
+ }
618
+ const gitdir0 = gitdirOf(ws);
619
+ const headBefore = summaryCtx ? kgit.resolveRef(eng, gitdir0, BRANCH) : null;
620
+ const t0 = Date.now();
621
+ let seq = 0;
622
+ const span = (name, kind, depth = 1, extra = {}) => {
623
+ const s0 = Date.now();
624
+ return (end = {}) => timings.spans.push({ id: `intent-offer-${t0}-${seq++}`, track: "cloud", name, kind, depth, t0: s0, t1: Date.now(), ...extra, ...end });
625
+ };
626
+ for (const offer of Array.isArray(offers) ? offers : []) {
627
+ const cid = typeof offer?.session === "string" && offer.session ? offer.session : "direct";
628
+ const rec = sessions.get(cid) || { session: cid, admitted: [], rejected: [], skipped: [] };
629
+ sessions.set(cid, rec);
630
+ const done = span(`intent offer ${cid.slice(0, 14)}`, "cadmit", 1, { client: cid });
631
+ let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
632
+ try {
633
+ if (!blob && typeof offer?.intentB64 === "string") blob = Uint8Array.from(atob(offer.intentB64), (c) => c.charCodeAt(0));
634
+ const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 : 8 * 1024 * 1024;
635
+ if (!blob || blob.byteLength === 0 || blob.byteLength > maxOfferBytes) {
636
+ rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: `intent offer must carry intentB64 <= ${maxOfferBytes / (1024 * 1024)} MiB` });
637
+ continue;
638
+ }
639
+ let doc = null;
640
+ try {
641
+ doc = JSON.parse(dec3.decode(blob));
642
+ } catch {
643
+ }
644
+ const intentId = typeof doc?.id === "string" ? doc.id : null;
645
+ const dom = typeof doc?.payload?.domain === "string" ? doc.payload.domain : null;
646
+ const dirId = typeof doc?.payload?.directiveId === "string" ? doc.payload.directiveId : null;
647
+ const oid = String(offer?.oid || intentId || "").slice(0, 10);
648
+ if (!dom) {
649
+ rec.rejected.push({ oid, error: "non-authored intent \u2014 governance enters its own admin lane" });
650
+ continue;
651
+ }
652
+ if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
653
+ const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes. Author it via POST /v1/workspaces/${ws}/author (owner Bearer), or \u2014 if this parked receipt is sanctioned \u2014 the owner unjams it: POST /v1/workspaces/${ws}/dead-letters/retry`;
654
+ rec.rejected.push({ oid, error, deadLettered: true });
655
+ deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
656
+ continue;
657
+ }
658
+ if (intentId && main.ids.has(intentId)) {
659
+ rec.skipped.push({ oid, intent: intentId.slice(0, 12), alreadyOnMain: true });
660
+ continue;
661
+ }
662
+ const cap = summaryCtx ? beginSummaryCapture(eng, ws, summaryCtx, blob) : null;
663
+ const _tOffer = Date.now();
664
+ const res = offerAdmit(eng, ws, blob, { defer: !summaryCtx });
665
+ log("offer-admit-ms", { ws, ms: Date.now() - _tOffer, deferred: !summaryCtx, ok: res.ok });
666
+ if (res.ok) {
667
+ if (intentId) main.ids.add(intentId);
668
+ main.oids.add(res.head);
669
+ main.head = res.head;
670
+ rec.admitted.push({ oid, head: res.head });
671
+ admittedTotal += 1;
672
+ if (Array.isArray(res.born) && res.born.length) born.push(...res.born);
673
+ if (Array.isArray(res.birthOutcomes) && res.birthOutcomes.length) birthOutcomes.push(...res.birthOutcomes);
674
+ if (cap) finishSummaryCapture(eng, ws, summaryCtx, cap, res.head, intentId);
675
+ } else {
676
+ const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
677
+ rec.rejected.push({ oid, error, deadLettered: true });
678
+ deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
679
+ }
680
+ } catch (e) {
681
+ rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: String(e).slice(0, 200) });
682
+ } finally {
683
+ done();
684
+ }
685
+ }
686
+ timings.sessions = sessions.size;
687
+ timings.intents = admittedTotal;
688
+ if (admittedTotal && !summaryCtx) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
689
+ if (deadLetters.length) {
690
+ try {
691
+ await materializeRefused(eng, ws, ledger, deadLetters);
692
+ } catch (e) {
693
+ log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
694
+ }
695
+ }
696
+ const deferDurability = defer && !summaryCtx;
697
+ if (admittedTotal && !deferDurability) {
698
+ const sealDone = span("seal offered intents", "cseal", 1, { admitted: admittedTotal });
699
+ await commitAndPush(eng, ws, ledger);
700
+ sealDone();
701
+ }
702
+ const gitdir = gitdirOf(ws);
703
+ const head = kgit.resolveRef(eng, gitdir, BRANCH) ?? main.head;
704
+ const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore, head) : null;
705
+ let resultPackB64 = null;
706
+ if (admittedTotal && head && /^[0-9a-f]{40}$/i.test(clientBase || "") && clientBase !== head) {
707
+ try {
708
+ const p = servePack(eng, ws, clientBase);
709
+ if (p && p.packB64 && p.bytes && p.bytes <= RESULT_PACK_INLINE_MAX) resultPackB64 = p.packB64;
710
+ } catch {
711
+ }
712
+ }
713
+ log("admit-intent-offers", { ws, sessions: sessions.size, admitted: admittedTotal, rejected: [...sessions.values()].reduce((n, s) => n + s.rejected.length, 0), resultPack: resultPackB64 ? "inline" : "none" });
714
+ const bornPacked = born.length ? packBornChildren(eng, born) : born;
715
+ return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, deadLetters, timings, ...summary ? { summary } : {}, ...bornPacked.length ? { born: bornPacked } : {}, ...birthOutcomes.length ? { birthOutcomes } : {}, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {}, ...deferDurability && admittedTotal && head ? { deferred: true, head } : {} };
716
+ }
597
717
  async function authorOn(eng, ws, ledger, domain, directiveId, payload, lawHash, opts = {}) {
598
718
  await mountWorkspace(eng, ws, ledger);
599
719
  let res;
@@ -794,7 +914,17 @@ function servePack(eng, ws, base = "") {
794
914
  try {
795
915
  r = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base, branch: BRANCH }, eng.STDERR));
796
916
  } catch (e) {
797
- if (/nothing to pack|head == base/.test(String(e))) return { packB64: "", bytes: 0, upToDate: true, ...head ? { head } : {} };
917
+ const msg = String(e);
918
+ if (/nothing to pack|head == base/.test(msg)) return { packB64: "", bytes: 0, upToDate: true, ...head ? { head } : {} };
919
+ if (base && /reached root|before base|not found|unknown|no (?:merge )?base|missing|invalid/i.test(msg)) {
920
+ try {
921
+ const full = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base: "", branch: BRANCH }, eng.STDERR));
922
+ if (full && full.ok === true && typeof full.packB64 === "string") {
923
+ return { packB64: full.packB64, bytes: full.bytes, divergent: true, ...head ? { head } : {} };
924
+ }
925
+ } catch {
926
+ }
927
+ }
798
928
  throw e;
799
929
  }
800
930
  if (!r || r.ok !== true || typeof r.packB64 !== "string") throw new Error("servePack: kernel produced no pack");
@@ -1166,6 +1296,35 @@ async function writeRefusedRef(eng, ws, dl) {
1166
1296
  });
1167
1297
  return ref;
1168
1298
  }
1299
+ async function listRefusedRefs(eng, ws) {
1300
+ const gitdir = gitdirOf(ws);
1301
+ const refs = kgit.listRefs(eng, gitdir, "refs/heads/refused/");
1302
+ const out = [];
1303
+ for (const { ref, oid } of refs) {
1304
+ try {
1305
+ const vb = kgit.readBlob(eng, gitdir, oid, "verdict.json");
1306
+ const ib = kgit.readBlob(eng, gitdir, oid, "intent.json");
1307
+ const v = JSON.parse(dec3.decode(vb));
1308
+ out.push({ ref, ...v, intentB64: b64Bytes(ib) });
1309
+ } catch {
1310
+ }
1311
+ }
1312
+ return out;
1313
+ }
1314
+ async function refusedSummary(eng, ws) {
1315
+ const gitdir = gitdirOf(ws);
1316
+ const refs = kgit.listRefs(eng, gitdir, "refs/heads/refused/");
1317
+ let count2 = 0, latestParkedAt = null;
1318
+ for (const { oid } of refs) {
1319
+ try {
1320
+ const v = JSON.parse(dec3.decode(kgit.readBlob(eng, gitdir, oid, "verdict.json")));
1321
+ count2++;
1322
+ if (v && typeof v.parkedAt === "string" && (!latestParkedAt || v.parkedAt > latestParkedAt)) latestParkedAt = v.parkedAt;
1323
+ } catch {
1324
+ }
1325
+ }
1326
+ return { count: count2, latestParkedAt };
1327
+ }
1169
1328
  async function materializeRefused(eng, ws, ledger, deadLetters) {
1170
1329
  const refs = [];
1171
1330
  const unparkedSessions = /* @__PURE__ */ new Set();
@@ -1181,6 +1340,20 @@ async function materializeRefused(eng, ws, ledger, deadLetters) {
1181
1340
  }
1182
1341
  return { refs, unparkedSessions };
1183
1342
  }
1343
+ async function deleteRefusedRef(eng, ws, ledger, offerId) {
1344
+ const gitdir = gitdirOf(ws);
1345
+ const ref = refusedRefName(offerId);
1346
+ try {
1347
+ kgit.deleteRef(eng, gitdir, ref);
1348
+ } catch {
1349
+ }
1350
+ try {
1351
+ await custodyPush(eng, ws, ledger, { remoteRef: ref, del: true });
1352
+ } catch (e) {
1353
+ log("refused-ref-delete-push-failed", { ws, ref, error: String(e).slice(0, 200) });
1354
+ }
1355
+ return ref;
1356
+ }
1184
1357
  async function boot(eng, ws, ledger) {
1185
1358
  const m = await mountWorkspace(eng, ws, ledger);
1186
1359
  if (nomosActive(eng, ws)) {
@@ -1578,6 +1751,34 @@ function receivePackCommands(raw) {
1578
1751
  // ../cloud/dev-server/server.mjs
1579
1752
  var DIR = path.dirname(fileURLToPath(import.meta.url));
1580
1753
  var REPO_ROOT = path.resolve(DIR, "../..");
1754
+ var RECOGNIZED_UNSUPPORTED = /* @__PURE__ */ new Set([
1755
+ "container-status",
1756
+ "recycle-container",
1757
+ "hostnames",
1758
+ "rebirth-custody",
1759
+ "upload-begin",
1760
+ "upload-gate",
1761
+ "upload-verify",
1762
+ "drain-reclaims",
1763
+ "drain-rebirths",
1764
+ "git-read-lease",
1765
+ "poke-admit",
1766
+ "clear-checkpoint",
1767
+ "domain-package",
1768
+ "capabilities"
1769
+ ]);
1770
+ var UNSUPPORTED_WHY = {
1771
+ "container-status": "the heavy container tier is Cloudflare-only (the dev cloud is one in-process engine)",
1772
+ "recycle-container": "the heavy container tier is Cloudflare-only (the dev cloud is one in-process engine)",
1773
+ "hostnames": "platform custom-hostname routing is a multi-tenant edge concern",
1774
+ "rebirth-custody": "the custodian rebirth lane is bound to Cloudflare Artifacts custody",
1775
+ "upload-begin": "the staged-upload birth variant \u2014 push to /v2/workspaces/<ws>/git instead (the upload-birth lane works locally)",
1776
+ "upload-gate": "the staged-upload birth variant \u2014 push to /v2/workspaces/<ws>/git instead",
1777
+ "upload-verify": "the staged-upload birth variant \u2014 push to /v2/workspaces/<ws>/git instead",
1778
+ "drain-reclaims": "an operator lawful-recovery drain (multi-node lifecycle)",
1779
+ "drain-rebirths": "an operator lawful-recovery drain (multi-node lifecycle)",
1780
+ "capabilities": "retired everywhere \u2014 host-side capability storage is gone (capabilities are holon-offered obligations now)"
1781
+ };
1581
1782
  var _realFetch = globalThis.fetch;
1582
1783
  globalThis.fetch = (input, init) => {
1583
1784
  const u = typeof input === "string" ? input : input && input.url || "";
@@ -1893,13 +2094,101 @@ async function startDevCloud(opts = {}) {
1893
2094
  sendJson(res, { ok: true, ws, sessions: out.sessions, main: out.main, ...out.timings ? { timings: out.timings } : {} });
1894
2095
  });
1895
2096
  }
2097
+ if (sub[0] === "intent-offers" && req.method === "POST") {
2098
+ return serialize(async () => {
2099
+ const ledger = ledgerFor(ws);
2100
+ await mountWorkspace(eng, ws, ledger);
2101
+ const body = JSON.parse((await readBody(req)).toString("utf8") || "{}");
2102
+ const offers = Array.isArray(body.offers) ? body.offers.slice(0, 100) : [];
2103
+ if (!offers.length) return sendJson(res, { ok: false, error: "body must be { offers:[{session, oid, intentB64}] }" }, 400);
2104
+ const clientBase = /^[0-9a-f]{40}$/i.test(body.base || "") ? body.base : null;
2105
+ const out = await admitIntentOffers(eng, ws, ledger, offers, { ...clientBase ? { clientBase } : {} });
2106
+ if (Array.isArray(out.born) && out.born.length) await persistBorn(out.born);
2107
+ const bornOut = Array.isArray(out.born) ? out.born.map(({ packB64, packBytes, ...b }) => b) : null;
2108
+ sendJson(res, {
2109
+ ok: true,
2110
+ ws,
2111
+ sessions: out.sessions,
2112
+ main: out.main,
2113
+ ...bornOut ? { born: bornOut } : {},
2114
+ ...out.birthOutcomes ? { birthOutcomes: out.birthOutcomes } : {},
2115
+ ...out.mainDelta ? { mainDelta: out.mainDelta } : {},
2116
+ ...out.resultPackB64 ? { resultPackB64: out.resultPackB64, resultBase: out.resultBase } : {},
2117
+ ...out.deadLetters && out.deadLetters.length ? { deadLetters: out.deadLetters.map(({ blob, ...m }) => m) } : {}
2118
+ });
2119
+ });
2120
+ }
2121
+ if (sub[0] === "dead-letters" && sub.length === 1 && req.method === "GET") {
2122
+ return serialize(async () => {
2123
+ await mountWorkspace(eng, ws, ledgerFor(ws));
2124
+ sendJson(res, { ok: true, ws, deadLetters: await listRefusedRefs(eng, ws) });
2125
+ });
2126
+ }
2127
+ if (sub[0] === "dlq-summary" && req.method === "GET") {
2128
+ return serialize(async () => {
2129
+ await mountWorkspace(eng, ws, ledgerFor(ws));
2130
+ sendJson(res, { ok: true, ws, ...await refusedSummary(eng, ws) });
2131
+ });
2132
+ }
2133
+ if (sub[0] === "dead-letters" && sub[1] === "retry" && req.method === "POST") {
2134
+ return serialize(async () => {
2135
+ const ledger = ledgerFor(ws);
2136
+ await mountWorkspace(eng, ws, ledger);
2137
+ const body = JSON.parse((await readBody(req)).toString("utf8") || "{}");
2138
+ const only = body.id || null;
2139
+ const refs = await listRefusedRefs(eng, ws);
2140
+ const retried = [], failed = [];
2141
+ for (const e of refs) {
2142
+ if (only && e.offerId !== only && e.oid !== only) continue;
2143
+ if (!e.intentB64) {
2144
+ failed.push({ intentId: e.offerId, error: "refused ref carries no intent bytes" });
2145
+ continue;
2146
+ }
2147
+ try {
2148
+ const r2 = offerAdmit(eng, ws, unb64(e.intentB64));
2149
+ if (r2.ok) retried.push({ intentId: e.offerId, head: r2.head });
2150
+ else failed.push({ intentId: e.offerId, error: String(r2.error || "").slice(0, 300) });
2151
+ } catch (err) {
2152
+ failed.push({ intentId: e.offerId, error: String(err).slice(0, 300) });
2153
+ }
2154
+ }
2155
+ if (retried.length) await commitAndPush(eng, ws, ledger);
2156
+ for (const r2 of retried) await deleteRefusedRef(eng, ws, ledger, r2.intentId);
2157
+ sendJson(res, { ok: true, ws, retried, failed, remaining: (await listRefusedRefs(eng, ws)).length });
2158
+ });
2159
+ }
2160
+ if (sub[0] === "dead-letters" && (sub[1] === "discard" || req.method === "DELETE")) {
2161
+ return serialize(async () => {
2162
+ const ledger = ledgerFor(ws);
2163
+ await mountWorkspace(eng, ws, ledger);
2164
+ const body = req.method === "DELETE" ? {} : JSON.parse((await readBody(req)).toString("utf8") || "{}");
2165
+ const only = body.id || url.searchParams.get("id") || null;
2166
+ const targets = (await listRefusedRefs(eng, ws)).filter((e) => !only || e.offerId === only || e.oid === only);
2167
+ for (const e of targets) await deleteRefusedRef(eng, ws, ledger, e.offerId || e.oid);
2168
+ sendJson(res, { ok: true, ws, discarded: targets.length });
2169
+ });
2170
+ }
2171
+ if (sub[0] === "rotate-secret" && req.method === "POST") {
2172
+ return sendJson(res, { ok: false, retired: true, error: "rotate-secret is retired \u2014 ownership is a ledger fact (sha256 of the push password on the law record); re-own by re-birthing via the upload lane" }, 410);
2173
+ }
1896
2174
  if (sub[0] === "pack" && req.method === "GET") {
1897
2175
  return serialize(async () => {
1898
2176
  await mountWorkspace(eng, ws, ledgerFor(ws));
1899
2177
  const have = url.searchParams.get("have") || "";
1900
- const r = servePack(eng, ws, have);
2178
+ let r;
2179
+ try {
2180
+ r = servePack(eng, ws, have);
2181
+ } catch (e) {
2182
+ return sendJson(res, { ok: false, custodyBreak: true, error: String(e && e.message || e).slice(0, 400), have }, 409);
2183
+ }
1901
2184
  if (!r.packB64) return sendBytes(res, Buffer.alloc(0), 204, r.head ? { "x-nomos-head": r.head } : {});
1902
- sendBytes(res, unb64(r.packB64), 200, { "content-type": "application/x-git-packfile", "x-nomos-pack": have ? "delta" : "full", ...r.head ? { "x-nomos-head": r.head } : {} });
2185
+ const kind = r.divergent ? "full" : have ? "delta" : "full";
2186
+ sendBytes(res, unb64(r.packB64), 200, {
2187
+ "content-type": "application/x-git-packfile",
2188
+ "x-nomos-pack": kind,
2189
+ ...r.divergent ? { "x-nomos-divergent": "1" } : {},
2190
+ ...r.head ? { "x-nomos-head": r.head } : {}
2191
+ });
1903
2192
  });
1904
2193
  }
1905
2194
  if (sub[0] === "snapshot" && req.method === "GET") {
@@ -1930,7 +2219,17 @@ async function startDevCloud(opts = {}) {
1930
2219
  const resolved = resolveGatewayOperation(spec, opName, method);
1931
2220
  if (resolved.ambiguous) return sendJson(res, { ok: false, error: "ambiguous operation", candidates: resolved.ambiguous }, 409);
1932
2221
  const x = resolved.op && resolved.op["x-nomos"];
1933
- if (!x) return sendJson(res, { ok: false, error: "unknown operation", see: `GET /v2/workspaces/${ws}` }, 404);
2222
+ if (!x) {
2223
+ if (RECOGNIZED_UNSUPPORTED.has(opName)) {
2224
+ return sendJson(res, {
2225
+ ok: false,
2226
+ unsupported: true,
2227
+ route: opName,
2228
+ error: `'${opName}' is a recognized Nomos Cloud route that 'githolon cloud dev' (single-node local) does not serve \u2014 ${UNSUPPORTED_WHY[opName] || "a multi-node / Cloudflare-infra concern"}. This is NOT an app bug; if you need it locally, ask the Nomos devs.`
2229
+ }, 501);
2230
+ }
2231
+ return sendJson(res, { ok: false, error: "unknown operation", see: `GET /v2/workspaces/${ws}` }, 404);
2232
+ }
1934
2233
  const principal = principalOf(req);
1935
2234
  const authToken = authTokenOf(req);
1936
2235
  if (x.kind === "offer" && method === "post") {
@@ -2044,6 +2343,15 @@ Sec-WebSocket-Accept: ${accept}\r
2044
2343
  socket.on("error", () => {
2045
2344
  });
2046
2345
  });
2346
+ if (!globalThis.__nomosDevGuards) {
2347
+ globalThis.__nomosDevGuards = true;
2348
+ process.on("unhandledRejection", (e) => {
2349
+ console.error("[dev-cloud] unhandledRejection (kept alive):", String(e && e.stack || e && e.message || e).slice(0, 500));
2350
+ });
2351
+ process.on("uncaughtException", (e) => {
2352
+ console.error("[dev-cloud] uncaughtException (kept alive):", String(e && e.stack || e && e.message || e).slice(0, 500));
2353
+ });
2354
+ }
2047
2355
  await new Promise((resolve) => server.listen(port, host, resolve));
2048
2356
  return { server, origin, dataDir, kernelSha, eng, custody, ledgerFor, serialize, persistBorn, close: () => new Promise((r) => server.close(r)) };
2049
2357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.84.1",
3
+ "version": "0.86.0",
4
4
  "type": "module",
5
5
  "description": "githolon — the Nomos developer CLI: Rails-style generators for @githolon/dsl domains + the package compiler. Kernel-independent.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@bjorn3/browser_wasi_shim": "0.4.2",
32
- "@githolon/client": "^0.84.1",
33
- "@githolon/dsl": "^0.84.1",
32
+ "@githolon/client": "^0.86.0",
33
+ "@githolon/dsl": "^0.86.0",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {