githolon 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +890 -522
  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,606 @@ 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) {
836
+ const src = readFileSync3(proofPath, "utf8");
837
+ const legs = parseOfflineLegs(src);
838
+ const packageName = basename(proofPath).replace(/\.proof\.mts$/, "");
839
+ const deployFile = join6(dirname3(proofPath), `${packageName}.deploy.json`);
840
+ if (!existsSync5(deployFile)) {
841
+ throw new Error(`missing ${deployFile} \u2014 run \`githolon compile\` (the proof and the deploy body are built together)`);
842
+ }
843
+ const deployBody = JSON.parse(readFileSync3(deployFile, "utf8"));
844
+ const lawHash = await sha256hex(deployBody.packageUsda);
845
+ console.log(`githolon proof \u2014 OFFLINE legs on a local holon (the cloud loop: githolon proof --live)`);
846
+ const { eng } = await holonEngine(cloud, deployBody, "githolon-proof");
847
+ const ws = "proof-offline";
848
+ await mountFresh(eng, ws);
849
+ author(eng, ws, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, "githolon-proof"), "");
850
+ author(eng, ws, "nomos", "installDomain", installPayload(lawHash, deployBody.packageUsda, "githolon-proof"), eng.hashes.nomos);
851
+ const v = runOfflineLegs(eng, ws, lawHash, legs);
852
+ for (const l of v.legs) console.log(l);
853
+ if (!v.ok) {
854
+ console.error(`\u2717 ${v.error}`);
855
+ return 1;
856
+ }
857
+ console.log(`
858
+ OFFLINE PROOF GREEN (${Math.round(v.ms)}ms) \u2014 the same legs rerun on every save in \`githolon dev\`.`);
859
+ console.log(`Deploy with confidence when ready: githolon ws create <ws> && githolon deploy <ws> (or the full cloud proof: githolon proof --live)`);
860
+ return 0;
861
+ }
862
+ var init_proof_offline = __esm({
863
+ "src/proof_offline.ts"() {
864
+ "use strict";
865
+ init_engine();
866
+ init_local_holon();
867
+ }
868
+ });
869
+
870
+ // src/cli.ts
871
+ import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
872
+ import { spawnSync as spawnSync5 } from "node:child_process";
873
+ import { createRequire as createRequire2 } from "node:module";
874
+ import { dirname as dirname5, join as join10, resolve as resolve2 } from "node:path";
875
+ import { pathToFileURL as pathToFileURL4 } from "node:url";
876
+
877
+ // src/generate.ts
878
+ import { mkdirSync, existsSync, writeFileSync } from "node:fs";
879
+ import { join } from "node:path";
880
+
881
+ // src/naming.ts
882
+ function words(raw) {
883
+ return raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-\s]+/g, " ").trim().toLowerCase().split(" ").filter((w) => w.length > 0);
884
+ }
885
+ var cap = (w) => w.length === 0 ? w : w[0].toUpperCase() + w.slice(1);
886
+ function pascalCase(raw) {
887
+ return words(raw).map(cap).join("");
888
+ }
889
+ function camelCase(raw) {
890
+ return words(raw).map((w, i) => i === 0 ? w : cap(w)).join("");
891
+ }
892
+ function snakeCase(raw) {
893
+ return words(raw).join("_");
894
+ }
895
+ function screamingSnakeCase(raw) {
896
+ return words(raw).join("_").toUpperCase();
897
+ }
898
+ function assertValidName(raw) {
899
+ const ws = words(raw);
900
+ if (ws.length === 0) {
901
+ throw new Error(`Invalid name: '${raw}' \u2014 give at least one word, e.g. 'work_order'.`);
902
+ }
903
+ for (const w of ws) {
904
+ if (!/^[a-z][a-z0-9]*$/.test(w)) {
905
+ throw new Error(
906
+ `Invalid name token '${w}' in '${raw}' \u2014 words must be alphanumeric and start with a letter (got after normalisation: ${ws.join(", ")}).`
907
+ );
908
+ }
909
+ }
910
+ return ws;
911
+ }
912
+
913
+ // src/templates.ts
914
+ function namesFor(raw) {
915
+ return {
916
+ raw,
917
+ pascal: pascalCase(raw),
918
+ camel: camelCase(raw),
919
+ statusesConst: `${screamingSnakeCase(raw)}_STATUSES`
920
+ };
921
+ }
922
+ var HEADER = (raw, kind) => `/**
923
+ * The \`${raw}\` ${kind} \u2014 scaffolded by \`githolon generate\`.
924
+ *
925
+ * Authoring rules (see @githolon/dsl): write ONLY aggregates (typed fields, each
926
+ * tagged with a merge Driver) and directives (Zod payload + declarative plan).
927
+ * You never write apply/fold/merge \u2014 the kernel owns the merge algebra.
928
+ *
929
+ * Convention: the fail-closed \`Conflict\` driver is the default for scalars.
930
+ * Relax only what differs: \`.merge(Lww)\`, \`.merge(AddWins)\`, \`.merge(MapOf(Lww))\`.
931
+ */`;
932
+ function aggregateBlock(n) {
933
+ return `export const ${n.statusesConst} = ["active", "retired"] as const;
934
+
935
+ export const ${n.pascal} = aggregate("${n.pascal}", {
936
+ // Default driver is the fail-closed \`Conflict\`: concurrent edits surface
937
+ // as a conflict rather than silently coalescing. Declare only what differs.
938
+ label: t.string().merge(Conflict),
939
+ // Overrides \u2014 last-write-wins / set-union / per-key map:
940
+ pos: t.int().merge(Lww),
941
+ status: t.enum(${n.statusesConst}).merge(Lww),
942
+ tags: t.set(t.string()).merge(AddWins),
943
+ customFields: t.map(t.string()).merge(MapOf(Lww)),
944
+ });`;
945
+ }
946
+ function domainTemplate(raw) {
947
+ const n = namesFor(raw);
948
+ return `${HEADER(raw, "domain")}
949
+ import {
950
+ z,
951
+ aggregate,
952
+ directive,
953
+ t,
954
+ Conflict,
955
+ Lww,
956
+ AddWins,
957
+ MapOf,
958
+ set,
959
+ addToSet,
960
+ setEntry,
961
+ } from "@githolon/dsl";
962
+
963
+ ${aggregateBlock(n)}
964
+
965
+ /** create${n.pascal} \u2014 .creates the aggregate; seeds label/pos/status. */
966
+ export const create${n.pascal} = directive("create${n.pascal}")
967
+ .creates(${n.pascal})
968
+ .payload(
969
+ z.object({
970
+ label: z.string(),
971
+ pos: z.number().int(),
972
+ status: z.enum(${n.statusesConst}),
973
+ }),
974
+ )
975
+ .plan((p) => [
976
+ set(${n.pascal}, "label", p.label),
977
+ set(${n.pascal}, "pos", p.pos),
978
+ set(${n.pascal}, "status", p.status),
979
+ ]);
980
+
981
+ /** move${n.pascal} \u2014 .mutates pos. */
982
+ export const move${n.pascal} = directive("move${n.pascal}")
983
+ .mutates(${n.pascal})
984
+ .payload(z.object({ pos: z.number().int() }))
985
+ .plan((p) => [set(${n.pascal}, "pos", p.pos)]);
986
+
987
+ /** tag${n.pascal} \u2014 .mutates tags (add-wins union). */
988
+ export const tag${n.pascal} = directive("tag${n.pascal}")
989
+ .mutates(${n.pascal})
990
+ .payload(z.object({ tags: z.array(z.string()) }))
991
+ .plan((p) => [addToSet(${n.pascal}, "tags", p.tags)]);
992
+
993
+ /** setCustomFieldOn${n.pascal} \u2014 .mutates customFields (a map entry). */
994
+ export const setCustomFieldOn${n.pascal} = directive("setCustomFieldOn${n.pascal}")
995
+ .mutates(${n.pascal})
996
+ .payload(z.object({ key: z.string(), value: z.string() }))
997
+ .plan((p) => [setEntry(${n.pascal}, "customFields", p.key, p.value)]);
998
+ `;
999
+ }
1000
+ function aggregateTemplate(raw) {
1001
+ const n = namesFor(raw);
1002
+ return `${HEADER(raw, "aggregate")}
1003
+ import { aggregate, t, Conflict, Lww, AddWins, MapOf } from "@githolon/dsl";
1004
+
1005
+ ${aggregateBlock(n)}
1006
+ `;
1007
+ }
1008
+ function intentTemplate(raw) {
1009
+ const n = namesFor(raw);
1010
+ return `${HEADER(raw, "intent (directive)")}
1011
+ import { z, aggregate, directive, t, Conflict, set } from "@githolon/dsl";
1012
+
1013
+ // Stub target \u2014 in a real domain, import the existing aggregate handle instead.
1014
+ export const ${n.pascal} = aggregate("${n.pascal}", {
1015
+ label: t.string().merge(Conflict),
1016
+ });
1017
+
1018
+ /** ${n.camel} \u2014 .mutates the target aggregate. */
1019
+ export const ${n.camel} = directive("${n.camel}")
1020
+ .mutates(${n.pascal})
1021
+ .payload(z.object({ label: z.string() }))
1022
+ .plan((p) => [set(${n.pascal}, "label", p.label)]);
1023
+ `;
1024
+ }
1025
+
1026
+ // src/generate.ts
1027
+ var RENDERERS = {
1028
+ domain: domainTemplate,
1029
+ aggregate: aggregateTemplate,
1030
+ intent: intentTemplate
1031
+ };
1032
+ function renderScaffold(kind, name) {
1033
+ assertValidName(name);
1034
+ const render = RENDERERS[kind];
1035
+ if (!render) {
1036
+ throw new Error(`Unknown generator kind '${kind}'. Use: domain | aggregate | intent.`);
1037
+ }
1038
+ return render(name);
1039
+ }
1040
+ function generate(opts) {
1041
+ const contents = renderScaffold(opts.kind, opts.name);
1042
+ const fileName = `${snakeCase(opts.name)}.ts`;
1043
+ const path = join(opts.outDir, fileName);
1044
+ if (existsSync(path) && !opts.force) {
1045
+ throw new Error(
1046
+ `Refusing to overwrite existing file: ${path}
1047
+ Re-run with --force to replace it.`
1048
+ );
1049
+ }
1050
+ mkdirSync(opts.outDir, { recursive: true });
1051
+ writeFileSync(path, contents, "utf8");
1052
+ return { path, contents };
1053
+ }
1054
+
1055
+ // src/capability.ts
1056
+ init_cloud();
1057
+ import { spawnSync } from "node:child_process";
1058
+ var out2 = (s) => void process.stdout.write(s + "\n");
1059
+ var err2 = (s) => void process.stderr.write("error: " + s + "\n");
1060
+ var CAP_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
1061
+ function readStdin() {
1062
+ if (process.stdin.isTTY === true) {
1063
+ process.stderr.write(
1064
+ "paste the credential value, then Ctrl-D (it never rides argv \u2014 ps/history are hosts too):\n"
1065
+ );
1066
+ }
1067
+ return new Promise((resolveValue) => {
1068
+ let buf = "";
1069
+ process.stdin.setEncoding("utf8");
1070
+ process.stdin.on("data", (chunk) => {
1071
+ buf += chunk;
1072
+ });
1073
+ process.stdin.on("end", () => resolveValue(buf.trim()));
1074
+ process.stdin.resume();
1075
+ });
1076
+ }
1077
+ function readGcloud(name) {
1078
+ const r = spawnSync("gcloud", ["secrets", "versions", "access", "latest", `--secret=${name}`], {
1079
+ encoding: "utf8",
1080
+ maxBuffer: 1024 * 1024
1081
+ });
1082
+ if (r.error !== void 0 || r.status !== 0) {
1083
+ throw new Error(
1084
+ `gcloud secret read failed (${r.error?.message ?? r.stderr?.trim() ?? `exit ${r.status}`}) \u2014 is gcloud installed + authed, and does secret '${name}' exist?`
1085
+ );
1086
+ }
1087
+ const v = r.stdout.trim();
1088
+ if (v === "") throw new Error(`gcloud secret '${name}' is empty`);
1089
+ return v;
1090
+ }
1091
+ function parseExpiresAt(s) {
1092
+ const n = /^\d+$/.test(s) ? Number(s) : Date.parse(s);
1093
+ if (!Number.isInteger(n) || n <= 0) throw new Error(`--expires-at '${s}' is neither epoch-ms nor an ISO date`);
1094
+ return n;
1095
+ }
1096
+ async function bindLane(lane, ws, capability, opts) {
1097
+ const cloud = cloudBase(opts.cloud);
1098
+ if (!CAP_NAME.test(capability)) {
1099
+ err2(`capability must be a lowercase law name matching [a-z0-9][a-z0-9._-]{0,63} \u2014 e.g. cf-analytics, openai`);
1100
+ return 1;
1101
+ }
1102
+ const secret = getSecret(cloud, ws);
1103
+ if (secret === void 0) {
1104
+ err2(
1105
+ `no stored secret for ${ws} on ${cloud} \u2014 capability ${lane} is owner-gated
1106
+ githolon secret set ${ws} <secret> (import the workspaceSecret you hold)`
1107
+ );
1108
+ return 1;
1109
+ }
1110
+ let value;
1111
+ try {
1112
+ value = opts.fromGcloud !== void 0 ? readGcloud(opts.fromGcloud) : await readStdin();
1113
+ } catch (e) {
1114
+ err2(e.message);
1115
+ return 1;
1116
+ }
1117
+ if (value === "") {
1118
+ err2("no credential value arrived \u2014 pipe it on stdin, or pass --from-gcloud <secret-name>");
1119
+ return 1;
1120
+ }
1121
+ let expiresAt;
1122
+ try {
1123
+ expiresAt = opts.expiresAt !== void 0 ? parseExpiresAt(opts.expiresAt) : void 0;
1124
+ } catch (e) {
1125
+ err2(e.message);
1126
+ return 1;
1127
+ }
1128
+ const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/${lane}`, {
1129
+ method: "POST",
1130
+ headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
1131
+ body: JSON.stringify({
1132
+ capability,
1133
+ secret: value,
1134
+ ...opts.scope !== void 0 ? { scope: opts.scope } : {},
1135
+ ...expiresAt !== void 0 ? { expiresAt } : {}
1136
+ })
1137
+ });
1138
+ const d = await r.json().catch(() => void 0);
1139
+ if (r.status === 401) {
1140
+ err2(`${lane} refused (401) \u2014 the stored secret for ${ws} is not the workspace's. githolon secret set ${ws} <secret>`);
1141
+ return 1;
1142
+ }
1143
+ if (!r.ok || d?.ok !== true) {
1144
+ err2(`capability ${lane} '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
1145
+ return 1;
867
1146
  }
1147
+ out2(`\u2713 ${capability} ${lane === "rotate" ? "rotated" : "bound"} on ${ws} (epoch ${d.epoch})`);
1148
+ out2(` the VALUE lives host-side only (the workspace's holon) \u2014 it never enters the ledger`);
1149
+ out2(` the law fact: keyHash ${(d.keyHash ?? "").slice(0, 16)}\u2026 status ${d.status}`);
1150
+ return 0;
868
1151
  }
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)));
1152
+ async function capabilityBind(ws, capability, opts) {
1153
+ return bindLane("bind", ws, capability, opts);
1154
+ }
1155
+ async function capabilityRotate(ws, capability, opts) {
1156
+ return bindLane("rotate", ws, capability, opts);
1157
+ }
1158
+ async function capabilityList(ws, opts) {
1159
+ const cloud = cloudBase(opts.cloud);
1160
+ const secret = getSecret(cloud, ws);
1161
+ if (secret === void 0) {
1162
+ err2(`no stored secret for ${ws} on ${cloud} \u2014 the binding facts are owner-gated
1163
+ githolon secret set ${ws} <secret>`);
1164
+ return 1;
875
1165
  }
876
- return new Directory2(contents);
1166
+ const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities`, {
1167
+ headers: { authorization: `Bearer ${secret}` }
1168
+ });
1169
+ const d = await r.json().catch(() => void 0);
1170
+ if (!r.ok || d?.ok !== true) {
1171
+ err2(`capability list '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
1172
+ return 1;
1173
+ }
1174
+ const bindings = d.bindings ?? [];
1175
+ if (bindings.length === 0) {
1176
+ out2(`no capability bindings on ${ws} \u2014 bind one: githolon capability bind ${ws} <capability>`);
1177
+ return 0;
1178
+ }
1179
+ for (const b of bindings) {
1180
+ const bits = [
1181
+ `epoch ${b.epoch}`,
1182
+ `${b.status}`,
1183
+ `keyHash ${(b.keyHash ?? "").slice(0, 16)}\u2026`,
1184
+ ...b.scope !== void 0 ? [`scope ${b.scope}`] : [],
1185
+ ...b.expiresAt !== void 0 ? [`expires ${new Date(b.expiresAt).toISOString()}`] : [],
1186
+ ...b.held !== void 0 ? [b.held ? "value held" : "VALUE MISSING (rotate to restore)"] : []
1187
+ ];
1188
+ out2(`${b.capability} ${bits.join(" \xB7 ")}`);
1189
+ }
1190
+ out2(`(facts about authority \u2014 the values live host-side and are never listed)`);
1191
+ return 0;
877
1192
  }
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
1193
+ async function capabilityUnbind(ws, capability, opts) {
1194
+ const cloud = cloudBase(opts.cloud);
1195
+ const secret = getSecret(cloud, ws);
1196
+ if (secret === void 0) {
1197
+ err2(`no stored secret for ${ws} on ${cloud} \u2014 unbind is owner-gated
1198
+ githolon secret set ${ws} <secret>`);
1199
+ return 1;
1200
+ }
1201
+ const r = await fetch(`${cloud}/v1/workspaces/${ws}/capabilities/unbind`, {
1202
+ method: "POST",
1203
+ headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
1204
+ body: JSON.stringify({ capability })
889
1205
  });
890
- return { eng, runtime };
1206
+ const d = await r.json().catch(() => void 0);
1207
+ if (!r.ok || d?.ok !== true) {
1208
+ err2(`capability unbind '${capability}' on ${ws} failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
1209
+ return 1;
1210
+ }
1211
+ out2(`\u2713 ${capability} unbound on ${ws} \u2014 the law fact is revoked (the record stays) and the host forgot the value`);
1212
+ return 0;
891
1213
  }
892
- async function mountFresh(eng, ws) {
893
- await mountWorkspace(eng, ws, { remote: "https://unborn.invalid/", headers: {} });
1214
+
1215
+ // src/cli.ts
1216
+ init_cloud();
1217
+
1218
+ // src/compile.ts
1219
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
1220
+ import { createRequire } from "node:module";
1221
+ import { dirname, join as join3 } from "node:path";
1222
+ import { pathToFileURL } from "node:url";
1223
+ function resolveCompileLauncher(cwd) {
1224
+ const resolveDslPkg = (fromDir) => {
1225
+ try {
1226
+ const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join3(fromDir, "noop.js")));
1227
+ return req.resolve("@githolon/dsl/package.json");
1228
+ } catch {
1229
+ return void 0;
1230
+ }
1231
+ };
1232
+ const dslPkg = resolveDslPkg(cwd) ?? resolveDslPkg(void 0);
1233
+ return dslPkg === void 0 ? void 0 : join3(dirname(dslPkg), "compile_package.mjs");
894
1234
  }
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;
1235
+ var NO_DSL_REMEDY = "@githolon/dsl not found \u2014 add it to your project's dependencies";
1236
+ function runCompile(args) {
1237
+ const launcher = resolveCompileLauncher(process.cwd());
1238
+ if (launcher === void 0) {
1239
+ process.stderr.write(`error: ${NO_DSL_REMEDY}
1240
+ `);
1241
+ return 1;
903
1242
  }
1243
+ const r = spawnSync2(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
1244
+ return r.status ?? 1;
904
1245
  }
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;
1246
+ function compileAsync(cwd, args = []) {
1247
+ const launcher = resolveCompileLauncher(cwd);
1248
+ const t0 = performance.now();
1249
+ if (launcher === void 0) {
1250
+ return Promise.resolve({ ok: false, ms: 0, output: NO_DSL_REMEDY });
910
1251
  }
911
- err2(`chain INVALID at ${v["check"]}${v["atIndex"] !== void 0 ? ` (intent ${v["atIndex"]})` : ""}: ${v["error"]}`);
912
- return false;
1252
+ return new Promise((resolveRun) => {
1253
+ const chunks = [];
1254
+ const dec3 = new TextDecoder();
1255
+ const child = spawn(process.execPath, [launcher, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
1256
+ child.stdout?.on("data", (c) => chunks.push(dec3.decode(c)));
1257
+ child.stderr?.on("data", (c) => chunks.push(dec3.decode(c)));
1258
+ child.on("error", (e) => resolveRun({ ok: false, ms: performance.now() - t0, output: String(e) }));
1259
+ child.on(
1260
+ "close",
1261
+ (code) => resolveRun({ ok: code === 0, ms: performance.now() - t0, output: chunks.join("") })
1262
+ );
1263
+ });
913
1264
  }
914
1265
 
1266
+ // src/dev.ts
1267
+ init_engine();
1268
+ init_cloud();
1269
+ import { existsSync as existsSync6, readFileSync as readFileSync4, watch } from "node:fs";
1270
+ import { createServer } from "node:http";
1271
+ import { dirname as dirname4, join as join7, resolve } from "node:path";
1272
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
1273
+ init_local_holon();
1274
+
915
1275
  // src/project.ts
916
1276
  import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
917
1277
  import { join as join5 } from "node:path";
@@ -997,89 +1357,10 @@ async function resolveWorkspace(explicit, cwd, target, usageHint) {
997
1357
  return resolveWorkspaceDecl(project.workspace, target, project.configPath);
998
1358
  }
999
1359
 
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
1360
  // src/dev.ts
1081
- var out3 = (s) => void process.stdout.write(s + "\n");
1082
- var err3 = (s) => void process.stderr.write("error: " + s + "\n");
1361
+ init_proof_offline();
1362
+ var out4 = (s) => void process.stdout.write(s + "\n");
1363
+ var err4 = (s) => void process.stderr.write("error: " + s + "\n");
1083
1364
  var WS = "dev";
1084
1365
  var DEFAULT_PORT = 7700;
1085
1366
  var DEBOUNCE_MS = 200;
@@ -1163,11 +1444,11 @@ function startServer(s) {
1163
1444
  });
1164
1445
  }
1165
1446
  function readDeployBody(cwd, packageName) {
1166
- const file = join6(cwd, "build", `${packageName}.deploy.json`);
1167
- if (!existsSync5(file)) {
1447
+ const file = join7(cwd, "build", `${packageName}.deploy.json`);
1448
+ if (!existsSync6(file)) {
1168
1449
  throw new Error(`compile produced no ${file} \u2014 check the compile output above`);
1169
1450
  }
1170
- return JSON.parse(readFileSync3(file, "utf8"));
1451
+ return JSON.parse(readFileSync4(file, "utf8"));
1171
1452
  }
1172
1453
  async function installLaw(s, deployBody, installedBy) {
1173
1454
  const newHash = await sha256hex(deployBody.packageUsda);
@@ -1186,11 +1467,11 @@ async function installLaw(s, deployBody, installedBy) {
1186
1467
  return { ok: true };
1187
1468
  }
1188
1469
  async function runProofCycle(s, cwd, deployBody, installedBy) {
1189
- const proofPath = join6(cwd, "build", `${s.packageName}.proof.mts`);
1190
- if (!existsSync5(proofPath)) return void 0;
1470
+ const proofPath = join7(cwd, "build", `${s.packageName}.proof.mts`);
1471
+ if (!existsSync6(proofPath)) return void 0;
1191
1472
  const scratch = `proof-${s.cycle}`;
1192
1473
  try {
1193
- const legs = parseOfflineLegs(readFileSync3(proofPath, "utf8"));
1474
+ const legs = parseOfflineLegs(readFileSync4(proofPath, "utf8"));
1194
1475
  await mountFresh(s.eng, scratch);
1195
1476
  author(s.eng, scratch, "bootstrap", "installDomain", installPayload(s.eng.hashes.nomos, s.eng.nomosPkg, installedBy), "");
1196
1477
  author(s.eng, scratch, "nomos", "installDomain", installPayload(s.lawHash, deployBody.packageUsda, installedBy), s.eng.hashes.nomos);
@@ -1205,21 +1486,21 @@ async function dev(opts) {
1205
1486
  const cwd = process.cwd();
1206
1487
  const cloud = cloudBase(opts.cloud);
1207
1488
  const project = await loadProject(cwd).catch((e) => {
1208
- err3(e.message);
1489
+ err4(e.message);
1209
1490
  return void 0;
1210
1491
  });
1211
1492
  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>\``);
1493
+ if (!existsSync6(join7(cwd, CONFIG_FILE))) {
1494
+ err4(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
1214
1495
  }
1215
1496
  return 1;
1216
1497
  }
1217
1498
  const creds = loadCreds();
1218
1499
  const installedBy = creds.session?.uid ?? creds.principal ?? "githolon-dev";
1219
- out3(`githolon dev \u2014 ${project.name}: compiling\u2026`);
1500
+ out4(`githolon dev \u2014 ${project.name}: compiling\u2026`);
1220
1501
  const firstCompile = await compileAsync(cwd);
1221
1502
  if (!firstCompile.ok) {
1222
- err3(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
1503
+ err4(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
1223
1504
  ${firstCompile.output}`);
1224
1505
  return 1;
1225
1506
  }
@@ -1227,14 +1508,14 @@ ${firstCompile.output}`);
1227
1508
  try {
1228
1509
  deployBody = readDeployBody(cwd, project.name);
1229
1510
  } catch (e) {
1230
- err3(e.message);
1511
+ err4(e.message);
1231
1512
  return 1;
1232
1513
  }
1233
1514
  let engBoot;
1234
1515
  try {
1235
1516
  engBoot = await holonEngine(cloud, deployBody, installedBy);
1236
1517
  } catch (e) {
1237
- err3(`cannot boot the local holon: ${String(e.message ?? e)}
1518
+ err4(`cannot boot the local holon: ${String(e.message ?? e)}
1238
1519
  the runtime rides ${cloud}/v1/runtime/* and is cached in ~/.holon/runtime after one fetch \u2014 connect once, then dev runs offline`);
1239
1520
  return 1;
1240
1521
  }
@@ -1257,31 +1538,31 @@ ${firstCompile.output}`);
1257
1538
  s.intents++;
1258
1539
  const installed = await installLaw(s, deployBody, installedBy);
1259
1540
  if (!installed.ok) {
1260
- err3(installed.error ?? "law install failed");
1541
+ err4(installed.error ?? "law install failed");
1261
1542
  return 1;
1262
1543
  }
1263
1544
  s.proof = await runProofCycle(s, cwd, deployBody, installedBy);
1264
1545
  if (opts.once === true) {
1265
- out3(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
1546
+ out4(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
1266
1547
  return s.proof === void 0 || s.proof.ok ? 0 : 1;
1267
1548
  }
1268
1549
  let server;
1269
1550
  try {
1270
1551
  server = await startServer(s);
1271
1552
  } catch (e) {
1272
- err3(e.message);
1553
+ err4(e.message);
1273
1554
  return 1;
1274
1555
  }
1275
1556
  const moduleDirs = /* @__PURE__ */ new Set();
1276
1557
  try {
1277
1558
  const cfgModule = await import(pathToFileURL3(project.configPath).href);
1278
1559
  for (const d of cfgModule.default?.domains ?? []) {
1279
- for (const m of d.modules ?? []) moduleDirs.add(dirname3(resolve(cwd, m)));
1560
+ for (const m of d.modules ?? []) moduleDirs.add(dirname4(resolve(cwd, m)));
1280
1561
  }
1281
1562
  } catch {
1282
- moduleDirs.add(join6(cwd, "domains"));
1563
+ moduleDirs.add(join7(cwd, "domains"));
1283
1564
  }
1284
- if (moduleDirs.size === 0) moduleDirs.add(join6(cwd, "domains"));
1565
+ if (moduleDirs.size === 0) moduleDirs.add(join7(cwd, "domains"));
1285
1566
  let timer;
1286
1567
  let cycling = false;
1287
1568
  let queued = false;
@@ -1297,7 +1578,7 @@ ${firstCompile.output}`);
1297
1578
  s.compileMs = run.ms;
1298
1579
  if (!run.ok) {
1299
1580
  s.lastError = run.output.trim() || "compile failed";
1300
- out3(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
1581
+ out4(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
1301
1582
  ${run.output}`);
1302
1583
  } else {
1303
1584
  s.lastError = void 0;
@@ -1306,16 +1587,16 @@ ${run.output}`);
1306
1587
  const inst = await installLaw(s, body, installedBy);
1307
1588
  if (!inst.ok) {
1308
1589
  s.lastError = inst.error ?? "install failed";
1309
- out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1590
+ out4(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1310
1591
  } else {
1311
1592
  s.proof = await runProofCycle(s, cwd, body, installedBy);
1312
1593
  const total = performance.now() - t0;
1313
- out3(statusScreen(s));
1314
- out3(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
1594
+ out4(statusScreen(s));
1595
+ out4(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
1315
1596
  }
1316
1597
  } catch (e) {
1317
1598
  s.lastError = String(e.message ?? e);
1318
- out3(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1599
+ out4(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
1319
1600
  }
1320
1601
  }
1321
1602
  cycling = false;
@@ -1331,7 +1612,7 @@ ${run.output}`);
1331
1612
  };
1332
1613
  const watchers = [];
1333
1614
  for (const d of moduleDirs) {
1334
- if (!existsSync5(d)) continue;
1615
+ if (!existsSync6(d)) continue;
1335
1616
  watchers.push(watch(d, { recursive: true }, (_evt, f) => onChange(f)));
1336
1617
  s.watchRoots.push(d.startsWith(cwd) ? d.slice(cwd.length + 1) + "/" : d);
1337
1618
  }
@@ -1341,8 +1622,8 @@ ${run.output}`);
1341
1622
  })
1342
1623
  );
1343
1624
  s.watchRoots.push(CONFIG_FILE);
1344
- out3(statusScreen(s));
1345
- out3(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
1625
+ out4(statusScreen(s));
1626
+ out4(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
1346
1627
  return new Promise((resolveExit) => {
1347
1628
  const stop = () => {
1348
1629
  for (const w of watchers) w.close();
@@ -1355,13 +1636,14 @@ ${run.output}`);
1355
1636
  }
1356
1637
 
1357
1638
  // src/git.ts
1358
- import { spawnSync as spawnSync2 } from "node:child_process";
1639
+ init_cloud();
1640
+ import { spawnSync as spawnSync3 } from "node:child_process";
1359
1641
  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");
1642
+ import { readFileSync as readFileSync5 } from "node:fs";
1643
+ var out5 = (s) => void process.stdout.write(s + "\n");
1644
+ var err5 = (s) => void process.stderr.write("error: " + s + "\n");
1363
1645
  function git2(args, opts = {}) {
1364
- const r = spawnSync2("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
1646
+ const r = spawnSync3("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
1365
1647
  return r.status ?? 1;
1366
1648
  }
1367
1649
  function ensureSecret(cloud, ws) {
@@ -1386,36 +1668,36 @@ function workspaceFromPath(path) {
1386
1668
  }
1387
1669
  async function gitCredential(action) {
1388
1670
  if (action !== "get") return 0;
1389
- const kv = parseCredentialInput(readFileSync4(0, "utf8"));
1671
+ const kv = parseCredentialInput(readFileSync5(0, "utf8"));
1390
1672
  const ws = workspaceFromPath(kv["path"]);
1391
1673
  if (kv["protocol"] !== "https" || ws === void 0) return 0;
1392
1674
  const cloud = `https://${kv["host"]}`;
1393
1675
  const token = await sessionToken().catch(() => void 0);
1394
- out4(`username=${token ?? "nomos"}`);
1395
- out4(`password=${ensureSecret(cloud, ws)}`);
1396
- out4("");
1676
+ out5(`username=${token ?? "nomos"}`);
1677
+ out5(`password=${ensureSecret(cloud, ws)}`);
1678
+ out5("");
1397
1679
  return 0;
1398
1680
  }
1399
1681
  async function gitSetup(opts) {
1400
1682
  const cloud = cloudBase(opts.cloud);
1401
1683
  const self = process.argv[1];
1402
1684
  if (self === void 0) {
1403
- err4("cannot locate the githolon CLI entry to install as a credential helper");
1685
+ err5("cannot locate the githolon CLI entry to install as a credential helper");
1404
1686
  return 1;
1405
1687
  }
1406
1688
  const helper = `!"${process.execPath}" "${self}" git-credential`;
1407
1689
  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?");
1690
+ err5("git config failed \u2014 is git installed?");
1409
1691
  return 1;
1410
1692
  }
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`);
1693
+ out5(`\u2713 git credential helper installed for ${cloud} (global gitconfig)`);
1694
+ out5(` pushes/clones of ${cloud}/v1/workspaces/<ws>/git now authenticate from ~/.holon`);
1413
1695
  return 0;
1414
1696
  }
1415
1697
  async function gitRemote(ws, name, opts) {
1416
1698
  const cloud = cloudBase(opts.cloud);
1417
1699
  if (git2(["rev-parse", "--git-dir"], { quiet: true }) !== 0) {
1418
- err4("not a git repository \u2014 run inside your repo (git init first)");
1700
+ err5("not a git repository \u2014 run inside your repo (git init first)");
1419
1701
  return 1;
1420
1702
  }
1421
1703
  const setupRc = await gitSetup(opts);
@@ -1424,7 +1706,7 @@ async function gitRemote(ws, name, opts) {
1424
1706
  const url = `${cloud}/v1/workspaces/${ws}/git`;
1425
1707
  if (git2(["remote", "add", name, url], { quiet: true }) !== 0) {
1426
1708
  if (git2(["remote", "set-url", name, url], { quiet: true }) !== 0) {
1427
- err4(`could not add or update remote '${name}'`);
1709
+ err5(`could not add or update remote '${name}'`);
1428
1710
  return 1;
1429
1711
  }
1430
1712
  }
@@ -1432,7 +1714,7 @@ async function gitRemote(ws, name, opts) {
1432
1714
  if (token === void 0) {
1433
1715
  const principal = loadCreds().principal;
1434
1716
  if (principal === void 0) {
1435
- err4(
1717
+ err5(
1436
1718
  `remote '${name}' \u2192 ${url} is wired, but a BIRTH push needs a principal:
1437
1719
  githolon login --agent (then just push)
1438
1720
  \u2014 or re-run after githolon ws create/login so a principal is on file`
@@ -1440,13 +1722,13 @@ async function gitRemote(ws, name, opts) {
1440
1722
  return 1;
1441
1723
  }
1442
1724
  if (git2(["config", `http.${url}.extraHeader`, `x-nomos-principal: ${principal}`]) !== 0) {
1443
- err4("git config failed setting the principal header");
1725
+ err5("git config failed setting the principal header");
1444
1726
  return 1;
1445
1727
  }
1446
1728
  }
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`);
1729
+ out5(`\u2713 remote '${name}' \u2192 ${url}`);
1730
+ out5(` workspace secret on file; ${token !== void 0 ? "births ride your session (verified)" : "births ride x-nomos-principal (bare uid)"}`);
1731
+ out5(` birth/deploy by push: git push ${name} main`);
1450
1732
  return 0;
1451
1733
  }
1452
1734
 
@@ -1462,9 +1744,9 @@ var HELP = {
1462
1744
  examples: ["githolon compile", "githolon compile nomos.package.mjs --layered"]
1463
1745
  },
1464
1746
  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"]
1747
+ usage: "githolon proof [build/<name>.proof.mts] [--live] [--keep]",
1748
+ 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). --live runs the full cloud loop (throwaway workspace \u2192\ndeploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence), RETIRED on every exit;\n--keep keeps it and prints the secret once. ALL GREEN or it names the jam. The offline legs\nalso rerun on every save in `githolon dev`.",
1749
+ examples: ["githolon proof", "githolon proof --live", "githolon proof --live --keep"]
1468
1750
  },
1469
1751
  dev: {
1470
1752
  usage: "githolon dev [--port <n>] [--cloud <url>] [--once]",
@@ -1550,6 +1832,22 @@ var HELP = {
1550
1832
  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
1833
  examples: ["githolon secret set my-ws nws_v1_\u2026", "githolon secret rotate my-ws"]
1552
1834
  },
1835
+ capability: {
1836
+ usage: "githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws> [--from-gcloud <name>] [--scope <s>] [--expires-at <iso|ms>] [--cloud <url>]",
1837
+ 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.",
1838
+ flags: [
1839
+ ["--from-gcloud <name>", "read the value from `gcloud secrets versions access latest --secret=<name>`"],
1840
+ ["--scope <s>", "a narrowing label recorded on the law fact (e.g. read-only)"],
1841
+ ["--expires-at <t>", "expiry recorded on the law fact (ISO date/datetime or epoch-ms; fail-closed at the host)"],
1842
+ ["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
1843
+ ],
1844
+ examples: [
1845
+ "cat token.txt | githolon capability bind nomos-usage cf-analytics",
1846
+ "githolon capability bind my-ws openai --from-gcloud openai-api-key --scope read-only",
1847
+ "githolon capability list nomos-usage",
1848
+ "githolon capability rotate nomos-usage cf-analytics --from-gcloud cf-analytics-token"
1849
+ ]
1850
+ },
1553
1851
  git: {
1554
1852
  usage: "githolon git <setup | remote [<ws>] [<remoteName>]> [--cloud <url>] [--target <name>]",
1555
1853
  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 +1876,37 @@ function commandHelp(cmd) {
1578
1876
  }
1579
1877
 
1580
1878
  // 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");
1879
+ init_engine();
1880
+ init_cloud();
1881
+ init_local_holon();
1882
+ import { spawnSync as spawnSync4 } from "node:child_process";
1883
+ import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
1884
+ import { join as join8 } from "node:path";
1885
+ var out6 = (s) => void process.stdout.write(s + "\n");
1886
+ var err6 = (s) => void process.stderr.write("error: " + s + "\n");
1586
1887
  var WS2 = "local";
1587
1888
  async function ledgerInit(dir, opts) {
1588
1889
  const cloud = cloudBase(opts.cloud);
1589
- if (existsSync6(dir) && readdirSync3(dir).length > 0) {
1590
- err5(`refusing to mint into non-empty directory: ${dir}`);
1890
+ if (existsSync7(dir) && readdirSync3(dir).length > 0) {
1891
+ err6(`refusing to mint into non-empty directory: ${dir}`);
1591
1892
  return 1;
1592
1893
  }
1593
1894
  let deployFile;
1594
1895
  try {
1595
1896
  deployFile = discoverDeployJson(process.cwd(), opts.file);
1596
1897
  } catch (e) {
1597
- err5(e.message);
1898
+ err6(e.message);
1598
1899
  return 1;
1599
1900
  }
1600
- const deployBody = JSON.parse(readFileSync5(deployFile, "utf8"));
1901
+ const deployBody = JSON.parse(readFileSync6(deployFile, "utf8"));
1601
1902
  const domainHash = await sha256hex(deployBody.packageUsda);
1602
1903
  const c = loadCreds();
1603
1904
  const principal = await sessionToken().catch(() => void 0) !== void 0 ? c.session.uid : c.principal;
1604
1905
  if (principal === void 0) {
1605
- err5("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
1906
+ err6("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
1606
1907
  return 1;
1607
1908
  }
1608
- out5(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
1909
+ out6(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
1609
1910
  const { eng } = await holonEngine(cloud, deployBody, principal);
1610
1911
  await mountFresh(eng, WS2);
1611
1912
  author(eng, WS2, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, principal), "");
@@ -1613,21 +1914,21 @@ async function ledgerInit(dir, opts) {
1613
1914
  const verdict = verifyChainLocal(eng, WS2);
1614
1915
  if (!printVerdict(verdict)) return 1;
1615
1916
  const gitTree = eng.preopen.dir.contents.get("ws").contents.get(WS2).contents.get("nomos.git");
1616
- const gitDir = join7(dir, ".git");
1917
+ const gitDir = join8(dir, ".git");
1617
1918
  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(`
1919
+ const cfgPath = join8(gitDir, "config");
1920
+ writeFileSync4(cfgPath, readFileSync6(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
1921
+ spawnSync4("git", ["-C", dir, "reset", "--hard", "main"], { stdio: "ignore" });
1922
+ out6(`\u2713 holon written \u2192 ${dir} (a normal git repo; git log IS the audit trail)`);
1923
+ out6(`
1623
1924
  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`);
1925
+ out6(` cd ${dir} && npx githolon git remote <ws> && git push nomos main`);
1625
1926
  return 0;
1626
1927
  }
1627
1928
  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)`);
1929
+ const gitDir = existsSync7(join8(dir, ".git")) ? join8(dir, ".git") : dir;
1930
+ if (!existsSync7(join8(gitDir, "HEAD"))) {
1931
+ err6(`${dir} is not a git repo (no HEAD)`);
1631
1932
  return 1;
1632
1933
  }
1633
1934
  const { eng } = await holonEngine(cloudBase(opts.cloud), void 0, "verify");
@@ -1638,11 +1939,15 @@ async function ledgerVerify(dir, opts) {
1638
1939
  }
1639
1940
 
1640
1941
  // src/replay.ts
1641
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
1642
- import { join as join8 } from "node:path";
1942
+ init_engine();
1943
+ init_engine();
1944
+ init_cloud();
1945
+ init_local_holon();
1946
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
1947
+ import { join as join9 } from "node:path";
1643
1948
  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");
1949
+ var out7 = (s) => void process.stdout.write(s + "\n");
1950
+ var err7 = (s) => void process.stderr.write("error: " + s + "\n");
1646
1951
  var SOURCE = "source";
1647
1952
  var REPLAY = "replay";
1648
1953
  function summarizePayload(payload, max = 100) {
@@ -1678,9 +1983,9 @@ function capJsonStrings(v, max = 2048) {
1678
1983
  if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
1679
1984
  if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
1680
1985
  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;
1986
+ const out9 = {};
1987
+ for (const [k, x] of Object.entries(v)) out9[k] = capJsonStrings(x, max);
1988
+ return out9;
1684
1989
  }
1685
1990
  return v;
1686
1991
  }
@@ -1727,10 +2032,10 @@ function readKey() {
1727
2032
  async function replay(target, opts) {
1728
2033
  const cloud = cloudBase(opts.cloud);
1729
2034
  const json = opts.json === true;
1730
- const emit = (o) => out6(JSON.stringify(o));
2035
+ const emit = (o) => out7(JSON.stringify(o));
1731
2036
  let overlay;
1732
2037
  try {
1733
- overlay = JSON.parse(readFileSync6(discoverDeployJson(process.cwd(), opts.file), "utf8"));
2038
+ overlay = JSON.parse(readFileSync7(discoverDeployJson(process.cwd(), opts.file), "utf8"));
1734
2039
  } catch {
1735
2040
  overlay = void 0;
1736
2041
  }
@@ -1738,15 +2043,15 @@ async function replay(target, opts) {
1738
2043
  try {
1739
2044
  ({ eng } = await holonEngine(cloud, overlay, "githolon-replay"));
1740
2045
  } catch (e) {
1741
- err6(`cannot boot the local holon: ${String(e.message ?? e)}
2046
+ err7(`cannot boot the local holon: ${String(e.message ?? e)}
1742
2047
  the runtime rides ${cloud}/v1/runtime/* (cached in ~/.holon/runtime after one fetch)`);
1743
2048
  return 1;
1744
2049
  }
1745
- const isDir = existsSync7(target) || target.includes("/") || target.startsWith(".");
2050
+ const isDir = existsSync8(target) || target.includes("/") || target.startsWith(".");
1746
2051
  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`);
2052
+ const gitDir = existsSync8(join9(target, ".git")) ? join9(target, ".git") : target;
2053
+ if (!existsSync8(join9(gitDir, "HEAD"))) {
2054
+ err7(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
1750
2055
  return 1;
1751
2056
  }
1752
2057
  await mountFresh(eng, SOURCE);
@@ -1754,7 +2059,7 @@ async function replay(target, opts) {
1754
2059
  } else {
1755
2060
  const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/v1/workspaces/${target}/git`, headers: {} });
1756
2061
  if (m.restored !== true) {
1757
- err6(
2062
+ err7(
1758
2063
  `workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to replay` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "") + `
1759
2064
  check the name (githolon ws status ${target}), or pass a local holon directory instead`
1760
2065
  );
@@ -1763,7 +2068,7 @@ async function replay(target, opts) {
1763
2068
  }
1764
2069
  const chain = await walkChain(eng, SOURCE);
1765
2070
  if (chain.length === 0) {
1766
- err6(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
2071
+ err7(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
1767
2072
  return 1;
1768
2073
  }
1769
2074
  await mountFresh(eng, REPLAY);
@@ -1772,8 +2077,8 @@ async function replay(target, opts) {
1772
2077
  const tallies = /* @__PURE__ */ new Map();
1773
2078
  let autoRun = !interactive;
1774
2079
  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");
2080
+ out7(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014 every step re-admitted through the wasm gate`);
2081
+ if (interactive) out7(" keys: enter = next intent \xB7 a = run to the end \xB7 q = quit\n");
1777
2082
  }
1778
2083
  for (let i = 0; i < chain.length; i++) {
1779
2084
  const { oid, bytes, doc } = chain[i];
@@ -1787,8 +2092,8 @@ async function replay(target, opts) {
1787
2092
  if (res.ok === false) {
1788
2093
  const msg = `replay REFUSED at intent ${i} (${domain}/${directive}, ${oid.slice(0, 10)}): ${res.error ?? "unknown"}`;
1789
2094
  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)");
2095
+ else err7(msg);
2096
+ 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
2097
  printVerdict(verifyChainLocal(eng, SOURCE));
1793
2098
  return 1;
1794
2099
  }
@@ -1809,31 +2114,31 @@ async function replay(target, opts) {
1809
2114
  tallies: Object.fromEntries(tallies)
1810
2115
  });
1811
2116
  } 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)}`);
2117
+ out7(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)}, ${ms.toFixed(0)} ms)`);
2118
+ out7(` payload ${summarizePayload(doc.payload?.payload)}`);
1814
2119
  for (const ev of doc.events ?? []) {
1815
2120
  const id = ev.aggregate ?? "?";
1816
2121
  const was = before.get(id);
1817
2122
  const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" : was === void 0 ? "~ touched" : "~ updated";
1818
- out6(` ${verb} ${id}`);
2123
+ out7(` ${verb} ${id}`);
1819
2124
  const ops = summarizeOps(ev);
1820
- if (ops.length > 0) out6(` ${ops}`);
2125
+ if (ops.length > 0) out7(` ${ops}`);
1821
2126
  }
1822
2127
  const tallyLine = [...tallies.entries()].map(([t, n]) => `${t}:${n}`).join(" ");
1823
2128
  const showTally = stepEvery > 0 ? (i + 1) % stepEvery === 0 || i === chain.length - 1 : true;
1824
- if (showTally && tallyLine.length > 0) out6(` rows ${tallyLine}`);
2129
+ if (showTally && tallyLine.length > 0) out7(` rows ${tallyLine}`);
1825
2130
  }
1826
2131
  if (interactive && !autoRun && i < chain.length - 1) {
1827
2132
  const key = await readKey();
1828
2133
  if (key === "quit") {
1829
- out6(`
2134
+ out7(`
1830
2135
  stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below covers the WHOLE chain as delivered`);
1831
2136
  break;
1832
2137
  }
1833
2138
  if (key === "all") autoRun = true;
1834
2139
  }
1835
2140
  }
1836
- if (!json) out6("");
2141
+ if (!json) out7("");
1837
2142
  const verdict = verifyChainLocal(eng, SOURCE);
1838
2143
  if (json) {
1839
2144
  emit({ finale: true, verdict });
@@ -1843,9 +2148,11 @@ stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below cover
1843
2148
  }
1844
2149
 
1845
2150
  // 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");
2151
+ init_engine();
2152
+ init_cloud();
2153
+ import { readFileSync as readFileSync8 } from "node:fs";
2154
+ var out8 = (s) => void process.stdout.write(s + "\n");
2155
+ var err8 = (s) => void process.stderr.write("error: " + s + "\n");
1849
2156
  function lawDiffVerdict(localHash, installed, ws) {
1850
2157
  const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
1851
2158
  const match = active.find((d) => d.domainHash === localHash);
@@ -1868,40 +2175,40 @@ async function status(wsArg, opts) {
1868
2175
  try {
1869
2176
  ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
1870
2177
  } catch (e) {
1871
- err7(e.message);
2178
+ err8(e.message);
1872
2179
  return 1;
1873
2180
  }
1874
2181
  let localHash;
1875
2182
  try {
1876
- const body = JSON.parse(readFileSync7(discoverDeployJson(process.cwd(), opts.file), "utf8"));
2183
+ const body = JSON.parse(readFileSync8(discoverDeployJson(process.cwd(), opts.file), "utf8"));
1877
2184
  if (typeof body.packageUsda === "string") localHash = await sha256hex(body.packageUsda);
1878
2185
  } catch {
1879
2186
  localHash = void 0;
1880
2187
  }
1881
2188
  const r = await fetch(`${cloud}/v1/workspaces/${ws}/domains`).catch(() => void 0);
1882
2189
  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)`);
2190
+ err8(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
1884
2191
  return 1;
1885
2192
  }
1886
2193
  const d = await r.json().catch(() => void 0);
1887
2194
  if (!r.ok || d?.ok !== true) {
1888
- err7(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
2195
+ err8(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
1889
2196
  is the workspace born? githolon ws create ${ws}`);
1890
2197
  return 1;
1891
2198
  }
1892
- out7(`workspace ${ws} on ${cloud}`);
2199
+ out8(`workspace ${ws} on ${cloud}`);
1893
2200
  const domains = d.domains ?? [];
1894
- if (domains.length === 0) out7(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
2201
+ if (domains.length === 0) out8(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
1895
2202
  for (const dom of domains) {
1896
- out7(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}`);
2203
+ out8(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}`);
1897
2204
  }
1898
2205
  if (localHash === void 0) {
1899
- out7(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
2206
+ out8(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
1900
2207
  return 0;
1901
2208
  }
1902
- out7(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
2209
+ out8(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
1903
2210
  const verdict = lawDiffVerdict(localHash, domains, ws);
1904
- for (const line of verdict.lines) out7(line);
2211
+ for (const line of verdict.lines) out8(line);
1905
2212
  return verdict.inSync ? 0 : 2;
1906
2213
  }
1907
2214
 
@@ -1943,6 +2250,15 @@ a bare <ws> resolves from nomos.package.mjs's optional \`workspace\` binding):
1943
2250
  githolon secret set <ws> <secret> import a secret you already hold
1944
2251
  githolon secret rotate <ws> rotate (and re-save) the workspace secret
1945
2252
 
2253
+ Capability credentials (facts on the ledger, values host-side \u2014 the value NEVER rides argv;
2254
+ stdin or --from-gcloud only; architecture/capability_credentials.md):
2255
+ githolon capability bind <ws> <capability> bind a credential: value from stdin (or
2256
+ --from-gcloud <name>) \u2192 the workspace's holon;
2257
+ sha256(value) \u2192 the law-state binding fact
2258
+ githolon capability list <ws> the binding FACTS (never the values)
2259
+ githolon capability rotate <ws> <capability> revoke + re-bind at epoch+1 (new value)
2260
+ githolon capability unbind <ws> <capability> revoke the fact; the host forgets the value
2261
+
1946
2262
  Git lane (stock git push as deploy/birth \u2014 see also: push-to-create):
1947
2263
  githolon git setup install the credential helper for the cloud host
1948
2264
  githolon git remote [<ws>] [<remoteName>] wire this repo to a workspace (default name: nomos)
@@ -2021,23 +2337,58 @@ ${USAGE}`);
2021
2337
  if (sub === "rotate" && ws !== void 0) return secretRotate(ws, opts);
2022
2338
  return usageFail("expected: githolon secret <set <ws> <secret> | rotate <ws>>");
2023
2339
  }
2340
+ async function runCapability(argv) {
2341
+ const usageFail = (m) => {
2342
+ process.stderr.write(`error: ${m}
2343
+
2344
+ ${commandHelp("capability")}`);
2345
+ return 1;
2346
+ };
2347
+ const pos = [];
2348
+ const opts = {};
2349
+ const rest = argv.slice(1);
2350
+ for (let i = 0; i < rest.length; i++) {
2351
+ const a = rest[i];
2352
+ if (a === "--cloud" || a === "--from-gcloud" || a === "--scope" || a === "--expires-at") {
2353
+ const v = rest[++i];
2354
+ if (v === void 0) return usageFail(`${a} requires a value`);
2355
+ if (a === "--cloud") opts.cloud = v;
2356
+ else if (a === "--from-gcloud") opts.fromGcloud = v;
2357
+ else if (a === "--scope") opts.scope = v;
2358
+ else opts.expiresAt = v;
2359
+ } else if (a.startsWith("--")) {
2360
+ return usageFail(`unknown flag '${a}'`);
2361
+ } else {
2362
+ pos.push(a);
2363
+ }
2364
+ }
2365
+ const [sub, ws, capability] = pos;
2366
+ if (sub === "bind" && ws !== void 0 && capability !== void 0) return capabilityBind(ws, capability, opts);
2367
+ if (sub === "rotate" && ws !== void 0 && capability !== void 0) return capabilityRotate(ws, capability, opts);
2368
+ if (sub === "unbind" && ws !== void 0 && capability !== void 0) return capabilityUnbind(ws, capability, opts);
2369
+ if (sub === "list" && ws !== void 0) return capabilityList(ws, opts);
2370
+ return usageFail("expected: githolon capability <bind|rotate|unbind> <ws> <capability> | list <ws>");
2371
+ }
2024
2372
  function isKind(s) {
2025
2373
  return s !== void 0 && KINDS.includes(s);
2026
2374
  }
2027
- function runProof(args) {
2375
+ async function runProof(args) {
2028
2376
  const refuse = (m) => {
2029
2377
  process.stderr.write(`error: ${m}
2030
2378
  `);
2031
2379
  return 1;
2032
2380
  };
2381
+ const live = args.includes("--live");
2382
+ const keep = args.includes("--keep");
2383
+ if (keep && !live) return refuse("--keep only applies to --live (the offline proof creates no cloud workspace to keep)");
2033
2384
  const explicit = args.find((a) => !a.startsWith("--"));
2034
2385
  let proofPath;
2035
2386
  if (explicit !== void 0) {
2036
2387
  proofPath = resolve2(process.cwd(), explicit);
2037
- if (!existsSync8(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
2388
+ if (!existsSync9(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
2038
2389
  } else {
2039
- const buildDir = join9(process.cwd(), "build");
2040
- if (!existsSync8(buildDir)) {
2390
+ const buildDir = join10(process.cwd(), "build");
2391
+ if (!existsSync9(buildDir)) {
2041
2392
  return refuse("no build/ directory here \u2014 run `githolon compile` first (every compile generates build/<name>.proof.mts from your law)");
2042
2393
  }
2043
2394
  const proofs = readdirSync4(buildDir).filter((f) => f.endsWith(".proof.mts"));
@@ -2047,21 +2398,30 @@ function runProof(args) {
2047
2398
  if (proofs.length > 1) {
2048
2399
  return refuse(`found ${proofs.length} proofs (${proofs.join(", ")}) \u2014 name one: githolon proof build/<name>.proof.mts`);
2049
2400
  }
2050
- proofPath = join9(buildDir, proofs[0]);
2401
+ proofPath = join10(buildDir, proofs[0]);
2402
+ }
2403
+ if (!live) {
2404
+ try {
2405
+ const { runOfflineProofStandalone: runOfflineProofStandalone2 } = await Promise.resolve().then(() => (init_proof_offline(), proof_offline_exports));
2406
+ const { cloudBase: cloudBase2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
2407
+ return await runOfflineProofStandalone2(proofPath, cloudBase2());
2408
+ } catch (e) {
2409
+ return refuse(`${e.message ?? e}`);
2410
+ }
2051
2411
  }
2052
2412
  const resolvePkgDir = (name, fromDir) => {
2053
2413
  try {
2054
- return dirname4(createRequire2(pathToFileURL4(join9(fromDir, "noop.js"))).resolve(`${name}/package.json`));
2414
+ return dirname5(createRequire2(pathToFileURL4(join10(fromDir, "noop.js"))).resolve(`${name}/package.json`));
2055
2415
  } catch {
2056
2416
  return void 0;
2057
2417
  }
2058
2418
  };
2059
2419
  const dslDir = (() => {
2060
2420
  try {
2061
- return dirname4(createRequire2(pathToFileURL4(join9(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
2421
+ return dirname5(createRequire2(pathToFileURL4(join10(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
2062
2422
  } catch {
2063
2423
  try {
2064
- return dirname4(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
2424
+ return dirname5(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
2065
2425
  } catch {
2066
2426
  return void 0;
2067
2427
  }
@@ -2071,17 +2431,22 @@ function runProof(args) {
2071
2431
  if (tsxDir === void 0) {
2072
2432
  return refuse("tsx not found \u2014 add @githolon/dsl to your project's dependencies (tsx rides along) and npm install");
2073
2433
  }
2074
- const tsxPkg = JSON.parse(readFileSync8(join9(tsxDir, "package.json"), "utf8"));
2434
+ const tsxPkg = JSON.parse(readFileSync9(join10(tsxDir, "package.json"), "utf8"));
2075
2435
  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() });
2436
+ const tsxCli = join10(tsxDir, tsxBinRel ?? "dist/cli.mjs");
2437
+ console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
2438
+ const r = spawnSync5(process.execPath, [tsxCli, proofPath], {
2439
+ stdio: "inherit",
2440
+ cwd: process.cwd(),
2441
+ env: { ...process.env, ...keep ? { PROOF_KEEP: "1" } : {} }
2442
+ });
2078
2443
  return r.status ?? 1;
2079
2444
  }
2080
2445
  function parseArgs(argv) {
2081
2446
  const [command, ...rest] = argv;
2082
2447
  if (command !== "generate" && command !== "g") {
2083
2448
  throw new Error(
2084
- `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | secret.`
2449
+ `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | secret | capability.`
2085
2450
  );
2086
2451
  }
2087
2452
  let outDir = DEFAULT_OUT;
@@ -2209,6 +2574,9 @@ ${commandHelp("status")}`);
2209
2574
  if (argv[0] === "ws" || argv[0] === "deploy" || argv[0] === "secret") {
2210
2575
  return runCloud(argv);
2211
2576
  }
2577
+ if (argv[0] === "capability") {
2578
+ return runCapability(argv);
2579
+ }
2212
2580
  if (argv[0] === "login") {
2213
2581
  const agent = argv.includes("--agent");
2214
2582
  const tokenAt = argv.indexOf("--token");
@@ -2265,8 +2633,8 @@ ${USAGE}`);
2265
2633
  let parsed;
2266
2634
  try {
2267
2635
  parsed = parseArgs(argv);
2268
- } catch (err8) {
2269
- process.stderr.write(`error: ${err8.message}
2636
+ } catch (err9) {
2637
+ process.stderr.write(`error: ${err9.message}
2270
2638
 
2271
2639
  ${USAGE}`);
2272
2640
  return 1;
@@ -2285,8 +2653,8 @@ ${USAGE}`);
2285
2653
  process.stdout.write(`created ${path}
2286
2654
  `);
2287
2655
  return 0;
2288
- } catch (err8) {
2289
- process.stderr.write(`error: ${err8.message}
2656
+ } catch (err9) {
2657
+ process.stderr.write(`error: ${err9.message}
2290
2658
  `);
2291
2659
  return 1;
2292
2660
  }