githolon 0.4.0 → 0.5.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 +947 -530
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -1,197 +1,40 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/cli.ts
4
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "node:fs";
5
- import { spawnSync as spawnSync4 } from "node:child_process";
6
- import { createRequire as createRequire2 } from "node:module";
7
- import { dirname as dirname4, join as join9, resolve as resolve2 } from "node:path";
8
- import { pathToFileURL as pathToFileURL4 } from "node:url";
9
-
10
- // src/generate.ts
11
- import { mkdirSync, existsSync, writeFileSync } from "node:fs";
12
- import { join } from "node:path";
13
-
14
- // src/naming.ts
15
- function words(raw) {
16
- return raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-\s]+/g, " ").trim().toLowerCase().split(" ").filter((w) => w.length > 0);
17
- }
18
- var cap = (w) => w.length === 0 ? w : w[0].toUpperCase() + w.slice(1);
19
- function pascalCase(raw) {
20
- return words(raw).map(cap).join("");
21
- }
22
- function camelCase(raw) {
23
- return words(raw).map((w, i) => i === 0 ? w : cap(w)).join("");
24
- }
25
- function snakeCase(raw) {
26
- return words(raw).join("_");
27
- }
28
- function screamingSnakeCase(raw) {
29
- return words(raw).join("_").toUpperCase();
30
- }
31
- function assertValidName(raw) {
32
- const ws = words(raw);
33
- if (ws.length === 0) {
34
- throw new Error(`Invalid name: '${raw}' \u2014 give at least one word, e.g. 'work_order'.`);
35
- }
36
- for (const w of ws) {
37
- if (!/^[a-z][a-z0-9]*$/.test(w)) {
38
- throw new Error(
39
- `Invalid name token '${w}' in '${raw}' \u2014 words must be alphanumeric and start with a letter (got after normalisation: ${ws.join(", ")}).`
40
- );
41
- }
42
- }
43
- return ws;
44
- }
45
-
46
- // src/templates.ts
47
- function namesFor(raw) {
48
- return {
49
- raw,
50
- pascal: pascalCase(raw),
51
- camel: camelCase(raw),
52
- statusesConst: `${screamingSnakeCase(raw)}_STATUSES`
53
- };
54
- }
55
- var HEADER = (raw, kind) => `/**
56
- * The \`${raw}\` ${kind} \u2014 scaffolded by \`githolon generate\`.
57
- *
58
- * Authoring rules (see @githolon/dsl): write ONLY aggregates (typed fields, each
59
- * tagged with a merge Driver) and directives (Zod payload + declarative plan).
60
- * You never write apply/fold/merge \u2014 the kernel owns the merge algebra.
61
- *
62
- * Convention: the fail-closed \`Conflict\` driver is the default for scalars.
63
- * Relax only what differs: \`.merge(Lww)\`, \`.merge(AddWins)\`, \`.merge(MapOf(Lww))\`.
64
- */`;
65
- function aggregateBlock(n) {
66
- return `export const ${n.statusesConst} = ["active", "retired"] as const;
67
-
68
- export const ${n.pascal} = aggregate("${n.pascal}", {
69
- // Default driver is the fail-closed \`Conflict\`: concurrent edits surface
70
- // as a conflict rather than silently coalescing. Declare only what differs.
71
- label: t.string().merge(Conflict),
72
- // Overrides \u2014 last-write-wins / set-union / per-key map:
73
- pos: t.int().merge(Lww),
74
- status: t.enum(${n.statusesConst}).merge(Lww),
75
- tags: t.set(t.string()).merge(AddWins),
76
- customFields: t.map(t.string()).merge(MapOf(Lww)),
77
- });`;
78
- }
79
- function domainTemplate(raw) {
80
- const n = namesFor(raw);
81
- return `${HEADER(raw, "domain")}
82
- import {
83
- z,
84
- aggregate,
85
- directive,
86
- t,
87
- Conflict,
88
- Lww,
89
- AddWins,
90
- MapOf,
91
- set,
92
- addToSet,
93
- setEntry,
94
- } from "@githolon/dsl";
95
-
96
- ${aggregateBlock(n)}
97
-
98
- /** create${n.pascal} \u2014 .creates the aggregate; seeds label/pos/status. */
99
- export const create${n.pascal} = directive("create${n.pascal}")
100
- .creates(${n.pascal})
101
- .payload(
102
- z.object({
103
- label: z.string(),
104
- pos: z.number().int(),
105
- status: z.enum(${n.statusesConst}),
106
- }),
107
- )
108
- .plan((p) => [
109
- set(${n.pascal}, "label", p.label),
110
- set(${n.pascal}, "pos", p.pos),
111
- set(${n.pascal}, "status", p.status),
112
- ]);
113
-
114
- /** move${n.pascal} \u2014 .mutates pos. */
115
- export const move${n.pascal} = directive("move${n.pascal}")
116
- .mutates(${n.pascal})
117
- .payload(z.object({ pos: z.number().int() }))
118
- .plan((p) => [set(${n.pascal}, "pos", p.pos)]);
119
-
120
- /** tag${n.pascal} \u2014 .mutates tags (add-wins union). */
121
- export const tag${n.pascal} = directive("tag${n.pascal}")
122
- .mutates(${n.pascal})
123
- .payload(z.object({ tags: z.array(z.string()) }))
124
- .plan((p) => [addToSet(${n.pascal}, "tags", p.tags)]);
125
-
126
- /** setCustomFieldOn${n.pascal} \u2014 .mutates customFields (a map entry). */
127
- export const setCustomFieldOn${n.pascal} = directive("setCustomFieldOn${n.pascal}")
128
- .mutates(${n.pascal})
129
- .payload(z.object({ key: z.string(), value: z.string() }))
130
- .plan((p) => [setEntry(${n.pascal}, "customFields", p.key, p.value)]);
131
- `;
132
- }
133
- function aggregateTemplate(raw) {
134
- const n = namesFor(raw);
135
- return `${HEADER(raw, "aggregate")}
136
- import { aggregate, t, Conflict, Lww, AddWins, MapOf } from "@githolon/dsl";
137
-
138
- ${aggregateBlock(n)}
139
- `;
140
- }
141
- function intentTemplate(raw) {
142
- const n = namesFor(raw);
143
- return `${HEADER(raw, "intent (directive)")}
144
- import { z, aggregate, directive, t, Conflict, set } from "@githolon/dsl";
145
-
146
- // Stub target \u2014 in a real domain, import the existing aggregate handle instead.
147
- export const ${n.pascal} = aggregate("${n.pascal}", {
148
- label: t.string().merge(Conflict),
149
- });
150
-
151
- /** ${n.camel} \u2014 .mutates the target aggregate. */
152
- export const ${n.camel} = directive("${n.camel}")
153
- .mutates(${n.pascal})
154
- .payload(z.object({ label: z.string() }))
155
- .plan((p) => [set(${n.pascal}, "label", p.label)]);
156
- `;
157
- }
158
-
159
- // src/generate.ts
160
- var RENDERERS = {
161
- domain: domainTemplate,
162
- aggregate: aggregateTemplate,
163
- intent: intentTemplate
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
164
10
  };
165
- function renderScaffold(kind, name) {
166
- assertValidName(name);
167
- const render = RENDERERS[kind];
168
- if (!render) {
169
- throw new Error(`Unknown generator kind '${kind}'. Use: domain | aggregate | intent.`);
170
- }
171
- return render(name);
172
- }
173
- function generate(opts) {
174
- const contents = renderScaffold(opts.kind, opts.name);
175
- const fileName = `${snakeCase(opts.name)}.ts`;
176
- const path = join(opts.outDir, fileName);
177
- if (existsSync(path) && !opts.force) {
178
- throw new Error(
179
- `Refusing to overwrite existing file: ${path}
180
- Re-run with --force to replace it.`
181
- );
182
- }
183
- mkdirSync(opts.outDir, { recursive: true });
184
- writeFileSync(path, contents, "utf8");
185
- return { path, contents };
186
- }
187
11
 
188
12
  // src/cloud.ts
13
+ var cloud_exports = {};
14
+ __export(cloud_exports, {
15
+ cloudBase: () => cloudBase,
16
+ configDir: () => configDir,
17
+ deploy: () => deploy,
18
+ describeLawMove: () => describeLawMove,
19
+ discoverDeployJson: () => discoverDeployJson,
20
+ getSecret: () => getSecret,
21
+ loadCreds: () => loadCreds,
22
+ login: () => login,
23
+ logout: () => logout,
24
+ principalHeaders: () => principalHeaders,
25
+ secretRotate: () => secretRotate,
26
+ secretSet: () => secretSet,
27
+ sessionToken: () => sessionToken,
28
+ setPrincipal: () => setPrincipal,
29
+ setSecret: () => setSecret,
30
+ whoami: () => whoami,
31
+ wsCreate: () => wsCreate,
32
+ wsRetire: () => wsRetire,
33
+ wsStatus: () => wsStatus
34
+ });
189
35
  import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
190
36
  import { join as join2 } from "node:path";
191
37
  import { homedir } from "node:os";
192
- var DEFAULT_CLOUD = "https://nomos.captainapp.co.uk";
193
- var DEFAULT_AUTH_URL = "https://kjbcjkihxskuwwfdqklt.supabase.co";
194
- var DEFAULT_AUTH_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtqYmNqa2loeHNrdXd3ZmRxa2x0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTA3MDU2OTAsImV4cCI6MjA2NjI4MTY5MH0.V9e7XsuTlTOLqefOIedTqlBiTxUSn4O5FZSPWwAxiSI";
195
38
  function cloudBase(flag) {
196
39
  return (flag ?? process.env["NOMOS_CLOUD"] ?? DEFAULT_CLOUD).replace(/\/+$/, "");
197
40
  }
@@ -216,7 +59,6 @@ function saveCreds(c) {
216
59
  writeFileSync2(credsPath(), JSON.stringify(c, null, 2) + "\n", "utf8");
217
60
  chmodSync(credsPath(), 384);
218
61
  }
219
- var secretKey = (cloud, ws) => `${cloud} ${ws}`;
220
62
  function getSecret(cloud, ws) {
221
63
  return loadCreds().secrets[secretKey(cloud, ws)];
222
64
  }
@@ -289,8 +131,6 @@ function discoverDeployJson(cwd, explicit) {
289
131
  }
290
132
  return join2(buildDir, bodies[0]);
291
133
  }
292
- var out = (s) => void process.stdout.write(s + "\n");
293
- var err = (s) => void process.stderr.write("error: " + s + "\n");
294
134
  async function login(opts) {
295
135
  if (opts.agent !== true && opts.token === void 0) {
296
136
  err(
@@ -483,65 +323,18 @@ async function secretRotate(ws, opts) {
483
323
  out(`\u2713 secret for ${ws} rotated and saved \u2192 ${credsPath()}`);
484
324
  return 0;
485
325
  }
486
-
487
- // src/compile.ts
488
- import { spawn, spawnSync } from "node:child_process";
489
- import { createRequire } from "node:module";
490
- import { dirname, join as join3 } from "node:path";
491
- import { pathToFileURL } from "node:url";
492
- function resolveCompileLauncher(cwd) {
493
- const resolveDslPkg = (fromDir) => {
494
- try {
495
- const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join3(fromDir, "noop.js")));
496
- return req.resolve("@githolon/dsl/package.json");
497
- } catch {
498
- return void 0;
499
- }
500
- };
501
- const dslPkg = resolveDslPkg(cwd) ?? resolveDslPkg(void 0);
502
- return dslPkg === void 0 ? void 0 : join3(dirname(dslPkg), "compile_package.mjs");
503
- }
504
- var NO_DSL_REMEDY = "@githolon/dsl not found \u2014 add it to your project's dependencies";
505
- function runCompile(args) {
506
- const launcher = resolveCompileLauncher(process.cwd());
507
- if (launcher === void 0) {
508
- process.stderr.write(`error: ${NO_DSL_REMEDY}
509
- `);
510
- return 1;
511
- }
512
- const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
513
- return r.status ?? 1;
514
- }
515
- function compileAsync(cwd, args = []) {
516
- const launcher = resolveCompileLauncher(cwd);
517
- const t0 = performance.now();
518
- if (launcher === void 0) {
519
- return Promise.resolve({ ok: false, ms: 0, output: NO_DSL_REMEDY });
326
+ var DEFAULT_CLOUD, DEFAULT_AUTH_URL, DEFAULT_AUTH_ANON_KEY, secretKey, out, err;
327
+ var init_cloud = __esm({
328
+ "src/cloud.ts"() {
329
+ "use strict";
330
+ DEFAULT_CLOUD = "https://nomos.captainapp.co.uk";
331
+ DEFAULT_AUTH_URL = "https://kjbcjkihxskuwwfdqklt.supabase.co";
332
+ DEFAULT_AUTH_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtqYmNqa2loeHNrdXd3ZmRxa2x0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTA3MDU2OTAsImV4cCI6MjA2NjI4MTY5MH0.V9e7XsuTlTOLqefOIedTqlBiTxUSn4O5FZSPWwAxiSI";
333
+ secretKey = (cloud, ws) => `${cloud} ${ws}`;
334
+ out = (s) => void process.stdout.write(s + "\n");
335
+ err = (s) => void process.stderr.write("error: " + s + "\n");
520
336
  }
521
- return new Promise((resolveRun) => {
522
- const chunks = [];
523
- const dec3 = new TextDecoder();
524
- const child = spawn(process.execPath, [launcher, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
525
- child.stdout?.on("data", (c) => chunks.push(dec3.decode(c)));
526
- child.stderr?.on("data", (c) => chunks.push(dec3.decode(c)));
527
- child.on("error", (e) => resolveRun({ ok: false, ms: performance.now() - t0, output: String(e) }));
528
- child.on(
529
- "close",
530
- (code) => resolveRun({ ok: code === 0, ms: performance.now() - t0, output: chunks.join("") })
531
- );
532
- });
533
- }
534
-
535
- // src/dev.ts
536
- import { existsSync as existsSync5, readFileSync as readFileSync3, watch } from "node:fs";
537
- import { createServer } from "node:http";
538
- import { dirname as dirname3, join as join6, resolve } from "node:path";
539
- import { pathToFileURL as pathToFileURL3 } from "node:url";
540
-
541
- // vendor/engine/engine.mjs
542
- import { WASI, File, Directory, OpenFile, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
543
- import git from "isomorphic-git";
544
- import http from "isomorphic-git/http/web";
337
+ });
545
338
 
546
339
  // vendor/engine/git-fs.mjs
547
340
  function fsErr(code, p) {
@@ -640,21 +433,26 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
640
433
  };
641
434
  return { promises };
642
435
  }
436
+ var init_git_fs = __esm({
437
+ "vendor/engine/git-fs.mjs"() {
438
+ "use strict";
439
+ }
440
+ });
643
441
 
644
442
  // vendor/engine/tree.mjs
645
- var enc = new TextEncoder();
646
- var dec = new TextDecoder();
443
+ var enc, dec;
444
+ var init_tree = __esm({
445
+ "vendor/engine/tree.mjs"() {
446
+ "use strict";
447
+ enc = new TextEncoder();
448
+ dec = new TextDecoder();
449
+ }
450
+ });
647
451
 
648
452
  // vendor/engine/engine.mjs
649
- var enc2 = new TextEncoder();
650
- var dec2 = new TextDecoder();
651
- var BRANCH = "main";
652
- var repoArgOf = (ws) => `/work/ws/${ws}`;
653
- var gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
654
- var unpack = (p) => {
655
- const v = typeof p === "bigint" ? p : BigInt(p);
656
- return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
657
- };
453
+ import { WASI, File, Directory, OpenFile, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
454
+ import git from "isomorphic-git";
455
+ import http from "isomorphic-git/http/web";
658
456
  async function sha256hex(t) {
659
457
  const b = await crypto.subtle.digest("SHA-256", enc2.encode(t));
660
458
  return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");
@@ -662,13 +460,13 @@ async function sha256hex(t) {
662
460
  function osEntropyBuffer(n) {
663
461
  const bytes = new Uint8Array(n * 8);
664
462
  for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
665
- const out8 = new Array(n), T = 2 ** 53;
463
+ const out9 = new Array(n), T = 2 ** 53;
666
464
  for (let i = 0; i < n; i++) {
667
465
  let z = 0n;
668
466
  for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
669
- out8[i] = Number(z >> 11n) / T;
467
+ out9[i] = Number(z >> 11n) / T;
670
468
  }
671
- return out8;
469
+ return out9;
672
470
  }
673
471
  function stringifyBig(o) {
674
472
  return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
@@ -694,7 +492,6 @@ function seedManifests(m, strings) {
694
492
  m.set("domain_manifests.json", new File(enc2.encode(strings.read)));
695
493
  m.set("identity-manifests.json", new File(enc2.encode(strings.identity)));
696
494
  }
697
- var installPayload = (hash, usda, installedBy) => ({ domainHash: hash, packageUsda: usda, installedBy, authorityScope: "workspace/root", dependencies: [], finalizers: [] });
698
495
  async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, manifestStrings, replica, installedBy = "nomos-cloud" }) {
699
496
  const hashes = { bootstrap: await sha256hex(bootstrapPkg), nomos: await sha256hex(nomosPkg) };
700
497
  const root = /* @__PURE__ */ new Map();
@@ -780,6 +577,9 @@ function unmount(eng, ws) {
780
577
  function writeWork(eng, name, bytes) {
781
578
  eng.preopen.dir.contents.set(name, new File(bytes));
782
579
  }
580
+ function mintId(eng, typeTag) {
581
+ return JSON.parse(call(eng.ex, "mint", { typeTag, nowMillis: Date.now() }, eng.STDERR)).id;
582
+ }
783
583
  function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}) {
784
584
  const seq = eng.seq++;
785
585
  const envelope = { payload: { domain, directiveId, payload }, captured_ports: { clock: { physical: Date.now(), logical: 0, replica: eng.replica }, rng: osEntropyBuffer(opts.rngDraws ?? 64) }, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
@@ -791,10 +591,6 @@ function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}
791
591
  if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
792
592
  return res;
793
593
  }
794
- var qById = (eng, ws, id) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, branch: BRANCH }, eng.STDERR));
795
- var query = (eng, ws, queryId, paramsJson) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, branch: BRANCH }, eng.STDERR));
796
- var count = (eng, ws, countId, groupKey) => JSON.parse(call(eng.ex, "count", { repoArg: repoArgOf(ws), workspace: ws, countId, groupKey, branch: BRANCH }, eng.STDERR));
797
- var sum = (eng, ws, sumId, groupKey) => JSON.parse(call(eng.ex, "sum", { repoArg: repoArgOf(ws), workspace: ws, sumId, groupKey, branch: BRANCH }, eng.STDERR));
798
594
  function applyIntentBytes(eng, ws, bytes) {
799
595
  const name = `intent-in-${eng.seq++}.json`;
800
596
  writeWork(eng, name, bytes);
@@ -803,14 +599,34 @@ function applyIntentBytes(eng, ws, bytes) {
803
599
  function verifyChainLocal(eng, ws) {
804
600
  return JSON.parse(call(eng.ex, "verify_chain", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng.STDERR));
805
601
  }
602
+ var enc2, dec2, BRANCH, repoArgOf, gitdirOf, unpack, installPayload, qById, query, count, sum;
603
+ var init_engine = __esm({
604
+ "vendor/engine/engine.mjs"() {
605
+ "use strict";
606
+ init_git_fs();
607
+ init_tree();
608
+ enc2 = new TextEncoder();
609
+ dec2 = new TextDecoder();
610
+ BRANCH = "main";
611
+ repoArgOf = (ws) => `/work/ws/${ws}`;
612
+ gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
613
+ unpack = (p) => {
614
+ const v = typeof p === "bigint" ? p : BigInt(p);
615
+ return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
616
+ };
617
+ installPayload = (hash, usda, installedBy, dispositions) => ({ domainHash: hash, packageUsda: usda, installedBy, authorityScope: "workspace/root", dependencies: [], finalizers: [], ...dispositions ? { dispositions } : {} });
618
+ qById = (eng, ws, id) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, branch: BRANCH }, eng.STDERR));
619
+ query = (eng, ws, queryId, paramsJson) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, branch: BRANCH }, eng.STDERR));
620
+ count = (eng, ws, countId, groupKey) => JSON.parse(call(eng.ex, "count", { repoArg: repoArgOf(ws), workspace: ws, countId, groupKey, branch: BRANCH }, eng.STDERR));
621
+ sum = (eng, ws, sumId, groupKey) => JSON.parse(call(eng.ex, "sum", { repoArg: repoArgOf(ws), workspace: ws, sumId, groupKey, branch: BRANCH }, eng.STDERR));
622
+ }
623
+ });
806
624
 
807
625
  // src/local_holon.ts
808
626
  import { randomBytes } from "node:crypto";
809
627
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync3 } from "node:fs";
810
628
  import { dirname as dirname2, join as join4 } from "node:path";
811
629
  import { File as File2, Directory as Directory2 } from "@bjorn3/browser_wasi_shim";
812
- var out2 = (s) => void process.stdout.write(s + "\n");
813
- var err2 = (s) => void process.stderr.write("error: " + s + "\n");
814
630
  async function fetchJsonCached(url, cacheFile) {
815
631
  try {
816
632
  const r = await fetch(url);
@@ -856,62 +672,620 @@ function mergeManifests(baked, overlay) {
856
672
  }
857
673
  }
858
674
  }
859
- return { read: JSON.stringify(read), identity: JSON.stringify(identity) };
860
- }
861
- function writeTreeToDisk(dir, diskPath) {
862
- mkdirSync3(diskPath, { recursive: true });
863
- for (const [name, inode] of dir.contents) {
864
- const p = join4(diskPath, name);
865
- if (inode.contents instanceof Map) writeTreeToDisk(inode, p);
866
- else writeFileSync3(p, inode.data ?? new Uint8Array(0));
675
+ return { read: JSON.stringify(read), identity: JSON.stringify(identity) };
676
+ }
677
+ function writeTreeToDisk(dir, diskPath) {
678
+ mkdirSync3(diskPath, { recursive: true });
679
+ for (const [name, inode] of dir.contents) {
680
+ const p = join4(diskPath, name);
681
+ if (inode.contents instanceof Map) writeTreeToDisk(inode, p);
682
+ else writeFileSync3(p, inode.data ?? new Uint8Array(0));
683
+ }
684
+ }
685
+ function readTreeFromDisk(diskPath) {
686
+ const contents = /* @__PURE__ */ new Map();
687
+ for (const name of readdirSync2(diskPath)) {
688
+ const p = join4(diskPath, name);
689
+ if (statSync(p).isDirectory()) contents.set(name, readTreeFromDisk(p));
690
+ else contents.set(name, new File2(readFileSync2(p)));
691
+ }
692
+ return new Directory2(contents);
693
+ }
694
+ async function holonEngine(cloud, overlay, installedBy) {
695
+ const runtime = await fetchRuntime(cloud);
696
+ const manifestStrings = overlay === void 0 ? { read: runtime.manifests.domainManifests, identity: runtime.manifests.identityManifests } : mergeManifests(runtime.manifests, overlay);
697
+ const replica = BigInt(`0x${randomBytes(8).toString("hex")}`) & (1n << 63n) - 1n;
698
+ const eng = await createEngine({
699
+ wasmModule: await WebAssembly.compile(runtime.wasmBytes),
700
+ bootstrapPkg: runtime.packages.bootstrap,
701
+ nomosPkg: runtime.packages.nomos,
702
+ manifestStrings,
703
+ replica,
704
+ installedBy
705
+ });
706
+ return { eng, runtime };
707
+ }
708
+ async function mountFresh(eng, ws) {
709
+ await mountWorkspace(eng, ws, { remote: "https://unborn.invalid/", headers: {} });
710
+ }
711
+ function sealedIntentOf(eng, ws, res) {
712
+ if (typeof res.intentOut !== "string") return void 0;
713
+ const f = eng.preopen.dir.contents.get("ws").contents.get(ws)?.contents.get(res.intentOut);
714
+ if (f === void 0 || !(f.data instanceof Uint8Array)) return void 0;
715
+ try {
716
+ return JSON.parse(new TextDecoder().decode(f.data));
717
+ } catch {
718
+ return void 0;
719
+ }
720
+ }
721
+ function printVerdict(v) {
722
+ if (v["valid"] === true) {
723
+ out3(`\u2713 chain VALID \u2014 head ${String(v["head"]).slice(0, 12)}\u2026, ${v["intents"]} intent(s), ${v["plansRerun"]} plan(s) re-run`);
724
+ out3(` controller ${String(v["controllerHash"]).slice(0, 12)}\u2026 | installed: ${v["installedDomains"]?.map((h) => h.slice(0, 12) + "\u2026").join(", ") ?? "\u2014"}`);
725
+ return true;
726
+ }
727
+ err3(`chain INVALID at ${v["check"]}${v["atIndex"] !== void 0 ? ` (intent ${v["atIndex"]})` : ""}: ${v["error"]}`);
728
+ return false;
729
+ }
730
+ var out3, err3;
731
+ var init_local_holon = __esm({
732
+ "src/local_holon.ts"() {
733
+ "use strict";
734
+ init_engine();
735
+ init_cloud();
736
+ out3 = (s) => void process.stdout.write(s + "\n");
737
+ err3 = (s) => void process.stderr.write("error: " + s + "\n");
738
+ }
739
+ });
740
+
741
+ // src/proof_offline.ts
742
+ var proof_offline_exports = {};
743
+ __export(proof_offline_exports, {
744
+ parseOfflineLegs: () => parseOfflineLegs,
745
+ runOfflineLegs: () => runOfflineLegs,
746
+ runOfflineProofStandalone: () => runOfflineProofStandalone
747
+ });
748
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
749
+ import { basename, dirname as dirname3, join as join6 } from "node:path";
750
+ function parseOfflineLegs(proofSource) {
751
+ if (!proofSource.includes("AUTO-GENERATED by nomos-compile")) {
752
+ throw new Error("not a generated proof (missing the AUTO-GENERATED header) \u2014 run `githolon compile` to (re)generate build/<name>.proof.mts");
753
+ }
754
+ const marker = proofSource.match(/^\/\/ @nomos-proof-legs (\{.*\})$/m);
755
+ if (marker !== null) {
756
+ return JSON.parse(marker[1]);
757
+ }
758
+ const dispatch = proofSource.match(/^await app\.(\w+)\((\{.*\})\);$/m);
759
+ if (dispatch === null) {
760
+ throw new Error("generated proof has no `await app.<directive>({...})` dispatch \u2014 regenerate with `githolon compile` (a creating directive is required)");
761
+ }
762
+ const directiveId = dispatch[1];
763
+ const payload = JSON.parse(dispatch[2]);
764
+ const dom = proofSource.match(/dispatch (\w+)\/(\w+) — offline write/);
765
+ if (dom === null || dom[2] !== directiveId) {
766
+ throw new Error("generated proof names no `dispatch <domain>/<directive> \u2014 offline write` \u2014 regenerate with `githolon compile`");
767
+ }
768
+ const domain = dom[1];
769
+ const autoStamped = [];
770
+ const stampLine = proofSource.split("\n").find((l) => l.includes("omitted \u2014 the client auto-stamps ISO now()"));
771
+ if (stampLine !== void 0) {
772
+ for (const m of stampLine.matchAll(/`(\w+)`/g)) autoStamped.push(m[1]);
773
+ }
774
+ let queryLeg;
775
+ const q = proofSource.match(/local declared query (\w+):/);
776
+ if (q !== null) {
777
+ const call2 = proofSource.match(/^const rows = await app\.\w+\((\{.*\})\);$/m);
778
+ if (call2 === null) throw new Error(`generated proof names query '${q[1]}' but its accessor call is missing \u2014 regenerate with \`githolon compile\``);
779
+ queryLeg = { id: q[1], params: JSON.parse(call2[1]) };
780
+ }
781
+ let countLeg;
782
+ const c = proofSource.match(/local declared count (\w+):/);
783
+ if (c !== null) {
784
+ const call2 = proofSource.match(/^const localCount = await app\.\w+\((.*?)\);$/m);
785
+ if (call2 === null) throw new Error(`generated proof names count '${c[1]}' but its accessor call is missing \u2014 regenerate with \`githolon compile\``);
786
+ const arg = call2[1].trim();
787
+ countLeg = { id: c[1], group: arg.length === 0 ? "" : JSON.parse(arg) };
788
+ }
789
+ return {
790
+ domain,
791
+ directiveId,
792
+ payload,
793
+ autoStamped,
794
+ ...queryLeg !== void 0 ? { query: queryLeg } : {},
795
+ ...countLeg !== void 0 ? { count: countLeg } : {}
796
+ };
797
+ }
798
+ function runOfflineLegs(eng, ws, lawHash, legs) {
799
+ const t0 = performance.now();
800
+ const done = (ok, lines2, error) => ({
801
+ ok,
802
+ ms: performance.now() - t0,
803
+ legs: lines2,
804
+ ...error !== void 0 ? { error } : {}
805
+ });
806
+ const lines = [];
807
+ const payload = { ...legs.payload };
808
+ const stamp = (/* @__PURE__ */ new Date()).toISOString();
809
+ for (const f of legs.autoStamped) payload[f] = stamp;
810
+ if (legs.mintField !== void 0 && legs.aggregateId !== void 0) {
811
+ payload[legs.mintField] = mintId(eng, legs.aggregateId);
812
+ }
813
+ const res = author(eng, ws, legs.domain, legs.directiveId, payload, lawHash);
814
+ if (res.ok === false) {
815
+ return done(false, lines, `dispatch ${legs.domain}/${legs.directiveId} refused: ${res.error ?? "unknown"} \u2014 the law itself rejected its own synthesized sample; check the directive's plan/payload schema`);
816
+ }
817
+ const createdId = (sealedIntentOf(eng, ws, res)?.events ?? []).find((e) => e.marker === "Create")?.aggregate;
818
+ lines.push(`\u2713 dispatch ${legs.domain}/${legs.directiveId} \u2014 offline write under the new law${createdId !== void 0 ? ` (${createdId.slice(0, 28)}\u2026)` : ""}`);
819
+ if (legs.query !== void 0) {
820
+ const rows = query(eng, ws, legs.query.id, JSON.stringify(legs.query.params));
821
+ if (rows.length !== 1 || createdId !== void 0 && rows[0].id !== createdId) {
822
+ return done(false, lines, `declared query ${legs.query.id} expected the 1 created row, got ${JSON.stringify(rows.map((r) => r.id))} \u2014 check the query's key fields against the directive's payload`);
823
+ }
824
+ lines.push(`\u2713 declared query ${legs.query.id} answers locally \u2014 1 row`);
825
+ }
826
+ if (legs.count !== void 0) {
827
+ const c = count(eng, ws, legs.count.id, legs.count.group).count;
828
+ if (c !== 1) {
829
+ return done(false, lines, `declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) expected 1, got ${c} \u2014 check the count's grouping against the created row`);
830
+ }
831
+ lines.push(`\u2713 declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) = 1 locally`);
832
+ }
833
+ return done(true, lines);
834
+ }
835
+ async function runOfflineProofStandalone(proofPath, cloud, domain) {
836
+ const src = readFileSync3(proofPath, "utf8");
837
+ const packageName = basename(proofPath).replace(/\.proof\.mts$/, "");
838
+ const deployFile = join6(dirname3(proofPath), `${packageName}.deploy.json`);
839
+ if (!existsSync5(deployFile)) {
840
+ throw new Error(`missing ${deployFile} \u2014 run \`githolon compile\` (the proof and the deploy body are built together)`);
841
+ }
842
+ let legs;
843
+ const legsFile = join6(dirname3(proofPath), `${packageName}.proof-legs.json`);
844
+ if (existsSync5(legsFile)) {
845
+ const index = JSON.parse(readFileSync3(legsFile, "utf8"));
846
+ const keys = Object.keys(index.domains);
847
+ const chosen = domain ?? index.default;
848
+ if (index.domains[chosen] === void 0) {
849
+ throw new Error(`no provable domain '${chosen}' in this package \u2014 it proves: ${keys.join(", ")} (pick one: githolon proof --domain <key>)`);
850
+ }
851
+ legs = index.domains[chosen];
852
+ if (keys.length > 1) console.log(`githolon proof \u2014 domain '${chosen}' (of ${keys.length}: ${keys.join(", ")}; --domain <key> to switch)`);
853
+ } else {
854
+ if (domain !== void 0) throw new Error(`--domain needs a per-domain legs index (build/${packageName}.proof-legs.json) \u2014 recompile with \`githolon compile\` to emit it`);
855
+ legs = parseOfflineLegs(src);
856
+ }
857
+ const deployBody = JSON.parse(readFileSync3(deployFile, "utf8"));
858
+ const lawHash = await sha256hex(deployBody.packageUsda);
859
+ console.log(`githolon proof \u2014 OFFLINE legs on a local holon (the cloud loop: githolon proof --live)`);
860
+ const { eng } = await holonEngine(cloud, deployBody, "githolon-proof");
861
+ const ws = "proof-offline";
862
+ await mountFresh(eng, ws);
863
+ author(eng, ws, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, "githolon-proof"), "");
864
+ author(eng, ws, "nomos", "installDomain", installPayload(lawHash, deployBody.packageUsda, "githolon-proof"), eng.hashes.nomos);
865
+ const v = runOfflineLegs(eng, ws, lawHash, legs);
866
+ for (const l of v.legs) console.log(l);
867
+ if (!v.ok) {
868
+ console.error(`\u2717 ${v.error}`);
869
+ return 1;
870
+ }
871
+ console.log(`
872
+ OFFLINE PROOF GREEN (${Math.round(v.ms)}ms) \u2014 the same legs rerun on every save in \`githolon dev\`.`);
873
+ console.log(`Deploy with confidence when ready: githolon ws create <ws> && githolon deploy <ws> (or the full cloud proof: githolon proof --live)`);
874
+ return 0;
875
+ }
876
+ var init_proof_offline = __esm({
877
+ "src/proof_offline.ts"() {
878
+ "use strict";
879
+ init_engine();
880
+ init_local_holon();
881
+ }
882
+ });
883
+
884
+ // src/cli.ts
885
+ import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
886
+ import { spawnSync as spawnSync5 } from "node:child_process";
887
+ import { createRequire as createRequire2 } from "node:module";
888
+ import { dirname as dirname5, join as join10, resolve as resolve2 } from "node:path";
889
+ import { pathToFileURL as pathToFileURL4 } from "node:url";
890
+
891
+ // src/generate.ts
892
+ import { mkdirSync, existsSync, writeFileSync } from "node:fs";
893
+ import { join } from "node:path";
894
+
895
+ // src/naming.ts
896
+ function words(raw) {
897
+ return raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-\s]+/g, " ").trim().toLowerCase().split(" ").filter((w) => w.length > 0);
898
+ }
899
+ var cap = (w) => w.length === 0 ? w : w[0].toUpperCase() + w.slice(1);
900
+ function pascalCase(raw) {
901
+ return words(raw).map(cap).join("");
902
+ }
903
+ function camelCase(raw) {
904
+ return words(raw).map((w, i) => i === 0 ? w : cap(w)).join("");
905
+ }
906
+ function snakeCase(raw) {
907
+ return words(raw).join("_");
908
+ }
909
+ function screamingSnakeCase(raw) {
910
+ return words(raw).join("_").toUpperCase();
911
+ }
912
+ function assertValidName(raw) {
913
+ const ws = words(raw);
914
+ if (ws.length === 0) {
915
+ throw new Error(`Invalid name: '${raw}' \u2014 give at least one word, e.g. 'work_order'.`);
916
+ }
917
+ for (const w of ws) {
918
+ if (!/^[a-z][a-z0-9]*$/.test(w)) {
919
+ throw new Error(
920
+ `Invalid name token '${w}' in '${raw}' \u2014 words must be alphanumeric and start with a letter (got after normalisation: ${ws.join(", ")}).`
921
+ );
922
+ }
923
+ }
924
+ return ws;
925
+ }
926
+
927
+ // src/templates.ts
928
+ function namesFor(raw) {
929
+ return {
930
+ raw,
931
+ pascal: pascalCase(raw),
932
+ camel: camelCase(raw),
933
+ statusesConst: `${screamingSnakeCase(raw)}_STATUSES`
934
+ };
935
+ }
936
+ var HEADER = (raw, kind) => `/**
937
+ * The \`${raw}\` ${kind} \u2014 scaffolded by \`githolon generate\`.
938
+ *
939
+ * Authoring rules (see @githolon/dsl): write ONLY aggregates (typed fields, each
940
+ * tagged with a merge Driver) and directives (Zod payload + declarative plan).
941
+ * You never write apply/fold/merge \u2014 the kernel owns the merge algebra.
942
+ *
943
+ * Convention: the fail-closed \`Conflict\` driver is the default for scalars.
944
+ * Relax only what differs: \`.merge(Lww)\`, \`.merge(AddWins)\`, \`.merge(MapOf(Lww))\`.
945
+ */`;
946
+ function aggregateBlock(n) {
947
+ return `export const ${n.statusesConst} = ["active", "retired"] as const;
948
+
949
+ export const ${n.pascal} = aggregate("${n.pascal}", {
950
+ // Default driver is the fail-closed \`Conflict\`: concurrent edits surface
951
+ // as a conflict rather than silently coalescing. Declare only what differs.
952
+ label: t.string().merge(Conflict),
953
+ // Overrides \u2014 last-write-wins / set-union / per-key map:
954
+ pos: t.int().merge(Lww),
955
+ status: t.enum(${n.statusesConst}).merge(Lww),
956
+ tags: t.set(t.string()).merge(AddWins),
957
+ customFields: t.map(t.string()).merge(MapOf(Lww)),
958
+ });`;
959
+ }
960
+ function domainTemplate(raw) {
961
+ const n = namesFor(raw);
962
+ return `${HEADER(raw, "domain")}
963
+ import {
964
+ z,
965
+ aggregate,
966
+ directive,
967
+ t,
968
+ Conflict,
969
+ Lww,
970
+ AddWins,
971
+ MapOf,
972
+ set,
973
+ addToSet,
974
+ setEntry,
975
+ } from "@githolon/dsl";
976
+
977
+ ${aggregateBlock(n)}
978
+
979
+ /** create${n.pascal} \u2014 .creates the aggregate; seeds label/pos/status. */
980
+ export const create${n.pascal} = directive("create${n.pascal}")
981
+ .creates(${n.pascal})
982
+ .payload(
983
+ z.object({
984
+ label: z.string(),
985
+ pos: z.number().int(),
986
+ status: z.enum(${n.statusesConst}),
987
+ }),
988
+ )
989
+ .plan((p) => [
990
+ set(${n.pascal}, "label", p.label),
991
+ set(${n.pascal}, "pos", p.pos),
992
+ set(${n.pascal}, "status", p.status),
993
+ ]);
994
+
995
+ /** move${n.pascal} \u2014 .mutates pos. */
996
+ export const move${n.pascal} = directive("move${n.pascal}")
997
+ .mutates(${n.pascal})
998
+ .payload(z.object({ pos: z.number().int() }))
999
+ .plan((p) => [set(${n.pascal}, "pos", p.pos)]);
1000
+
1001
+ /** tag${n.pascal} \u2014 .mutates tags (add-wins union). */
1002
+ export const tag${n.pascal} = directive("tag${n.pascal}")
1003
+ .mutates(${n.pascal})
1004
+ .payload(z.object({ tags: z.array(z.string()) }))
1005
+ .plan((p) => [addToSet(${n.pascal}, "tags", p.tags)]);
1006
+
1007
+ /** setCustomFieldOn${n.pascal} \u2014 .mutates customFields (a map entry). */
1008
+ export const setCustomFieldOn${n.pascal} = directive("setCustomFieldOn${n.pascal}")
1009
+ .mutates(${n.pascal})
1010
+ .payload(z.object({ key: z.string(), value: z.string() }))
1011
+ .plan((p) => [setEntry(${n.pascal}, "customFields", p.key, p.value)]);
1012
+ `;
1013
+ }
1014
+ function aggregateTemplate(raw) {
1015
+ const n = namesFor(raw);
1016
+ return `${HEADER(raw, "aggregate")}
1017
+ import { aggregate, t, Conflict, Lww, AddWins, MapOf } from "@githolon/dsl";
1018
+
1019
+ ${aggregateBlock(n)}
1020
+ `;
1021
+ }
1022
+ function intentTemplate(raw) {
1023
+ const n = namesFor(raw);
1024
+ return `${HEADER(raw, "intent (directive)")}
1025
+ import { z, aggregate, directive, t, Conflict, set } from "@githolon/dsl";
1026
+
1027
+ // Stub target \u2014 in a real domain, import the existing aggregate handle instead.
1028
+ export const ${n.pascal} = aggregate("${n.pascal}", {
1029
+ label: t.string().merge(Conflict),
1030
+ });
1031
+
1032
+ /** ${n.camel} \u2014 .mutates the target aggregate. */
1033
+ export const ${n.camel} = directive("${n.camel}")
1034
+ .mutates(${n.pascal})
1035
+ .payload(z.object({ label: z.string() }))
1036
+ .plan((p) => [set(${n.pascal}, "label", p.label)]);
1037
+ `;
1038
+ }
1039
+
1040
+ // src/generate.ts
1041
+ var RENDERERS = {
1042
+ domain: domainTemplate,
1043
+ aggregate: aggregateTemplate,
1044
+ intent: intentTemplate
1045
+ };
1046
+ function renderScaffold(kind, name) {
1047
+ assertValidName(name);
1048
+ const render = RENDERERS[kind];
1049
+ if (!render) {
1050
+ throw new Error(`Unknown generator kind '${kind}'. Use: domain | aggregate | intent.`);
1051
+ }
1052
+ return render(name);
1053
+ }
1054
+ function generate(opts) {
1055
+ const contents = renderScaffold(opts.kind, opts.name);
1056
+ const fileName = `${snakeCase(opts.name)}.ts`;
1057
+ const path = join(opts.outDir, fileName);
1058
+ if (existsSync(path) && !opts.force) {
1059
+ throw new Error(
1060
+ `Refusing to overwrite existing file: ${path}
1061
+ Re-run with --force to replace it.`
1062
+ );
1063
+ }
1064
+ mkdirSync(opts.outDir, { recursive: true });
1065
+ writeFileSync(path, contents, "utf8");
1066
+ return { path, contents };
1067
+ }
1068
+
1069
+ // src/capability.ts
1070
+ init_cloud();
1071
+ import { spawnSync } from "node:child_process";
1072
+ var out2 = (s) => void process.stdout.write(s + "\n");
1073
+ var err2 = (s) => void process.stderr.write("error: " + s + "\n");
1074
+ var CAP_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
1075
+ function readStdin() {
1076
+ if (process.stdin.isTTY === true) {
1077
+ process.stderr.write(
1078
+ "paste the credential value, then Ctrl-D (it never rides argv \u2014 ps/history are hosts too):\n"
1079
+ );
1080
+ }
1081
+ return new Promise((resolveValue) => {
1082
+ let buf = "";
1083
+ process.stdin.setEncoding("utf8");
1084
+ process.stdin.on("data", (chunk) => {
1085
+ buf += chunk;
1086
+ });
1087
+ process.stdin.on("end", () => resolveValue(buf.trim()));
1088
+ process.stdin.resume();
1089
+ });
1090
+ }
1091
+ function readGcloud(name) {
1092
+ const r = spawnSync("gcloud", ["secrets", "versions", "access", "latest", `--secret=${name}`], {
1093
+ encoding: "utf8",
1094
+ maxBuffer: 1024 * 1024
1095
+ });
1096
+ if (r.error !== void 0 || r.status !== 0) {
1097
+ throw new Error(
1098
+ `gcloud secret read failed (${r.error?.message ?? r.stderr?.trim() ?? `exit ${r.status}`}) \u2014 is gcloud installed + authed, and does secret '${name}' exist?`
1099
+ );
1100
+ }
1101
+ const v = r.stdout.trim();
1102
+ if (v === "") throw new Error(`gcloud secret '${name}' is empty`);
1103
+ return v;
1104
+ }
1105
+ function parseExpiresAt(s) {
1106
+ const n = /^\d+$/.test(s) ? Number(s) : Date.parse(s);
1107
+ if (!Number.isInteger(n) || n <= 0) throw new Error(`--expires-at '${s}' is neither epoch-ms nor an ISO date`);
1108
+ return n;
1109
+ }
1110
+ async function bindLane(lane, ws, capability, opts) {
1111
+ const cloud = cloudBase(opts.cloud);
1112
+ if (!CAP_NAME.test(capability)) {
1113
+ err2(`capability must be a lowercase law name matching [a-z0-9][a-z0-9._-]{0,63} \u2014 e.g. cf-analytics, openai`);
1114
+ return 1;
1115
+ }
1116
+ const secret = getSecret(cloud, ws);
1117
+ if (secret === void 0) {
1118
+ err2(
1119
+ `no stored secret for ${ws} on ${cloud} \u2014 capability ${lane} is owner-gated
1120
+ githolon secret set ${ws} <secret> (import the workspaceSecret you hold)`
1121
+ );
1122
+ return 1;
1123
+ }
1124
+ let value;
1125
+ try {
1126
+ value = opts.fromGcloud !== void 0 ? readGcloud(opts.fromGcloud) : await readStdin();
1127
+ } catch (e) {
1128
+ err2(e.message);
1129
+ return 1;
1130
+ }
1131
+ if (value === "") {
1132
+ err2("no credential value arrived \u2014 pipe it on stdin, or pass --from-gcloud <secret-name>");
1133
+ return 1;
1134
+ }
1135
+ let expiresAt;
1136
+ try {
1137
+ expiresAt = opts.expiresAt !== void 0 ? parseExpiresAt(opts.expiresAt) : void 0;
1138
+ } catch (e) {
1139
+ err2(e.message);
1140
+ return 1;
1141
+ }
1142
+ const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/${lane}`, {
1143
+ method: "POST",
1144
+ headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
1145
+ body: JSON.stringify({
1146
+ capability,
1147
+ secret: value,
1148
+ ...opts.scope !== void 0 ? { scope: opts.scope } : {},
1149
+ ...expiresAt !== void 0 ? { expiresAt } : {}
1150
+ })
1151
+ });
1152
+ const d = await r.json().catch(() => void 0);
1153
+ if (r.status === 401) {
1154
+ err2(`${lane} refused (401) \u2014 the stored secret for ${ws} is not the workspace's. githolon secret set ${ws} <secret>`);
1155
+ return 1;
1156
+ }
1157
+ if (!r.ok || d?.ok !== true) {
1158
+ err2(`capability ${lane} '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
1159
+ return 1;
867
1160
  }
1161
+ out2(`\u2713 ${capability} ${lane === "rotate" ? "rotated" : "bound"} on ${ws} (epoch ${d.epoch})`);
1162
+ out2(` the VALUE lives host-side only (the workspace's holon) \u2014 it never enters the ledger`);
1163
+ out2(` the law fact: keyHash ${(d.keyHash ?? "").slice(0, 16)}\u2026 status ${d.status}`);
1164
+ return 0;
868
1165
  }
869
- function readTreeFromDisk(diskPath) {
870
- const contents = /* @__PURE__ */ new Map();
871
- for (const name of readdirSync2(diskPath)) {
872
- const p = join4(diskPath, name);
873
- if (statSync(p).isDirectory()) contents.set(name, readTreeFromDisk(p));
874
- else contents.set(name, new File2(readFileSync2(p)));
1166
+ async function capabilityBind(ws, capability, opts) {
1167
+ return bindLane("bind", ws, capability, opts);
1168
+ }
1169
+ async function capabilityRotate(ws, capability, opts) {
1170
+ return bindLane("rotate", ws, capability, opts);
1171
+ }
1172
+ async function capabilityList(ws, opts) {
1173
+ const cloud = cloudBase(opts.cloud);
1174
+ const secret = getSecret(cloud, ws);
1175
+ if (secret === void 0) {
1176
+ err2(`no stored secret for ${ws} on ${cloud} \u2014 the binding facts are owner-gated
1177
+ githolon secret set ${ws} <secret>`);
1178
+ return 1;
875
1179
  }
876
- return new Directory2(contents);
1180
+ const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities`, {
1181
+ headers: { authorization: `Bearer ${secret}` }
1182
+ });
1183
+ const d = await r.json().catch(() => void 0);
1184
+ if (!r.ok || d?.ok !== true) {
1185
+ err2(`capability list '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
1186
+ return 1;
1187
+ }
1188
+ const bindings = d.bindings ?? [];
1189
+ if (bindings.length === 0) {
1190
+ out2(`no capability bindings on ${ws} \u2014 bind one: githolon capability bind ${ws} <capability>`);
1191
+ return 0;
1192
+ }
1193
+ for (const b of bindings) {
1194
+ const bits = [
1195
+ `epoch ${b.epoch}`,
1196
+ `${b.status}`,
1197
+ `keyHash ${(b.keyHash ?? "").slice(0, 16)}\u2026`,
1198
+ ...b.scope !== void 0 ? [`scope ${b.scope}`] : [],
1199
+ ...b.expiresAt !== void 0 ? [`expires ${new Date(b.expiresAt).toISOString()}`] : [],
1200
+ ...b.held !== void 0 ? [b.held ? "value held" : "VALUE MISSING (rotate to restore)"] : []
1201
+ ];
1202
+ out2(`${b.capability} ${bits.join(" \xB7 ")}`);
1203
+ }
1204
+ out2(`(facts about authority \u2014 the values live host-side and are never listed)`);
1205
+ return 0;
877
1206
  }
878
- async function holonEngine(cloud, overlay, installedBy) {
879
- const runtime = await fetchRuntime(cloud);
880
- const manifestStrings = overlay === void 0 ? { read: runtime.manifests.domainManifests, identity: runtime.manifests.identityManifests } : mergeManifests(runtime.manifests, overlay);
881
- const replica = BigInt(`0x${randomBytes(8).toString("hex")}`) & (1n << 63n) - 1n;
882
- const eng = await createEngine({
883
- wasmModule: await WebAssembly.compile(runtime.wasmBytes),
884
- bootstrapPkg: runtime.packages.bootstrap,
885
- nomosPkg: runtime.packages.nomos,
886
- manifestStrings,
887
- replica,
888
- installedBy
1207
+ async function capabilityUnbind(ws, capability, opts) {
1208
+ const cloud = cloudBase(opts.cloud);
1209
+ const secret = getSecret(cloud, ws);
1210
+ if (secret === void 0) {
1211
+ err2(`no stored secret for ${ws} on ${cloud} \u2014 unbind is owner-gated
1212
+ githolon secret set ${ws} <secret>`);
1213
+ return 1;
1214
+ }
1215
+ const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/unbind`, {
1216
+ method: "POST",
1217
+ headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
1218
+ body: JSON.stringify({ capability })
889
1219
  });
890
- return { eng, runtime };
1220
+ const d = await r.json().catch(() => void 0);
1221
+ if (!r.ok || d?.ok !== true) {
1222
+ err2(`capability unbind '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
1223
+ return 1;
1224
+ }
1225
+ out2(`\u2713 ${capability} unbound on ${ws} \u2014 the law fact is revoked (the record stays) and the host forgot the value`);
1226
+ return 0;
891
1227
  }
892
- async function mountFresh(eng, ws) {
893
- await mountWorkspace(eng, ws, { remote: "https://unborn.invalid/", headers: {} });
1228
+
1229
+ // src/cli.ts
1230
+ init_cloud();
1231
+
1232
+ // src/compile.ts
1233
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
1234
+ import { createRequire } from "node:module";
1235
+ import { dirname, join as join3 } from "node:path";
1236
+ import { pathToFileURL } from "node:url";
1237
+ function resolveCompileLauncher(cwd) {
1238
+ const resolveDslPkg = (fromDir) => {
1239
+ try {
1240
+ const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join3(fromDir, "noop.js")));
1241
+ return req.resolve("@githolon/dsl/package.json");
1242
+ } catch {
1243
+ return void 0;
1244
+ }
1245
+ };
1246
+ const dslPkg = resolveDslPkg(cwd) ?? resolveDslPkg(void 0);
1247
+ return dslPkg === void 0 ? void 0 : join3(dirname(dslPkg), "compile_package.mjs");
894
1248
  }
895
- function sealedIntentOf(eng, ws, res) {
896
- if (typeof res.intentOut !== "string") return void 0;
897
- const f = eng.preopen.dir.contents.get("ws").contents.get(ws)?.contents.get(res.intentOut);
898
- if (f === void 0 || !(f.data instanceof Uint8Array)) return void 0;
899
- try {
900
- return JSON.parse(new TextDecoder().decode(f.data));
901
- } catch {
902
- return void 0;
1249
+ var NO_DSL_REMEDY = "@githolon/dsl not found \u2014 add it to your project's dependencies";
1250
+ function runCompile(args) {
1251
+ const launcher = resolveCompileLauncher(process.cwd());
1252
+ if (launcher === void 0) {
1253
+ process.stderr.write(`error: ${NO_DSL_REMEDY}
1254
+ `);
1255
+ return 1;
903
1256
  }
1257
+ const r = spawnSync2(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
1258
+ return r.status ?? 1;
904
1259
  }
905
- function printVerdict(v) {
906
- if (v["valid"] === true) {
907
- out2(`\u2713 chain VALID \u2014 head ${String(v["head"]).slice(0, 12)}\u2026, ${v["intents"]} intent(s), ${v["plansRerun"]} plan(s) re-run`);
908
- out2(` controller ${String(v["controllerHash"]).slice(0, 12)}\u2026 | installed: ${v["installedDomains"]?.map((h) => h.slice(0, 12) + "\u2026").join(", ") ?? "\u2014"}`);
909
- return true;
1260
+ function compileAsync(cwd, args = []) {
1261
+ const launcher = resolveCompileLauncher(cwd);
1262
+ const t0 = performance.now();
1263
+ if (launcher === void 0) {
1264
+ return Promise.resolve({ ok: false, ms: 0, output: NO_DSL_REMEDY });
910
1265
  }
911
- err2(`chain INVALID at ${v["check"]}${v["atIndex"] !== void 0 ? ` (intent ${v["atIndex"]})` : ""}: ${v["error"]}`);
912
- return false;
1266
+ return new Promise((resolveRun) => {
1267
+ const chunks = [];
1268
+ const dec3 = new TextDecoder();
1269
+ const child = spawn(process.execPath, [launcher, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
1270
+ child.stdout?.on("data", (c) => chunks.push(dec3.decode(c)));
1271
+ child.stderr?.on("data", (c) => chunks.push(dec3.decode(c)));
1272
+ child.on("error", (e) => resolveRun({ ok: false, ms: performance.now() - t0, output: String(e) }));
1273
+ child.on(
1274
+ "close",
1275
+ (code) => resolveRun({ ok: code === 0, ms: performance.now() - t0, output: chunks.join("") })
1276
+ );
1277
+ });
913
1278
  }
914
1279
 
1280
+ // src/dev.ts
1281
+ init_engine();
1282
+ init_cloud();
1283
+ import { existsSync as existsSync6, readFileSync as readFileSync4, watch } from "node:fs";
1284
+ import { createServer } from "node:http";
1285
+ import { dirname as dirname4, join as join7, resolve } from "node:path";
1286
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
1287
+ init_local_holon();
1288
+
915
1289
  // src/project.ts
916
1290
  import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
917
1291
  import { join as join5 } from "node:path";
@@ -997,89 +1371,10 @@ async function resolveWorkspace(explicit, cwd, target, usageHint) {
997
1371
  return resolveWorkspaceDecl(project.workspace, target, project.configPath);
998
1372
  }
999
1373
 
1000
- // src/proof_offline.ts
1001
- function parseOfflineLegs(proofSource) {
1002
- if (!proofSource.includes("AUTO-GENERATED by nomos-compile")) {
1003
- throw new Error("not a generated proof (missing the AUTO-GENERATED header) \u2014 run `githolon compile` to (re)generate build/<name>.proof.mts");
1004
- }
1005
- const dispatch = proofSource.match(/^await app\.(\w+)\((\{.*\})\);$/m);
1006
- if (dispatch === null) {
1007
- throw new Error("generated proof has no `await app.<directive>({...})` dispatch \u2014 regenerate with `githolon compile` (a creating directive is required)");
1008
- }
1009
- const directiveId = dispatch[1];
1010
- const payload = JSON.parse(dispatch[2]);
1011
- const dom = proofSource.match(/dispatch (\w+)\/(\w+) — offline write/);
1012
- if (dom === null || dom[2] !== directiveId) {
1013
- throw new Error("generated proof names no `dispatch <domain>/<directive> \u2014 offline write` \u2014 regenerate with `githolon compile`");
1014
- }
1015
- const domain = dom[1];
1016
- const autoStamped = [];
1017
- const stampLine = proofSource.split("\n").find((l) => l.includes("omitted \u2014 the client auto-stamps ISO now()"));
1018
- if (stampLine !== void 0) {
1019
- for (const m of stampLine.matchAll(/`(\w+)`/g)) autoStamped.push(m[1]);
1020
- }
1021
- let queryLeg;
1022
- const q = proofSource.match(/local declared query (\w+):/);
1023
- if (q !== null) {
1024
- const call2 = proofSource.match(/^const rows = await app\.\w+\((\{.*\})\);$/m);
1025
- if (call2 === null) throw new Error(`generated proof names query '${q[1]}' but its accessor call is missing \u2014 regenerate with \`githolon compile\``);
1026
- queryLeg = { id: q[1], params: JSON.parse(call2[1]) };
1027
- }
1028
- let countLeg;
1029
- const c = proofSource.match(/local declared count (\w+):/);
1030
- if (c !== null) {
1031
- const call2 = proofSource.match(/^const localCount = await app\.\w+\((.*?)\);$/m);
1032
- if (call2 === null) throw new Error(`generated proof names count '${c[1]}' but its accessor call is missing \u2014 regenerate with \`githolon compile\``);
1033
- const arg = call2[1].trim();
1034
- countLeg = { id: c[1], group: arg.length === 0 ? "" : JSON.parse(arg) };
1035
- }
1036
- return {
1037
- domain,
1038
- directiveId,
1039
- payload,
1040
- autoStamped,
1041
- ...queryLeg !== void 0 ? { query: queryLeg } : {},
1042
- ...countLeg !== void 0 ? { count: countLeg } : {}
1043
- };
1044
- }
1045
- function runOfflineLegs(eng, ws, lawHash, legs) {
1046
- const t0 = performance.now();
1047
- const done = (ok, lines2, error) => ({
1048
- ok,
1049
- ms: performance.now() - t0,
1050
- legs: lines2,
1051
- ...error !== void 0 ? { error } : {}
1052
- });
1053
- const lines = [];
1054
- const payload = { ...legs.payload };
1055
- const stamp = (/* @__PURE__ */ new Date()).toISOString();
1056
- for (const f of legs.autoStamped) payload[f] = stamp;
1057
- const res = author(eng, ws, legs.domain, legs.directiveId, payload, lawHash);
1058
- if (res.ok === false) {
1059
- return done(false, lines, `dispatch ${legs.domain}/${legs.directiveId} refused: ${res.error ?? "unknown"} \u2014 the law itself rejected its own synthesized sample; check the directive's plan/payload schema`);
1060
- }
1061
- const createdId = (sealedIntentOf(eng, ws, res)?.events ?? []).find((e) => e.marker === "Create")?.aggregate;
1062
- lines.push(`\u2713 dispatch ${legs.domain}/${legs.directiveId} \u2014 offline write under the new law${createdId !== void 0 ? ` (${createdId.slice(0, 28)}\u2026)` : ""}`);
1063
- if (legs.query !== void 0) {
1064
- const rows = query(eng, ws, legs.query.id, JSON.stringify(legs.query.params));
1065
- if (rows.length !== 1 || createdId !== void 0 && rows[0].id !== createdId) {
1066
- return done(false, lines, `declared query ${legs.query.id} expected the 1 created row, got ${JSON.stringify(rows.map((r) => r.id))} \u2014 check the query's key fields against the directive's payload`);
1067
- }
1068
- lines.push(`\u2713 declared query ${legs.query.id} answers locally \u2014 1 row`);
1069
- }
1070
- if (legs.count !== void 0) {
1071
- const c = count(eng, ws, legs.count.id, legs.count.group).count;
1072
- if (c !== 1) {
1073
- return done(false, lines, `declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) expected 1, got ${c} \u2014 check the count's grouping against the created row`);
1074
- }
1075
- lines.push(`\u2713 declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) = 1 locally`);
1076
- }
1077
- return done(true, lines);
1078
- }
1079
-
1080
1374
  // src/dev.ts
1081
- var out3 = (s) => void process.stdout.write(s + "\n");
1082
- var err3 = (s) => void process.stderr.write("error: " + s + "\n");
1375
+ init_proof_offline();
1376
+ var out4 = (s) => void process.stdout.write(s + "\n");
1377
+ var err4 = (s) => void process.stderr.write("error: " + s + "\n");
1083
1378
  var WS = "dev";
1084
1379
  var DEFAULT_PORT = 7700;
1085
1380
  var DEBOUNCE_MS = 200;
@@ -1163,11 +1458,11 @@ function startServer(s) {
1163
1458
  });
1164
1459
  }
1165
1460
  function readDeployBody(cwd, packageName) {
1166
- const file = join6(cwd, "build", `${packageName}.deploy.json`);
1167
- if (!existsSync5(file)) {
1461
+ const file = join7(cwd, "build", `${packageName}.deploy.json`);
1462
+ if (!existsSync6(file)) {
1168
1463
  throw new Error(`compile produced no ${file} \u2014 check the compile output above`);
1169
1464
  }
1170
- return JSON.parse(readFileSync3(file, "utf8"));
1465
+ return JSON.parse(readFileSync4(file, "utf8"));
1171
1466
  }
1172
1467
  async function installLaw(s, deployBody, installedBy) {
1173
1468
  const newHash = await sha256hex(deployBody.packageUsda);
@@ -1186,11 +1481,11 @@ async function installLaw(s, deployBody, installedBy) {
1186
1481
  return { ok: true };
1187
1482
  }
1188
1483
  async function runProofCycle(s, cwd, deployBody, installedBy) {
1189
- const proofPath = join6(cwd, "build", `${s.packageName}.proof.mts`);
1190
- if (!existsSync5(proofPath)) return void 0;
1484
+ const proofPath = join7(cwd, "build", `${s.packageName}.proof.mts`);
1485
+ if (!existsSync6(proofPath)) return void 0;
1191
1486
  const scratch = `proof-${s.cycle}`;
1192
1487
  try {
1193
- const legs = parseOfflineLegs(readFileSync3(proofPath, "utf8"));
1488
+ const legs = parseOfflineLegs(readFileSync4(proofPath, "utf8"));
1194
1489
  await mountFresh(s.eng, scratch);
1195
1490
  author(s.eng, scratch, "bootstrap", "installDomain", installPayload(s.eng.hashes.nomos, s.eng.nomosPkg, installedBy), "");
1196
1491
  author(s.eng, scratch, "nomos", "installDomain", installPayload(s.lawHash, deployBody.packageUsda, installedBy), s.eng.hashes.nomos);
@@ -1205,21 +1500,21 @@ async function dev(opts) {
1205
1500
  const cwd = process.cwd();
1206
1501
  const cloud = cloudBase(opts.cloud);
1207
1502
  const project = await loadProject(cwd).catch((e) => {
1208
- err3(e.message);
1503
+ err4(e.message);
1209
1504
  return void 0;
1210
1505
  });
1211
1506
  if (project === void 0) {
1212
- if (!existsSync5(join6(cwd, CONFIG_FILE))) {
1213
- err3(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
1507
+ if (!existsSync6(join7(cwd, CONFIG_FILE))) {
1508
+ err4(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
1214
1509
  }
1215
1510
  return 1;
1216
1511
  }
1217
1512
  const creds = loadCreds();
1218
1513
  const installedBy = creds.session?.uid ?? creds.principal ?? "githolon-dev";
1219
- out3(`githolon dev \u2014 ${project.name}: compiling\u2026`);
1514
+ out4(`githolon dev \u2014 ${project.name}: compiling\u2026`);
1220
1515
  const firstCompile = await compileAsync(cwd);
1221
1516
  if (!firstCompile.ok) {
1222
- err3(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
1517
+ err4(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
1223
1518
  ${firstCompile.output}`);
1224
1519
  return 1;
1225
1520
  }
@@ -1227,14 +1522,14 @@ ${firstCompile.output}`);
1227
1522
  try {
1228
1523
  deployBody = readDeployBody(cwd, project.name);
1229
1524
  } catch (e) {
1230
- err3(e.message);
1525
+ err4(e.message);
1231
1526
  return 1;
1232
1527
  }
1233
1528
  let engBoot;
1234
1529
  try {
1235
1530
  engBoot = await holonEngine(cloud, deployBody, installedBy);
1236
1531
  } catch (e) {
1237
- err3(`cannot boot the local holon: ${String(e.message ?? e)}
1532
+ err4(`cannot boot the local holon: ${String(e.message ?? e)}
1238
1533
  the runtime rides ${cloud}/v1/runtime/* and is cached in ~/.holon/runtime after one fetch \u2014 connect once, then dev runs offline`);
1239
1534
  return 1;
1240
1535
  }
@@ -1257,31 +1552,31 @@ ${firstCompile.output}`);
1257
1552
  s.intents++;
1258
1553
  const installed = await installLaw(s, deployBody, installedBy);
1259
1554
  if (!installed.ok) {
1260
- err3(installed.error ?? "law install failed");
1555
+ err4(installed.error ?? "law install failed");
1261
1556
  return 1;
1262
1557
  }
1263
1558
  s.proof = await runProofCycle(s, cwd, deployBody, installedBy);
1264
1559
  if (opts.once === true) {
1265
- out3(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
1560
+ out4(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
1266
1561
  return s.proof === void 0 || s.proof.ok ? 0 : 1;
1267
1562
  }
1268
1563
  let server;
1269
1564
  try {
1270
1565
  server = await startServer(s);
1271
1566
  } catch (e) {
1272
- err3(e.message);
1567
+ err4(e.message);
1273
1568
  return 1;
1274
1569
  }
1275
1570
  const moduleDirs = /* @__PURE__ */ new Set();
1276
1571
  try {
1277
1572
  const cfgModule = await import(pathToFileURL3(project.configPath).href);
1278
1573
  for (const d of cfgModule.default?.domains ?? []) {
1279
- for (const m of d.modules ?? []) moduleDirs.add(dirname3(resolve(cwd, m)));
1574
+ for (const m of d.modules ?? []) moduleDirs.add(dirname4(resolve(cwd, m)));
1280
1575
  }
1281
1576
  } catch {
1282
- moduleDirs.add(join6(cwd, "domains"));
1577
+ moduleDirs.add(join7(cwd, "domains"));
1283
1578
  }
1284
- if (moduleDirs.size === 0) moduleDirs.add(join6(cwd, "domains"));
1579
+ if (moduleDirs.size === 0) moduleDirs.add(join7(cwd, "domains"));
1285
1580
  let timer;
1286
1581
  let cycling = false;
1287
1582
  let queued = false;
@@ -1297,7 +1592,7 @@ ${firstCompile.output}`);
1297
1592
  s.compileMs = run.ms;
1298
1593
  if (!run.ok) {
1299
1594
  s.lastError = run.output.trim() || "compile failed";
1300
- out3(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
1595
+ out4(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
1301
1596
  ${run.output}`);
1302
1597
  } else {
1303
1598
  s.lastError = void 0;
@@ -1306,16 +1601,16 @@ ${run.output}`);
1306
1601
  const inst = await installLaw(s, body, installedBy);
1307
1602
  if (!inst.ok) {
1308
1603
  s.lastError = inst.error ?? "install failed";
1309
- out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1604
+ out4(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1310
1605
  } else {
1311
1606
  s.proof = await runProofCycle(s, cwd, body, installedBy);
1312
1607
  const total = performance.now() - t0;
1313
- out3(statusScreen(s));
1314
- out3(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
1608
+ out4(statusScreen(s));
1609
+ out4(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
1315
1610
  }
1316
1611
  } catch (e) {
1317
1612
  s.lastError = String(e.message ?? e);
1318
- out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1613
+ out4(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1319
1614
  }
1320
1615
  }
1321
1616
  cycling = false;
@@ -1331,7 +1626,7 @@ ${run.output}`);
1331
1626
  };
1332
1627
  const watchers = [];
1333
1628
  for (const d of moduleDirs) {
1334
- if (!existsSync5(d)) continue;
1629
+ if (!existsSync6(d)) continue;
1335
1630
  watchers.push(watch(d, { recursive: true }, (_evt, f) => onChange(f)));
1336
1631
  s.watchRoots.push(d.startsWith(cwd) ? d.slice(cwd.length + 1) + "/" : d);
1337
1632
  }
@@ -1341,8 +1636,8 @@ ${run.output}`);
1341
1636
  })
1342
1637
  );
1343
1638
  s.watchRoots.push(CONFIG_FILE);
1344
- out3(statusScreen(s));
1345
- out3(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
1639
+ out4(statusScreen(s));
1640
+ out4(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
1346
1641
  return new Promise((resolveExit) => {
1347
1642
  const stop = () => {
1348
1643
  for (const w of watchers) w.close();
@@ -1355,13 +1650,14 @@ ${run.output}`);
1355
1650
  }
1356
1651
 
1357
1652
  // src/git.ts
1358
- import { spawnSync as spawnSync2 } from "node:child_process";
1653
+ init_cloud();
1654
+ import { spawnSync as spawnSync3 } from "node:child_process";
1359
1655
  import { randomBytes as randomBytes2 } from "node:crypto";
1360
- import { readFileSync as readFileSync4 } from "node:fs";
1361
- var out4 = (s) => void process.stdout.write(s + "\n");
1362
- var err4 = (s) => void process.stderr.write("error: " + s + "\n");
1656
+ import { readFileSync as readFileSync5 } from "node:fs";
1657
+ var out5 = (s) => void process.stdout.write(s + "\n");
1658
+ var err5 = (s) => void process.stderr.write("error: " + s + "\n");
1363
1659
  function git2(args, opts = {}) {
1364
- const r = spawnSync2("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
1660
+ const r = spawnSync3("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
1365
1661
  return r.status ?? 1;
1366
1662
  }
1367
1663
  function ensureSecret(cloud, ws) {
@@ -1386,36 +1682,36 @@ function workspaceFromPath(path) {
1386
1682
  }
1387
1683
  async function gitCredential(action) {
1388
1684
  if (action !== "get") return 0;
1389
- const kv = parseCredentialInput(readFileSync4(0, "utf8"));
1685
+ const kv = parseCredentialInput(readFileSync5(0, "utf8"));
1390
1686
  const ws = workspaceFromPath(kv["path"]);
1391
1687
  if (kv["protocol"] !== "https" || ws === void 0) return 0;
1392
1688
  const cloud = `https://${kv["host"]}`;
1393
1689
  const token = await sessionToken().catch(() => void 0);
1394
- out4(`username=${token ?? "nomos"}`);
1395
- out4(`password=${ensureSecret(cloud, ws)}`);
1396
- out4("");
1690
+ out5(`username=${token ?? "nomos"}`);
1691
+ out5(`password=${ensureSecret(cloud, ws)}`);
1692
+ out5("");
1397
1693
  return 0;
1398
1694
  }
1399
1695
  async function gitSetup(opts) {
1400
1696
  const cloud = cloudBase(opts.cloud);
1401
1697
  const self = process.argv[1];
1402
1698
  if (self === void 0) {
1403
- err4("cannot locate the githolon CLI entry to install as a credential helper");
1699
+ err5("cannot locate the githolon CLI entry to install as a credential helper");
1404
1700
  return 1;
1405
1701
  }
1406
1702
  const helper = `!"${process.execPath}" "${self}" git-credential`;
1407
1703
  if (git2(["config", "--global", `credential.${cloud}.helper`, helper]) !== 0 || git2(["config", "--global", `credential.${cloud}.useHttpPath`, "true"]) !== 0) {
1408
- err4("git config failed \u2014 is git installed?");
1704
+ err5("git config failed \u2014 is git installed?");
1409
1705
  return 1;
1410
1706
  }
1411
- out4(`\u2713 git credential helper installed for ${cloud} (global gitconfig)`);
1412
- out4(` pushes/clones of ${cloud}/v1/workspaces/<ws>/git now authenticate from ~/.holon`);
1707
+ out5(`\u2713 git credential helper installed for ${cloud} (global gitconfig)`);
1708
+ out5(` pushes/clones of ${cloud}/v1/workspaces/<ws>/git now authenticate from ~/.holon`);
1413
1709
  return 0;
1414
1710
  }
1415
1711
  async function gitRemote(ws, name, opts) {
1416
1712
  const cloud = cloudBase(opts.cloud);
1417
1713
  if (git2(["rev-parse", "--git-dir"], { quiet: true }) !== 0) {
1418
- err4("not a git repository \u2014 run inside your repo (git init first)");
1714
+ err5("not a git repository \u2014 run inside your repo (git init first)");
1419
1715
  return 1;
1420
1716
  }
1421
1717
  const setupRc = await gitSetup(opts);
@@ -1424,7 +1720,7 @@ async function gitRemote(ws, name, opts) {
1424
1720
  const url = `${cloud}/v1/workspaces/${ws}/git`;
1425
1721
  if (git2(["remote", "add", name, url], { quiet: true }) !== 0) {
1426
1722
  if (git2(["remote", "set-url", name, url], { quiet: true }) !== 0) {
1427
- err4(`could not add or update remote '${name}'`);
1723
+ err5(`could not add or update remote '${name}'`);
1428
1724
  return 1;
1429
1725
  }
1430
1726
  }
@@ -1432,7 +1728,7 @@ async function gitRemote(ws, name, opts) {
1432
1728
  if (token === void 0) {
1433
1729
  const principal = loadCreds().principal;
1434
1730
  if (principal === void 0) {
1435
- err4(
1731
+ err5(
1436
1732
  `remote '${name}' \u2192 ${url} is wired, but a BIRTH push needs a principal:
1437
1733
  githolon login --agent (then just push)
1438
1734
  \u2014 or re-run after githolon ws create/login so a principal is on file`
@@ -1440,13 +1736,13 @@ async function gitRemote(ws, name, opts) {
1440
1736
  return 1;
1441
1737
  }
1442
1738
  if (git2(["config", `http.${url}.extraHeader`, `x-nomos-principal: ${principal}`]) !== 0) {
1443
- err4("git config failed setting the principal header");
1739
+ err5("git config failed setting the principal header");
1444
1740
  return 1;
1445
1741
  }
1446
1742
  }
1447
- out4(`\u2713 remote '${name}' \u2192 ${url}`);
1448
- out4(` workspace secret on file; ${token !== void 0 ? "births ride your session (verified)" : "births ride x-nomos-principal (bare uid)"}`);
1449
- out4(` birth/deploy by push: git push ${name} main`);
1743
+ out5(`\u2713 remote '${name}' \u2192 ${url}`);
1744
+ out5(` workspace secret on file; ${token !== void 0 ? "births ride your session (verified)" : "births ride x-nomos-principal (bare uid)"}`);
1745
+ out5(` birth/deploy by push: git push ${name} main`);
1450
1746
  return 0;
1451
1747
  }
1452
1748
 
@@ -1462,9 +1758,9 @@ var HELP = {
1462
1758
  examples: ["githolon compile", "githolon compile nomos.package.mjs --layered"]
1463
1759
  },
1464
1760
  proof: {
1465
- usage: "githolon proof [build/<name>.proof.mts]",
1466
- what: "Run the proof GENERATED from your law, live against the cloud (throwaway workspace \u2192\ndeploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence). ALL GREEN or it names the jam.\nThe offline half of the same proof reruns automatically inside `githolon dev`.",
1467
- examples: ["githolon proof", "githolon proof build/guestbook.proof.mts"]
1761
+ usage: "githolon proof [build/<name>.proof.mts] [--domain <key>] [--live] [--keep]",
1762
+ what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence), RETIRED on every\nexit; --keep keeps it and prints the secret once. ALL GREEN or it names the jam. The offline\nlegs also rerun on every save in `githolon dev`.",
1763
+ examples: ["githolon proof", "githolon proof --domain co2_platform", "githolon proof --live", "githolon proof --live --keep"]
1468
1764
  },
1469
1765
  dev: {
1470
1766
  usage: "githolon dev [--port <n>] [--cloud <url>] [--once]",
@@ -1550,6 +1846,22 @@ var HELP = {
1550
1846
  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).",
1551
1847
  examples: ["githolon secret set my-ws nws_v1_\u2026", "githolon secret rotate my-ws"]
1552
1848
  },
1849
+ capability: {
1850
+ usage: "githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws> [--from-gcloud <name>] [--scope <s>] [--expires-at <iso|ms>] [--cloud <url>]",
1851
+ 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.",
1852
+ flags: [
1853
+ ["--from-gcloud <name>", "read the value from `gcloud secrets versions access latest --secret=<name>`"],
1854
+ ["--scope <s>", "a narrowing label recorded on the law fact (e.g. read-only)"],
1855
+ ["--expires-at <t>", "expiry recorded on the law fact (ISO date/datetime or epoch-ms; fail-closed at the host)"],
1856
+ ["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
1857
+ ],
1858
+ examples: [
1859
+ "cat token.txt | githolon capability bind nomos-usage cf-analytics",
1860
+ "githolon capability bind my-ws openai --from-gcloud openai-api-key --scope read-only",
1861
+ "githolon capability list nomos-usage",
1862
+ "githolon capability rotate nomos-usage cf-analytics --from-gcloud cf-analytics-token"
1863
+ ]
1864
+ },
1553
1865
  git: {
1554
1866
  usage: "githolon git <setup | remote [<ws>] [<remoteName>]> [--cloud <url>] [--target <name>]",
1555
1867
  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.",
@@ -1578,34 +1890,37 @@ function commandHelp(cmd) {
1578
1890
  }
1579
1891
 
1580
1892
  // src/ledger.ts
1581
- import { spawnSync as spawnSync3 } from "node:child_process";
1582
- import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
1583
- import { join as join7 } from "node:path";
1584
- var out5 = (s) => void process.stdout.write(s + "\n");
1585
- var err5 = (s) => void process.stderr.write("error: " + s + "\n");
1893
+ init_engine();
1894
+ init_cloud();
1895
+ init_local_holon();
1896
+ import { spawnSync as spawnSync4 } from "node:child_process";
1897
+ import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
1898
+ import { join as join8 } from "node:path";
1899
+ var out6 = (s) => void process.stdout.write(s + "\n");
1900
+ var err6 = (s) => void process.stderr.write("error: " + s + "\n");
1586
1901
  var WS2 = "local";
1587
1902
  async function ledgerInit(dir, opts) {
1588
1903
  const cloud = cloudBase(opts.cloud);
1589
- if (existsSync6(dir) && readdirSync3(dir).length > 0) {
1590
- err5(`refusing to mint into non-empty directory: ${dir}`);
1904
+ if (existsSync7(dir) && readdirSync3(dir).length > 0) {
1905
+ err6(`refusing to mint into non-empty directory: ${dir}`);
1591
1906
  return 1;
1592
1907
  }
1593
1908
  let deployFile;
1594
1909
  try {
1595
1910
  deployFile = discoverDeployJson(process.cwd(), opts.file);
1596
1911
  } catch (e) {
1597
- err5(e.message);
1912
+ err6(e.message);
1598
1913
  return 1;
1599
1914
  }
1600
- const deployBody = JSON.parse(readFileSync5(deployFile, "utf8"));
1915
+ const deployBody = JSON.parse(readFileSync6(deployFile, "utf8"));
1601
1916
  const domainHash = await sha256hex(deployBody.packageUsda);
1602
1917
  const c = loadCreds();
1603
1918
  const principal = await sessionToken().catch(() => void 0) !== void 0 ? c.session.uid : c.principal;
1604
1919
  if (principal === void 0) {
1605
- err5("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
1920
+ err6("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
1606
1921
  return 1;
1607
1922
  }
1608
- out5(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
1923
+ out6(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
1609
1924
  const { eng } = await holonEngine(cloud, deployBody, principal);
1610
1925
  await mountFresh(eng, WS2);
1611
1926
  author(eng, WS2, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, principal), "");
@@ -1613,21 +1928,21 @@ async function ledgerInit(dir, opts) {
1613
1928
  const verdict = verifyChainLocal(eng, WS2);
1614
1929
  if (!printVerdict(verdict)) return 1;
1615
1930
  const gitTree = eng.preopen.dir.contents.get("ws").contents.get(WS2).contents.get("nomos.git");
1616
- const gitDir = join7(dir, ".git");
1931
+ const gitDir = join8(dir, ".git");
1617
1932
  writeTreeToDisk(gitTree, gitDir);
1618
- const cfgPath = join7(gitDir, "config");
1619
- writeFileSync4(cfgPath, readFileSync5(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
1620
- spawnSync3("git", ["-C", dir, "reset", "--hard", "main"], { stdio: "ignore" });
1621
- out5(`\u2713 holon written \u2192 ${dir} (a normal git repo; git log IS the audit trail)`);
1622
- out5(`
1933
+ const cfgPath = join8(gitDir, "config");
1934
+ writeFileSync4(cfgPath, readFileSync6(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
1935
+ spawnSync4("git", ["-C", dir, "reset", "--hard", "main"], { stdio: "ignore" });
1936
+ out6(`\u2713 holon written \u2192 ${dir} (a normal git repo; git log IS the audit trail)`);
1937
+ out6(`
1623
1938
  Birth it on Nomos Cloud (the cloud re-derives this exact verdict):`);
1624
- out5(` cd ${dir} && npx githolon git remote <ws> && git push nomos main`);
1939
+ out6(` cd ${dir} && npx githolon git remote <ws> && git push nomos main`);
1625
1940
  return 0;
1626
1941
  }
1627
1942
  async function ledgerVerify(dir, opts) {
1628
- const gitDir = existsSync6(join7(dir, ".git")) ? join7(dir, ".git") : dir;
1629
- if (!existsSync6(join7(gitDir, "HEAD"))) {
1630
- err5(`${dir} is not a git repo (no HEAD)`);
1943
+ const gitDir = existsSync7(join8(dir, ".git")) ? join8(dir, ".git") : dir;
1944
+ if (!existsSync7(join8(gitDir, "HEAD"))) {
1945
+ err6(`${dir} is not a git repo (no HEAD)`);
1631
1946
  return 1;
1632
1947
  }
1633
1948
  const { eng } = await holonEngine(cloudBase(opts.cloud), void 0, "verify");
@@ -1638,11 +1953,15 @@ async function ledgerVerify(dir, opts) {
1638
1953
  }
1639
1954
 
1640
1955
  // src/replay.ts
1641
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
1642
- import { join as join8 } from "node:path";
1956
+ init_engine();
1957
+ init_engine();
1958
+ init_cloud();
1959
+ init_local_holon();
1960
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
1961
+ import { join as join9 } from "node:path";
1643
1962
  import git3 from "isomorphic-git";
1644
- var out6 = (s) => void process.stdout.write(s + "\n");
1645
- var err6 = (s) => void process.stderr.write("error: " + s + "\n");
1963
+ var out7 = (s) => void process.stdout.write(s + "\n");
1964
+ var err7 = (s) => void process.stderr.write("error: " + s + "\n");
1646
1965
  var SOURCE = "source";
1647
1966
  var REPLAY = "replay";
1648
1967
  function summarizePayload(payload, max = 100) {
@@ -1678,9 +1997,9 @@ function capJsonStrings(v, max = 2048) {
1678
1997
  if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
1679
1998
  if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
1680
1999
  if (typeof v === "object" && v !== null) {
1681
- const out8 = {};
1682
- for (const [k, x] of Object.entries(v)) out8[k] = capJsonStrings(x, max);
1683
- return out8;
2000
+ const out9 = {};
2001
+ for (const [k, x] of Object.entries(v)) out9[k] = capJsonStrings(x, max);
2002
+ return out9;
1684
2003
  }
1685
2004
  return v;
1686
2005
  }
@@ -1727,10 +2046,10 @@ function readKey() {
1727
2046
  async function replay(target, opts) {
1728
2047
  const cloud = cloudBase(opts.cloud);
1729
2048
  const json = opts.json === true;
1730
- const emit = (o) => out6(JSON.stringify(o));
2049
+ const emit = (o) => out7(JSON.stringify(o));
1731
2050
  let overlay;
1732
2051
  try {
1733
- overlay = JSON.parse(readFileSync6(discoverDeployJson(process.cwd(), opts.file), "utf8"));
2052
+ overlay = JSON.parse(readFileSync7(discoverDeployJson(process.cwd(), opts.file), "utf8"));
1734
2053
  } catch {
1735
2054
  overlay = void 0;
1736
2055
  }
@@ -1738,15 +2057,15 @@ async function replay(target, opts) {
1738
2057
  try {
1739
2058
  ({ eng } = await holonEngine(cloud, overlay, "githolon-replay"));
1740
2059
  } catch (e) {
1741
- err6(`cannot boot the local holon: ${String(e.message ?? e)}
2060
+ err7(`cannot boot the local holon: ${String(e.message ?? e)}
1742
2061
  the runtime rides ${cloud}/v1/runtime/* (cached in ~/.holon/runtime after one fetch)`);
1743
2062
  return 1;
1744
2063
  }
1745
- const isDir = existsSync7(target) || target.includes("/") || target.startsWith(".");
2064
+ const isDir = existsSync8(target) || target.includes("/") || target.startsWith(".");
1746
2065
  if (isDir) {
1747
- const gitDir = existsSync7(join8(target, ".git")) ? join8(target, ".git") : target;
1748
- if (!existsSync7(join8(gitDir, "HEAD"))) {
1749
- err6(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
2066
+ const gitDir = existsSync8(join9(target, ".git")) ? join9(target, ".git") : target;
2067
+ if (!existsSync8(join9(gitDir, "HEAD"))) {
2068
+ err7(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
1750
2069
  return 1;
1751
2070
  }
1752
2071
  await mountFresh(eng, SOURCE);
@@ -1754,7 +2073,7 @@ async function replay(target, opts) {
1754
2073
  } else {
1755
2074
  const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/v1/workspaces/${target}/git`, headers: {} });
1756
2075
  if (m.restored !== true) {
1757
- err6(
2076
+ err7(
1758
2077
  `workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to replay` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "") + `
1759
2078
  check the name (githolon ws status ${target}), or pass a local holon directory instead`
1760
2079
  );
@@ -1763,7 +2082,7 @@ async function replay(target, opts) {
1763
2082
  }
1764
2083
  const chain = await walkChain(eng, SOURCE);
1765
2084
  if (chain.length === 0) {
1766
- err6(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
2085
+ err7(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
1767
2086
  return 1;
1768
2087
  }
1769
2088
  await mountFresh(eng, REPLAY);
@@ -1772,8 +2091,8 @@ async function replay(target, opts) {
1772
2091
  const tallies = /* @__PURE__ */ new Map();
1773
2092
  let autoRun = !interactive;
1774
2093
  if (!json) {
1775
- out6(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014 every step re-admitted through the wasm gate`);
1776
- if (interactive) out6(" keys: enter = next intent \xB7 a = run to the end \xB7 q = quit\n");
2094
+ out7(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014 every step re-admitted through the wasm gate`);
2095
+ if (interactive) out7(" keys: enter = next intent \xB7 a = run to the end \xB7 q = quit\n");
1777
2096
  }
1778
2097
  for (let i = 0; i < chain.length; i++) {
1779
2098
  const { oid, bytes, doc } = chain[i];
@@ -1787,8 +2106,8 @@ async function replay(target, opts) {
1787
2106
  if (res.ok === false) {
1788
2107
  const msg = `replay REFUSED at intent ${i} (${domain}/${directive}, ${oid.slice(0, 10)}): ${res.error ?? "unknown"}`;
1789
2108
  if (json) emit({ step: i, oid, domain, directive, refused: true, error: res.error ?? "unknown" });
1790
- else err6(msg);
1791
- 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)");
2109
+ else err7(msg);
2110
+ err7("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)");
1792
2111
  printVerdict(verifyChainLocal(eng, SOURCE));
1793
2112
  return 1;
1794
2113
  }
@@ -1809,31 +2128,31 @@ async function replay(target, opts) {
1809
2128
  tallies: Object.fromEntries(tallies)
1810
2129
  });
1811
2130
  } else {
1812
- out6(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)}, ${ms.toFixed(0)} ms)`);
1813
- out6(` payload ${summarizePayload(doc.payload?.payload)}`);
2131
+ out7(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)}, ${ms.toFixed(0)} ms)`);
2132
+ out7(` payload ${summarizePayload(doc.payload?.payload)}`);
1814
2133
  for (const ev of doc.events ?? []) {
1815
2134
  const id = ev.aggregate ?? "?";
1816
2135
  const was = before.get(id);
1817
2136
  const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" : was === void 0 ? "~ touched" : "~ updated";
1818
- out6(` ${verb} ${id}`);
2137
+ out7(` ${verb} ${id}`);
1819
2138
  const ops = summarizeOps(ev);
1820
- if (ops.length > 0) out6(` ${ops}`);
2139
+ if (ops.length > 0) out7(` ${ops}`);
1821
2140
  }
1822
2141
  const tallyLine = [...tallies.entries()].map(([t, n]) => `${t}:${n}`).join(" ");
1823
2142
  const showTally = stepEvery > 0 ? (i + 1) % stepEvery === 0 || i === chain.length - 1 : true;
1824
- if (showTally && tallyLine.length > 0) out6(` rows ${tallyLine}`);
2143
+ if (showTally && tallyLine.length > 0) out7(` rows ${tallyLine}`);
1825
2144
  }
1826
2145
  if (interactive && !autoRun && i < chain.length - 1) {
1827
2146
  const key = await readKey();
1828
2147
  if (key === "quit") {
1829
- out6(`
2148
+ out7(`
1830
2149
  stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below covers the WHOLE chain as delivered`);
1831
2150
  break;
1832
2151
  }
1833
2152
  if (key === "all") autoRun = true;
1834
2153
  }
1835
2154
  }
1836
- if (!json) out6("");
2155
+ if (!json) out7("");
1837
2156
  const verdict = verifyChainLocal(eng, SOURCE);
1838
2157
  if (json) {
1839
2158
  emit({ finale: true, verdict });
@@ -1843,65 +2162,89 @@ stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below cover
1843
2162
  }
1844
2163
 
1845
2164
  // src/status.ts
1846
- import { readFileSync as readFileSync7 } from "node:fs";
1847
- var out7 = (s) => void process.stdout.write(s + "\n");
1848
- var err7 = (s) => void process.stderr.write("error: " + s + "\n");
2165
+ init_engine();
2166
+ init_cloud();
2167
+ import { readFileSync as readFileSync8 } from "node:fs";
2168
+ var out8 = (s) => void process.stdout.write(s + "\n");
2169
+ var err8 = (s) => void process.stderr.write("error: " + s + "\n");
1849
2170
  function lawDiffVerdict(localHash, installed, ws) {
1850
2171
  const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
1851
- const match = active.find((d) => d.domainHash === localHash);
1852
- if (match !== void 0) {
1853
- return { inSync: true, lines: [`\u2713 in sync \u2014 the deployed law IS your local build (${localHash.slice(0, 16)}\u2026)`] };
1854
- }
2172
+ const currents = active.filter((d) => d.current === true);
2173
+ const priors = active.filter((d) => d.current !== true);
2174
+ const localActive = active.find((d) => d.domainHash === localHash);
2175
+ const localIsCurrent = currents.some((d) => d.domainHash === localHash);
1855
2176
  const lines = [];
2177
+ if (localIsCurrent || currents.length === 0 && localActive !== void 0) {
2178
+ lines.push(`\u2713 in sync \u2014 the deployed law IS your local build (${localHash.slice(0, 16)}\u2026)`);
2179
+ if (priors.length > 0) lines.push(` (${priors.length} prior deploy(s) remain Active under install-beside; yours is the current one resolution serves)`);
2180
+ return { inSync: true, lines };
2181
+ }
1856
2182
  if (active.length === 0) {
1857
2183
  lines.push(`\u2717 workspace '${ws}' has NO active tenant law \u2014 your local build is not deployed`);
2184
+ } else if (localActive !== void 0) {
2185
+ lines.push(`~ your build (${localHash.slice(0, 16)}\u2026) is an ACTIVE install on '${ws}', but SUPERSEDED \u2014 a newer deploy is current:`);
2186
+ for (const c of currents) lines.push(` current ${c.domainHash.slice(0, 16)}\u2026 (${c.installedBy ?? "?"}) \u2190 resolution serves this`);
2187
+ lines.push(` redeploy to make your build current: githolon deploy ${ws}`);
2188
+ return { inSync: false, lines };
2189
+ } else if (currents.length > 0) {
2190
+ lines.push(`\u2717 local law differs from the current install${currents.length > 1 ? "s" : ""} on '${ws}':`);
2191
+ for (const c of currents) lines.push(` current ${c.domainHash.slice(0, 16)}\u2026 (${c.installedBy ?? "?"}) \u2190 resolution serves this`);
2192
+ if (priors.length > 0) lines.push(` + ${priors.length} prior active install(s) (install-beside \u2014 historical, not served)`);
1858
2193
  } else {
1859
- lines.push(`\u2717 local law differs from every active installation on '${ws}':`);
2194
+ lines.push(`\u2717 local law differs from every active installation on '${ws}' (${active.length}):`);
1860
2195
  for (const d of active) lines.push(` deployed ${d.domainHash.slice(0, 16)}\u2026 (${d.installedBy ?? "?"})`);
1861
2196
  }
1862
- lines.push(` remedy: githolon deploy ${ws} (installs ${localHash.slice(0, 16)}\u2026)`);
2197
+ lines.push(` remedy: githolon deploy ${ws} (installs ${localHash.slice(0, 16)}\u2026 and makes it current)`);
1863
2198
  return { inSync: false, lines };
1864
2199
  }
2200
+ var CONTROL_PLANE_KEYS = ["nomos", "bootstrap", "workspaces"];
1865
2201
  async function status(wsArg, opts) {
1866
2202
  const cloud = cloudBase(opts.cloud);
1867
2203
  let ws;
1868
2204
  try {
1869
2205
  ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
1870
2206
  } catch (e) {
1871
- err7(e.message);
2207
+ err8(e.message);
1872
2208
  return 1;
1873
2209
  }
1874
2210
  let localHash;
1875
2211
  try {
1876
- const body = JSON.parse(readFileSync7(discoverDeployJson(process.cwd(), opts.file), "utf8"));
2212
+ const body = JSON.parse(readFileSync8(discoverDeployJson(process.cwd(), opts.file), "utf8"));
1877
2213
  if (typeof body.packageUsda === "string") localHash = await sha256hex(body.packageUsda);
1878
2214
  } catch {
1879
2215
  localHash = void 0;
1880
2216
  }
1881
2217
  const r = await fetch(`${cloud}/v1/workspaces/${ws}/domains`).catch(() => void 0);
1882
2218
  if (r === void 0) {
1883
- err7(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
2219
+ err8(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
1884
2220
  return 1;
1885
2221
  }
1886
2222
  const d = await r.json().catch(() => void 0);
1887
2223
  if (!r.ok || d?.ok !== true) {
1888
- err7(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
2224
+ err8(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
1889
2225
  is the workspace born? githolon ws create ${ws}`);
1890
2226
  return 1;
1891
2227
  }
1892
- out7(`workspace ${ws} on ${cloud}`);
1893
- const domains = d.domains ?? [];
1894
- if (domains.length === 0) out7(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
2228
+ out8(`workspace ${ws} on ${cloud}`);
2229
+ const allDomains = d.domains ?? [];
2230
+ const controlPlaneHashes = new Set(CONTROL_PLANE_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
2231
+ const domains = allDomains.filter((dm) => !controlPlaneHashes.has(dm.domainHash));
2232
+ const controllerCount = allDomains.length - domains.length;
2233
+ if (allDomains.length === 0) out8(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
1895
2234
  for (const dom of domains) {
1896
- out7(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}`);
2235
+ const mark = dom.current === true ? " \u2190 current (the holon resolves dispatch here)" : "";
2236
+ out8(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
1897
2237
  }
2238
+ if (controllerCount > 0) out8(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
2239
+ const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
2240
+ if (tenantActive > 1) out8(` (${tenantActive} active tenant installs \u2014 install-beside keeps every prior deploy Active; the holon serves the current one per domain)`);
1898
2241
  if (localHash === void 0) {
1899
- out7(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
2242
+ out8(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
1900
2243
  return 0;
1901
2244
  }
1902
- out7(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
2245
+ out8(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
1903
2246
  const verdict = lawDiffVerdict(localHash, domains, ws);
1904
- for (const line of verdict.lines) out7(line);
2247
+ for (const line of verdict.lines) out8(line);
1905
2248
  return verdict.inSync ? 0 : 2;
1906
2249
  }
1907
2250
 
@@ -1943,6 +2286,15 @@ a bare <ws> resolves from nomos.package.mjs's optional \`workspace\` binding):
1943
2286
  githolon secret set <ws> <secret> import a secret you already hold
1944
2287
  githolon secret rotate <ws> rotate (and re-save) the workspace secret
1945
2288
 
2289
+ Capability credentials (facts on the ledger, values host-side \u2014 the value NEVER rides argv;
2290
+ stdin or --from-gcloud only; architecture/capability_credentials.md):
2291
+ githolon capability bind <ws> <capability> bind a credential: value from stdin (or
2292
+ --from-gcloud <name>) \u2192 the workspace's holon;
2293
+ sha256(value) \u2192 the law-state binding fact
2294
+ githolon capability list <ws> the binding FACTS (never the values)
2295
+ githolon capability rotate <ws> <capability> revoke + re-bind at epoch+1 (new value)
2296
+ githolon capability unbind <ws> <capability> revoke the fact; the host forgets the value
2297
+
1946
2298
  Git lane (stock git push as deploy/birth \u2014 see also: push-to-create):
1947
2299
  githolon git setup install the credential helper for the cloud host
1948
2300
  githolon git remote [<ws>] [<remoteName>] wire this repo to a workspace (default name: nomos)
@@ -2021,23 +2373,71 @@ ${USAGE}`);
2021
2373
  if (sub === "rotate" && ws !== void 0) return secretRotate(ws, opts);
2022
2374
  return usageFail("expected: githolon secret <set <ws> <secret> | rotate <ws>>");
2023
2375
  }
2376
+ async function runCapability(argv) {
2377
+ const usageFail = (m) => {
2378
+ process.stderr.write(`error: ${m}
2379
+
2380
+ ${commandHelp("capability")}`);
2381
+ return 1;
2382
+ };
2383
+ const pos = [];
2384
+ const opts = {};
2385
+ const rest = argv.slice(1);
2386
+ for (let i = 0; i < rest.length; i++) {
2387
+ const a = rest[i];
2388
+ if (a === "--cloud" || a === "--from-gcloud" || a === "--scope" || a === "--expires-at") {
2389
+ const v = rest[++i];
2390
+ if (v === void 0) return usageFail(`${a} requires a value`);
2391
+ if (a === "--cloud") opts.cloud = v;
2392
+ else if (a === "--from-gcloud") opts.fromGcloud = v;
2393
+ else if (a === "--scope") opts.scope = v;
2394
+ else opts.expiresAt = v;
2395
+ } else if (a.startsWith("--")) {
2396
+ return usageFail(`unknown flag '${a}'`);
2397
+ } else {
2398
+ pos.push(a);
2399
+ }
2400
+ }
2401
+ const [sub, ws, capability] = pos;
2402
+ if (sub === "bind" && ws !== void 0 && capability !== void 0) return capabilityBind(ws, capability, opts);
2403
+ if (sub === "rotate" && ws !== void 0 && capability !== void 0) return capabilityRotate(ws, capability, opts);
2404
+ if (sub === "unbind" && ws !== void 0 && capability !== void 0) return capabilityUnbind(ws, capability, opts);
2405
+ if (sub === "list" && ws !== void 0) return capabilityList(ws, opts);
2406
+ return usageFail("expected: githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws>");
2407
+ }
2024
2408
  function isKind(s) {
2025
2409
  return s !== void 0 && KINDS.includes(s);
2026
2410
  }
2027
- function runProof(args) {
2411
+ async function runProof(args) {
2028
2412
  const refuse = (m) => {
2029
2413
  process.stderr.write(`error: ${m}
2030
2414
  `);
2031
2415
  return 1;
2032
2416
  };
2033
- const explicit = args.find((a) => !a.startsWith("--"));
2417
+ const live = args.includes("--live");
2418
+ const keep = args.includes("--keep");
2419
+ if (keep && !live) return refuse("--keep only applies to --live (the offline proof creates no cloud workspace to keep)");
2420
+ let domain;
2421
+ let domainValueIdx = -1;
2422
+ const di = args.findIndex((a) => a === "--domain" || a.startsWith("--domain="));
2423
+ if (di >= 0) {
2424
+ const a = args[di];
2425
+ if (a.includes("=")) domain = a.slice(a.indexOf("=") + 1);
2426
+ else {
2427
+ domain = args[di + 1];
2428
+ domainValueIdx = di + 1;
2429
+ }
2430
+ if (domain === void 0 || domain === "" || domain.startsWith("--")) return refuse("--domain needs a domain key: githolon proof --domain <key>");
2431
+ }
2432
+ if (domain !== void 0 && live) return refuse("--domain is an OFFLINE-lane selector (the --live proof runs the package's primary domain end-to-end); drop --live or drop --domain");
2433
+ const explicit = args.find((a, i) => !a.startsWith("--") && i !== domainValueIdx);
2034
2434
  let proofPath;
2035
2435
  if (explicit !== void 0) {
2036
2436
  proofPath = resolve2(process.cwd(), explicit);
2037
- if (!existsSync8(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
2437
+ if (!existsSync9(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
2038
2438
  } else {
2039
- const buildDir = join9(process.cwd(), "build");
2040
- if (!existsSync8(buildDir)) {
2439
+ const buildDir = join10(process.cwd(), "build");
2440
+ if (!existsSync9(buildDir)) {
2041
2441
  return refuse("no build/ directory here \u2014 run `githolon compile` first (every compile generates build/<name>.proof.mts from your law)");
2042
2442
  }
2043
2443
  const proofs = readdirSync4(buildDir).filter((f) => f.endsWith(".proof.mts"));
@@ -2047,21 +2447,30 @@ function runProof(args) {
2047
2447
  if (proofs.length > 1) {
2048
2448
  return refuse(`found ${proofs.length} proofs (${proofs.join(", ")}) \u2014 name one: githolon proof build/<name>.proof.mts`);
2049
2449
  }
2050
- proofPath = join9(buildDir, proofs[0]);
2450
+ proofPath = join10(buildDir, proofs[0]);
2451
+ }
2452
+ if (!live) {
2453
+ try {
2454
+ const { runOfflineProofStandalone: runOfflineProofStandalone2 } = await Promise.resolve().then(() => (init_proof_offline(), proof_offline_exports));
2455
+ const { cloudBase: cloudBase2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
2456
+ return await runOfflineProofStandalone2(proofPath, cloudBase2(), domain);
2457
+ } catch (e) {
2458
+ return refuse(`${e.message ?? e}`);
2459
+ }
2051
2460
  }
2052
2461
  const resolvePkgDir = (name, fromDir) => {
2053
2462
  try {
2054
- return dirname4(createRequire2(pathToFileURL4(join9(fromDir, "noop.js"))).resolve(`${name}/package.json`));
2463
+ return dirname5(createRequire2(pathToFileURL4(join10(fromDir, "noop.js"))).resolve(`${name}/package.json`));
2055
2464
  } catch {
2056
2465
  return void 0;
2057
2466
  }
2058
2467
  };
2059
2468
  const dslDir = (() => {
2060
2469
  try {
2061
- return dirname4(createRequire2(pathToFileURL4(join9(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
2470
+ return dirname5(createRequire2(pathToFileURL4(join10(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
2062
2471
  } catch {
2063
2472
  try {
2064
- return dirname4(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
2473
+ return dirname5(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
2065
2474
  } catch {
2066
2475
  return void 0;
2067
2476
  }
@@ -2071,17 +2480,22 @@ function runProof(args) {
2071
2480
  if (tsxDir === void 0) {
2072
2481
  return refuse("tsx not found \u2014 add @githolon/dsl to your project's dependencies (tsx rides along) and npm install");
2073
2482
  }
2074
- const tsxPkg = JSON.parse(readFileSync8(join9(tsxDir, "package.json"), "utf8"));
2483
+ const tsxPkg = JSON.parse(readFileSync9(join10(tsxDir, "package.json"), "utf8"));
2075
2484
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
2076
- const tsxCli = join9(tsxDir, tsxBinRel ?? "dist/cli.mjs");
2077
- const r = spawnSync4(process.execPath, [tsxCli, proofPath], { stdio: "inherit", cwd: process.cwd() });
2485
+ const tsxCli = join10(tsxDir, tsxBinRel ?? "dist/cli.mjs");
2486
+ console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
2487
+ const r = spawnSync5(process.execPath, [tsxCli, proofPath], {
2488
+ stdio: "inherit",
2489
+ cwd: process.cwd(),
2490
+ env: { ...process.env, ...keep ? { PROOF_KEEP: "1" } : {} }
2491
+ });
2078
2492
  return r.status ?? 1;
2079
2493
  }
2080
2494
  function parseArgs(argv) {
2081
2495
  const [command, ...rest] = argv;
2082
2496
  if (command !== "generate" && command !== "g") {
2083
2497
  throw new Error(
2084
- `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | secret.`
2498
+ `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | secret | capability.`
2085
2499
  );
2086
2500
  }
2087
2501
  let outDir = DEFAULT_OUT;
@@ -2209,6 +2623,9 @@ ${commandHelp("status")}`);
2209
2623
  if (argv[0] === "ws" || argv[0] === "deploy" || argv[0] === "secret") {
2210
2624
  return runCloud(argv);
2211
2625
  }
2626
+ if (argv[0] === "capability") {
2627
+ return runCapability(argv);
2628
+ }
2212
2629
  if (argv[0] === "login") {
2213
2630
  const agent = argv.includes("--agent");
2214
2631
  const tokenAt = argv.indexOf("--token");
@@ -2265,8 +2682,8 @@ ${USAGE}`);
2265
2682
  let parsed;
2266
2683
  try {
2267
2684
  parsed = parseArgs(argv);
2268
- } catch (err8) {
2269
- process.stderr.write(`error: ${err8.message}
2685
+ } catch (err9) {
2686
+ process.stderr.write(`error: ${err9.message}
2270
2687
 
2271
2688
  ${USAGE}`);
2272
2689
  return 1;
@@ -2285,8 +2702,8 @@ ${USAGE}`);
2285
2702
  process.stdout.write(`created ${path}
2286
2703
  `);
2287
2704
  return 0;
2288
- } catch (err8) {
2289
- process.stderr.write(`error: ${err8.message}
2705
+ } catch (err9) {
2706
+ process.stderr.write(`error: ${err9.message}
2290
2707
  `);
2291
2708
  return 1;
2292
2709
  }