githolon 0.32.0 → 0.34.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 +1272 -53
  2. package/package.json +4 -3
package/dist/cli.mjs CHANGED
@@ -1143,7 +1143,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1143
1143
  const rel = p.slice(mount.length + 1).replace(/\/+$/, "");
1144
1144
  return rel === "" ? [] : rel.split("/");
1145
1145
  }
1146
- function resolve3(ps) {
1146
+ function resolve4(ps) {
1147
1147
  let cur = root;
1148
1148
  for (const part of ps) {
1149
1149
  if (!isDir(cur)) return null;
@@ -1153,11 +1153,11 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1153
1153
  }
1154
1154
  return cur;
1155
1155
  }
1156
- const parent = (ps) => resolve3(ps.slice(0, -1));
1156
+ const parent = (ps) => resolve4(ps.slice(0, -1));
1157
1157
  const leaf = (ps) => ps[ps.length - 1];
1158
1158
  const promises = {
1159
1159
  async readFile(p, opts) {
1160
- const n = resolve3(parts(p));
1160
+ const n = resolve4(parts(p));
1161
1161
  if (n === null) throw fsErr("ENOENT", p);
1162
1162
  if (isDir(n)) throw fsErr("EISDIR", p);
1163
1163
  const data = n.data ?? new Uint8Array(0);
@@ -1176,7 +1176,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1176
1176
  if (!isDir(par) || !par.contents.delete(leaf(ps))) throw fsErr("ENOENT", p);
1177
1177
  },
1178
1178
  async readdir(p) {
1179
- const n = resolve3(parts(p));
1179
+ const n = resolve4(parts(p));
1180
1180
  if (n === null) throw fsErr("ENOENT", p);
1181
1181
  if (!isDir(n)) throw fsErr("ENOTDIR", p);
1182
1182
  return [...n.contents.keys()];
@@ -1196,7 +1196,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1196
1196
  return promises.lstat(p);
1197
1197
  },
1198
1198
  async lstat(p) {
1199
- const n = resolve3(parts(p));
1199
+ const n = resolve4(parts(p));
1200
1200
  if (n === null) throw fsErr("ENOENT", p);
1201
1201
  const dir = isDir(n);
1202
1202
  return {
@@ -1245,13 +1245,13 @@ var init_tree = __esm({
1245
1245
  function concatBytes(chunks) {
1246
1246
  let n = 0;
1247
1247
  for (const c of chunks) n += c.length;
1248
- const out11 = new Uint8Array(n);
1248
+ const out12 = new Uint8Array(n);
1249
1249
  let o = 0;
1250
1250
  for (const c of chunks) {
1251
- out11.set(c, o);
1251
+ out12.set(c, o);
1252
1252
  o += c.length;
1253
1253
  }
1254
- return out11;
1254
+ return out12;
1255
1255
  }
1256
1256
  function pkt(payload) {
1257
1257
  const body = typeof payload === "string" ? enc2.encode(payload) : payload;
@@ -1404,13 +1404,13 @@ async function sha256hex(t) {
1404
1404
  function osEntropyBuffer(n) {
1405
1405
  const bytes = new Uint8Array(n * 8);
1406
1406
  for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
1407
- const out11 = new Array(n), T = 2 ** 53;
1407
+ const out12 = new Array(n), T = 2 ** 53;
1408
1408
  for (let i = 0; i < n; i++) {
1409
1409
  let z = 0n;
1410
1410
  for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
1411
- out11[i] = Number(z >> 11n) / T;
1411
+ out12[i] = Number(z >> 11n) / T;
1412
1412
  }
1413
- return out11;
1413
+ return out12;
1414
1414
  }
1415
1415
  function stringifyBig(o) {
1416
1416
  return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
@@ -2112,11 +2112,1198 @@ var init_proof_offline = __esm({
2112
2112
  }
2113
2113
  });
2114
2114
 
2115
+ // src/scene_projector.ts
2116
+ import { createHash as createHash4 } from "node:crypto";
2117
+ function fnv1a32(s) {
2118
+ let h = 2166136261;
2119
+ for (let i = 0; i < s.length; i++) {
2120
+ h ^= s.charCodeAt(i);
2121
+ h = Math.imul(h, 16777619);
2122
+ }
2123
+ return h >>> 0;
2124
+ }
2125
+ function hslToRgb(h, s, l) {
2126
+ const hue = (h % 1 + 1) % 1;
2127
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
2128
+ const p = 2 * l - q;
2129
+ const chan = (t0) => {
2130
+ let t = (t0 % 1 + 1) % 1;
2131
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
2132
+ if (t < 1 / 2) return q;
2133
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
2134
+ return p;
2135
+ };
2136
+ const r32 = (v) => Math.round(v * 1e3) / 1e3;
2137
+ return [r32(chan(hue + 1 / 3)), r32(chan(hue)), r32(chan(hue - 1 / 3))];
2138
+ }
2139
+ function icosphere(subdiv) {
2140
+ const t = (1 + Math.sqrt(5)) / 2;
2141
+ let points = [
2142
+ [-1, t, 0],
2143
+ [1, t, 0],
2144
+ [-1, -t, 0],
2145
+ [1, -t, 0],
2146
+ [0, -1, t],
2147
+ [0, 1, t],
2148
+ [0, -1, -t],
2149
+ [0, 1, -t],
2150
+ [t, 0, -1],
2151
+ [t, 0, 1],
2152
+ [-t, 0, -1],
2153
+ [-t, 0, 1]
2154
+ ].map(norm);
2155
+ let faces = [
2156
+ [0, 11, 5],
2157
+ [0, 5, 1],
2158
+ [0, 1, 7],
2159
+ [0, 7, 10],
2160
+ [0, 10, 11],
2161
+ [1, 5, 9],
2162
+ [5, 11, 4],
2163
+ [11, 10, 2],
2164
+ [10, 7, 6],
2165
+ [7, 1, 8],
2166
+ [3, 9, 4],
2167
+ [3, 4, 2],
2168
+ [3, 2, 6],
2169
+ [3, 6, 8],
2170
+ [3, 8, 9],
2171
+ [4, 9, 5],
2172
+ [2, 4, 11],
2173
+ [6, 2, 10],
2174
+ [8, 6, 7],
2175
+ [9, 8, 1]
2176
+ ];
2177
+ for (let s = 0; s < subdiv; s++) {
2178
+ const midCache = /* @__PURE__ */ new Map();
2179
+ const mid = (a, b) => {
2180
+ const key = a < b ? `${a}_${b}` : `${b}_${a}`;
2181
+ const hit = midCache.get(key);
2182
+ if (hit !== void 0) return hit;
2183
+ const idx = points.length;
2184
+ points = [...points, norm(mul(add(points[a], points[b]), 0.5))];
2185
+ midCache.set(key, idx);
2186
+ return idx;
2187
+ };
2188
+ const next = [];
2189
+ for (const [a, b, c] of faces) {
2190
+ const ab = mid(a, b), bc = mid(b, c), ca = mid(c, a);
2191
+ next.push([a, ab, ca], [b, bc, ab], [c, ca, bc], [ab, bc, ca]);
2192
+ }
2193
+ faces = next;
2194
+ }
2195
+ return { points, indices: faces.flat() };
2196
+ }
2197
+ function boxMesh() {
2198
+ const p = [];
2199
+ for (const z of [-0.5, 0.5]) for (const y of [-0.5, 0.5]) for (const x of [-0.5, 0.5]) p.push([x, y, z]);
2200
+ const quads = [
2201
+ [0, 2, 3, 1],
2202
+ // -z
2203
+ [4, 5, 7, 6],
2204
+ // +z
2205
+ [0, 1, 5, 4],
2206
+ // -y
2207
+ [2, 6, 7, 3],
2208
+ // +y
2209
+ [0, 4, 6, 2],
2210
+ // -x
2211
+ [1, 3, 7, 5]
2212
+ // +x
2213
+ ];
2214
+ const indices = [];
2215
+ for (const [a, b, c, d] of quads) indices.push(a, b, c, a, c, d);
2216
+ return { points: p, indices };
2217
+ }
2218
+ function prism(n) {
2219
+ const points = [];
2220
+ for (const y of [-0.5, 0.5]) {
2221
+ for (let i = 0; i < n; i++) {
2222
+ const a = 2 * Math.PI * i / n;
2223
+ points.push([Math.cos(a), y, Math.sin(a)]);
2224
+ }
2225
+ }
2226
+ const indices = [];
2227
+ for (let i = 0; i < n; i++) {
2228
+ const j = (i + 1) % n;
2229
+ indices.push(i, j, n + j, i, n + j, n + i);
2230
+ }
2231
+ const bottomCenter = points.length;
2232
+ points.push([0, -0.5, 0]);
2233
+ const topCenter = points.length;
2234
+ points.push([0, 0.5, 0]);
2235
+ for (let i = 0; i < n; i++) {
2236
+ const j = (i + 1) % n;
2237
+ indices.push(bottomCenter, j, i);
2238
+ indices.push(topCenter, n + i, n + j);
2239
+ }
2240
+ return { points, indices };
2241
+ }
2242
+ function facetedPolyhedron(sides, elongation = 1) {
2243
+ const n = Math.max(3, Math.floor(sides));
2244
+ const points = [[0, elongation, 0], [0, -elongation, 0]];
2245
+ for (let i = 0; i < n; i++) {
2246
+ const a = 2 * Math.PI * i / n;
2247
+ points.push([Math.cos(a), 0, Math.sin(a)]);
2248
+ }
2249
+ const indices = [];
2250
+ for (let i = 0; i < n; i++) {
2251
+ const a = 2 + i, b = 2 + (i + 1) % n;
2252
+ indices.push(0, b, a);
2253
+ indices.push(1, a, b);
2254
+ }
2255
+ return { points, indices };
2256
+ }
2257
+ function pinLeaf() {
2258
+ const n = 10;
2259
+ const ringY = 1;
2260
+ const ringR = 0.34;
2261
+ const points = [[0, 0, 0]];
2262
+ for (let i = 0; i < n; i++) {
2263
+ const a = 2 * Math.PI * i / n;
2264
+ points.push([Math.cos(a) * ringR, ringY, Math.sin(a) * ringR]);
2265
+ }
2266
+ const ringCenter = points.length;
2267
+ points.push([0, ringY, 0]);
2268
+ const indices = [];
2269
+ for (let i = 0; i < n; i++) {
2270
+ const a = 1 + i, b = 1 + (i + 1) % n;
2271
+ indices.push(0, b, a);
2272
+ indices.push(ringCenter, a, b);
2273
+ }
2274
+ const head = icosphere(1);
2275
+ const headR = 0.42, headY = 1.28;
2276
+ const base = points.length;
2277
+ for (const p of head.points) points.push([p[0] * headR, p[1] * headR + headY, p[2] * headR]);
2278
+ for (const i of head.indices) indices.push(base + i);
2279
+ return { points, indices };
2280
+ }
2281
+ function segmentStrip(a, b, width) {
2282
+ const d = sub(b, a);
2283
+ const dn = norm(d);
2284
+ const ref = Math.abs(dn[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0];
2285
+ const u = norm(cross(dn, ref));
2286
+ const v = norm(cross(dn, u));
2287
+ const hu = mul(u, width / 2);
2288
+ const hv = mul(v, width / 2);
2289
+ const corners = (p) => [
2290
+ add(add(p, hu), hv),
2291
+ add(sub(p, hu), hv),
2292
+ sub(sub(p, hu), hv),
2293
+ sub(add(p, hu), hv)
2294
+ ];
2295
+ const points = [...corners(a), ...corners(b)];
2296
+ const quads = [
2297
+ [0, 1, 2, 3],
2298
+ // a cap
2299
+ [4, 7, 6, 5],
2300
+ // b cap
2301
+ [0, 4, 5, 1],
2302
+ [1, 5, 6, 2],
2303
+ [2, 6, 7, 3],
2304
+ [3, 7, 4, 0]
2305
+ ];
2306
+ const indices = [];
2307
+ for (const [x, y, z, w] of quads) indices.push(x, y, z, x, z, w);
2308
+ return { points, indices };
2309
+ }
2310
+ function sortDeep(v) {
2311
+ if (Array.isArray(v)) return v.map(sortDeep);
2312
+ if (v !== null && typeof v === "object") {
2313
+ return Object.fromEntries(
2314
+ Object.keys(v).sort().map((k) => [k, sortDeep(v[k])])
2315
+ );
2316
+ }
2317
+ return v;
2318
+ }
2319
+ function canonicalStructure(level, def, voIndex) {
2320
+ if (level === "aggregate") {
2321
+ const schema = def ?? {};
2322
+ const kinds = Object.values(schema).map((driver) => JSON.stringify(sortDeep(driver))).sort();
2323
+ return JSON.stringify({ level: "aggregate", fields: kinds });
2324
+ }
2325
+ if (level === "directive") {
2326
+ const d = def ?? {};
2327
+ return JSON.stringify({
2328
+ level: "directive",
2329
+ marker: d.marker ?? "",
2330
+ reads: (d.reads ?? []).length,
2331
+ requires: (d.requires ?? []).length
2332
+ });
2333
+ }
2334
+ const vo = def;
2335
+ if (vo.geometry !== void 0 && vo.fields === void 0) {
2336
+ return JSON.stringify({ level: "valueObject", geometry: vo.geometry });
2337
+ }
2338
+ const fields = Object.values(vo.fields ?? {}).map((f) => {
2339
+ const child = f.ref !== void 0 ? voIndex?.get(f.ref) : void 0;
2340
+ const childHash = child !== void 0 ? sha256Hex(canonicalStructure("valueObject", child, voIndex)) : void 0;
2341
+ return JSON.stringify(
2342
+ sortDeep({
2343
+ type: f.type ?? "",
2344
+ ...f.optional === true ? { optional: true } : {},
2345
+ ...f.array === true ? { array: true } : {},
2346
+ ...f.geometry !== void 0 ? { geometry: f.geometry } : {},
2347
+ ...childHash !== void 0 ? { child: childHash } : {}
2348
+ })
2349
+ );
2350
+ }).sort();
2351
+ return JSON.stringify({ level: "valueObject", fields });
2352
+ }
2353
+ function glyphSignature(structJson, name, partCount) {
2354
+ const h = sha256Hex(structJson);
2355
+ const hueBase = parseInt(h.slice(0, 8), 16) / 4294967296;
2356
+ const tint = fnv1a32(name) % 1e3 / 1e3 * 0.08;
2357
+ const hue = (hueBase + tint) % 1;
2358
+ const facetCount = 4 + parseInt(h.slice(8, 10), 16) % 5;
2359
+ const elongation = Math.round((0.8 + parseInt(h.slice(10, 12), 16) / 255 * 0.5) * 1e3) / 1e3;
2360
+ const size = Math.round((1 + Math.log2(1 + Math.max(0, partCount)) * 0.18) * 1e3) / 1e3;
2361
+ return { structureHash: h, facetCount, hue, hueBase, elongation, size, color: hslToRgb(hue, 0.62, 0.6) };
2362
+ }
2363
+ function packageLayers(packageUsda) {
2364
+ const m = /nomos:usdJsonHex = "([0-9a-f]+)"/.exec(packageUsda);
2365
+ if (!m) throw new Error("a chain-installed package carries no nomos:usdJsonHex \u2014 not an openusd-ir package");
2366
+ return JSON.parse(Buffer.from(m[1], "hex").toString("utf8")).layers;
2367
+ }
2368
+ function lawInstallsFromChain(entries) {
2369
+ const installs = [];
2370
+ for (let t = 0; t < entries.length; t++) {
2371
+ const p = JSON.parse(entries[t].intent).payload;
2372
+ if (p?.directiveId !== "installDomain" || typeof p.payload?.packageUsda !== "string") continue;
2373
+ installs.push({
2374
+ frame: t,
2375
+ domainHash: typeof p.payload.domainHash === "string" ? p.payload.domainHash : "",
2376
+ layers: packageLayers(p.payload.packageUsda)
2377
+ });
2378
+ }
2379
+ return installs;
2380
+ }
2381
+ function intentKind(directiveId) {
2382
+ if (directiveId === "installDomain") return "install";
2383
+ if (/order/i.test(directiveId)) return "order";
2384
+ if (/receipt/i.test(directiveId)) return "receipt";
2385
+ return "offer";
2386
+ }
2387
+ function nodePosition(domain, kind, name) {
2388
+ const angle = seededAngle(`domain:${domain}`) - Math.PI / 2;
2389
+ const boughRadius = 2;
2390
+ const center = [Math.cos(angle) * boughRadius, 0.35 + Math.sin(angle) * boughRadius * 0.55, 2.2];
2391
+ const outward0 = [center[0], center[1] - 0.35, 0];
2392
+ const outward = norm(outward0[0] * outward0[0] + outward0[1] * outward0[1] < 0.01 ? [1, 0.2, 0] : outward0);
2393
+ const lateral = [-outward[1], outward[0], 0];
2394
+ const place = (id2, forward, radius, lift) => {
2395
+ const spreadAngle = seededAngle(id2, Math.PI * 1.5) - Math.PI * 0.75;
2396
+ const off = add(
2397
+ add(mul(outward, radius * (0.55 + 0.45 * Math.abs(Math.cos(spreadAngle)))), mul(lateral, Math.sin(spreadAngle) * radius)),
2398
+ [0, lift, forward]
2399
+ );
2400
+ return round3(add(center, off));
2401
+ };
2402
+ const id = `${kind}:${domain}:${name}`;
2403
+ if (kind === "aggregate") return place(id, 1.15, 0.95, 0.15);
2404
+ if (kind === "directive") return place(id, 2.3, 1.35, 0.35);
2405
+ if (kind === "relationType") return place(id, 0.7, 1.15, -0.75);
2406
+ if (kind === "geoField") return place(id, 1.6, 1.05, -0.35);
2407
+ return place(id, 0.7, 0.8, -1.15);
2408
+ }
2409
+ function buildSceneGraph(installs) {
2410
+ const nodes = [];
2411
+ const edges = [];
2412
+ const layersByPath = /* @__PURE__ */ new Map();
2413
+ for (const inst of installs) {
2414
+ for (const layer of inst.layers) {
2415
+ const list = layersByPath.get(layer.path) ?? [];
2416
+ list.push({ frame: inst.frame, layer });
2417
+ layersByPath.set(layer.path, list);
2418
+ }
2419
+ }
2420
+ for (const path of [...layersByPath.keys()].sort()) {
2421
+ const gens = layersByPath.get(path);
2422
+ const domain = path.split("/").pop() ?? path;
2423
+ for (let g = 0; g < gens.length; g++) {
2424
+ const { frame, layer } = gens[g];
2425
+ const wither = g + 1 < gens.length ? gens[g + 1].frame : void 0;
2426
+ if (wither === frame) continue;
2427
+ const suffix = `f${frame}`;
2428
+ const voIndex = new Map(
2429
+ (layer.nomosValueObjects?.valueObjects ?? []).map((vo) => [vo.name, vo])
2430
+ );
2431
+ const nodeIds = /* @__PURE__ */ new Map();
2432
+ const pushNode = (kind, name, glyph, scale) => {
2433
+ const id = `${kind}:${domain}:${name}`;
2434
+ nodeIds.set(`${kind}:${name}`, id);
2435
+ nodes.push({
2436
+ id,
2437
+ primName: `${kind}_${sanitize(domain)}_${sanitize(name)}_${suffix}`,
2438
+ domain,
2439
+ name,
2440
+ kind,
2441
+ glyph,
2442
+ pos: nodePosition(domain, kind, name),
2443
+ scale,
2444
+ born: frame,
2445
+ wither
2446
+ });
2447
+ };
2448
+ const aggs = layer.prims.filter((p) => p.kind === "aggregate").sort((a, b) => (a.type ?? "") < (b.type ?? "") ? -1 : 1);
2449
+ for (const p of aggs) {
2450
+ const name = bareTypeLabel(p.type ?? p.path.split("/").pop() ?? "?");
2451
+ const struct = canonicalStructure("aggregate", p.schema ?? {});
2452
+ const glyph = glyphSignature(struct, name, Object.keys(p.schema ?? {}).length);
2453
+ pushNode("aggregate", name, glyph, 0.24 * glyph.size);
2454
+ }
2455
+ const dirs = layer.prims.filter((p) => p.kind === "directive").sort((a, b) => a.path < b.path ? -1 : 1);
2456
+ for (const p of dirs) {
2457
+ const name = p.path.split("/").pop() ?? "?";
2458
+ const struct = canonicalStructure("directive", p);
2459
+ const glyph = glyphSignature(struct, name, (p.requires ?? []).length + (p.reads ?? []).length + 1);
2460
+ pushNode("directive", name, glyph, 0.16 * glyph.size);
2461
+ }
2462
+ for (const rt of Object.keys(layer.nomosRelations?.types ?? {}).sort()) {
2463
+ const struct = JSON.stringify({ level: "relationType" });
2464
+ pushNode("relationType", rt, glyphSignature(struct, rt, 1), 0.1);
2465
+ }
2466
+ for (const vo of (layer.nomosValueObjects?.valueObjects ?? []).slice().sort((a, b) => a.name < b.name ? -1 : 1)) {
2467
+ const struct = canonicalStructure("valueObject", vo, voIndex);
2468
+ const glyph = glyphSignature(struct, vo.name, Object.keys(vo.fields ?? {}).length || 1);
2469
+ pushNode("valueObject", vo.name, glyph, 0.12 * glyph.size);
2470
+ }
2471
+ for (const u of (layer.nomosValueObjects?.usages ?? []).filter((u2) => voIndex.get(u2.vo)?.geometry !== void 0).slice().sort((a, b) => a.aggregate !== b.aggregate ? a.aggregate < b.aggregate ? -1 : 1 : a.field < b.field ? -1 : 1)) {
2472
+ const struct = JSON.stringify({ level: "geoField", geometry: voIndex.get(u.vo).geometry });
2473
+ const glyph = glyphSignature(struct, `${u.aggregate}.${u.field}`, 1);
2474
+ pushNode("geoField", `${u.aggregate}.${u.field}`, glyph, 0.1);
2475
+ }
2476
+ const aggNames = aggs.map((p) => bareTypeLabel(p.type ?? ""));
2477
+ const pushEdge = (kind, from, to, label) => {
2478
+ const fromId = nodeIds.get(from);
2479
+ const toId = nodeIds.get(to);
2480
+ if (fromId === void 0 || toId === void 0) return;
2481
+ edges.push({ id: `${kind}:${domain}:${label}:${suffix}`, kind, from: fromId, to: toId, label, born: frame, wither });
2482
+ };
2483
+ for (const p of aggs) {
2484
+ const pName = bareTypeLabel(p.type ?? "");
2485
+ const fieldKinds = layer.nomosReadProjection?.aggregateFieldKinds?.[p.type ?? ""] ?? layer.nomosReadProjection?.aggregateFieldKinds?.[pName] ?? p.schema ?? {};
2486
+ for (const f of Object.keys(fieldKinds).sort()) {
2487
+ if (!/Id$/.test(f) || f.length < 3) continue;
2488
+ const stem = f[0].toUpperCase() + f.slice(1, -2);
2489
+ const targets = aggNames.filter((t2) => t2 !== pName && (t2 === stem || t2.endsWith(stem))).sort((x, y) => x.length - y.length || (x < y ? -1 : 1));
2490
+ if (targets.length === 0) continue;
2491
+ pushEdge("nameRef", `aggregate:${pName}`, `aggregate:${targets[0]}`, `${pName}.${f}`);
2492
+ }
2493
+ }
2494
+ for (const p of dirs) {
2495
+ const name = p.path.split("/").pop() ?? "?";
2496
+ if (typeof p.target === "string" && p.target.length > 0) {
2497
+ const target = bareTypeLabel(p.target);
2498
+ pushEdge("targets", `directive:${name}`, `aggregate:${target}`, `${name}\u2192${target}`);
2499
+ }
2500
+ }
2501
+ const gate = layer.nomosRelationGate?.requires ?? {};
2502
+ for (const dirId of Object.keys(gate).sort()) {
2503
+ const req = gate[dirId];
2504
+ const relType = String(req.object ?? "").split(":")[0] ?? "";
2505
+ pushEdge("relationGate", `relationType:${relType}`, `directive:${dirId}`, `${dirId}\xB7${String(req.relation ?? "")}`);
2506
+ }
2507
+ }
2508
+ }
2509
+ const typeIndex = /* @__PURE__ */ new Map();
2510
+ for (const n of nodes) {
2511
+ if (n.kind !== "aggregate") continue;
2512
+ const list = typeIndex.get(n.name) ?? [];
2513
+ if (!list.includes(n.id)) list.push(n.id);
2514
+ typeIndex.set(n.name, list);
2515
+ }
2516
+ for (const [k, v] of typeIndex) typeIndex.set(k, v.sort());
2517
+ return { nodes, edges, typeIndex };
2518
+ }
2519
+ function bareTypeLabel(t) {
2520
+ const i = t.lastIndexOf(".");
2521
+ return i > 0 && i < t.length - 1 ? t.slice(i + 1) : t;
2522
+ }
2523
+ function geoFieldsOfInstalls(installs) {
2524
+ const out12 = /* @__PURE__ */ new Map();
2525
+ const add2 = (type, field) => {
2526
+ const set = out12.get(type) ?? /* @__PURE__ */ new Set();
2527
+ set.add(field);
2528
+ out12.set(type, set);
2529
+ };
2530
+ for (const inst of installs) {
2531
+ for (const layer of inst.layers) {
2532
+ const voIndex = new Map((layer.nomosValueObjects?.valueObjects ?? []).map((vo) => [vo.name, vo]));
2533
+ const qualified = layer.nomosReadProjectionNamespace?.types ?? [];
2534
+ const delim = layer.nomosReadProjectionNamespace?.delimiter ?? ".";
2535
+ for (const u of layer.nomosValueObjects?.usages ?? []) {
2536
+ if (voIndex.get(u.vo)?.geometry !== "Point") continue;
2537
+ add2(u.aggregate, u.field);
2538
+ for (const q of qualified) {
2539
+ if (q.endsWith(`${delim}${u.aggregate}`)) add2(q, u.field);
2540
+ }
2541
+ }
2542
+ }
2543
+ }
2544
+ return out12;
2545
+ }
2546
+ function pointOfOpValue(setOp) {
2547
+ let v = setOp["Json"];
2548
+ if (typeof v === "string") {
2549
+ try {
2550
+ v = JSON.parse(v);
2551
+ } catch {
2552
+ return void 0;
2553
+ }
2554
+ }
2555
+ if (v === void 0 && typeof setOp["Str"] === "string") {
2556
+ try {
2557
+ v = JSON.parse(setOp["Str"]);
2558
+ } catch {
2559
+ return void 0;
2560
+ }
2561
+ }
2562
+ const g = v;
2563
+ if (g === void 0 || g === null || g.type !== "Point" || !Array.isArray(g.coordinates)) return void 0;
2564
+ const [lng, lat] = g.coordinates;
2565
+ if (typeof lng !== "number" || typeof lat !== "number" || !Number.isFinite(lng) || !Number.isFinite(lat)) return void 0;
2566
+ return [lng, lat];
2567
+ }
2568
+ function framesFromChain(entries, upTo, geoFields) {
2569
+ const typeOf2 = /* @__PURE__ */ new Map();
2570
+ const frames = [];
2571
+ const firstPoint = /* @__PURE__ */ new Map();
2572
+ const allBorn = [];
2573
+ for (let t = 0; t < Math.min(entries.length, upTo); t++) {
2574
+ const doc = JSON.parse(entries[t].intent);
2575
+ const touched = [];
2576
+ const born = [];
2577
+ for (const ev of doc.events ?? []) {
2578
+ const aggId = ev.aggregate;
2579
+ if (typeof aggId !== "string") continue;
2580
+ const declaredRaw = (ev.ops ?? []).find((o) => o.field === "__type")?.op?.Set?.Str;
2581
+ const declared = declaredRaw === void 0 ? void 0 : bareTypeLabel(declaredRaw);
2582
+ if (declared !== void 0 && !typeOf2.has(aggId)) {
2583
+ typeOf2.set(aggId, declared);
2584
+ const b = { type: declared, id: aggId };
2585
+ born.push(b);
2586
+ allBorn.push(b);
2587
+ }
2588
+ const type = declared ?? typeOf2.get(aggId);
2589
+ if (type !== void 0) touched.push(type);
2590
+ const fields = type !== void 0 ? geoFields?.get(type) : void 0;
2591
+ if (fields !== void 0 && !firstPoint.has(aggId)) {
2592
+ for (const o of ev.ops ?? []) {
2593
+ if (typeof o.field !== "string" || !fields.has(o.field) || o.op?.Set === void 0) continue;
2594
+ const pt = pointOfOpValue(o.op.Set);
2595
+ if (pt !== void 0) {
2596
+ firstPoint.set(aggId, pt);
2597
+ break;
2598
+ }
2599
+ }
2600
+ }
2601
+ }
2602
+ frames.push({
2603
+ t,
2604
+ intentId: doc.id ?? "",
2605
+ directive: `${doc.payload?.domain ?? "?"}/${doc.payload?.directiveId ?? "?"}`,
2606
+ kind: intentKind(doc.payload?.directiveId ?? ""),
2607
+ touched: [...new Set(touched)].sort(),
2608
+ born
2609
+ });
2610
+ }
2611
+ for (const b of allBorn) {
2612
+ const pt = firstPoint.get(b.id);
2613
+ if (pt !== void 0) b.point = pt;
2614
+ }
2615
+ return frames;
2616
+ }
2617
+ function trunkPosition(k) {
2618
+ return round3([Math.sin(k * 0.45) * 0.38, -0.012 * k, -0.55 - 1.15 * k]);
2619
+ }
2620
+ function materialFor(mats, color, opacity, emissiveK) {
2621
+ const key = `${vec(color)}|${num(opacity)}|${num(emissiveK)}`;
2622
+ const hit = mats.names.get(key);
2623
+ if (hit !== void 0) return hit;
2624
+ const name = `mat_${String(mats.names.size).padStart(3, "0")}`;
2625
+ mats.names.set(key, name);
2626
+ const emissive = [r3(color[0] * emissiveK), r3(color[1] * emissiveK), r3(color[2] * emissiveK)];
2627
+ mats.defs.push(
2628
+ ` def Material "${name}"`,
2629
+ ` {`,
2630
+ ` token outputs:surface.connect = </Materials/${name}/PreviewSurface.outputs:surface>`,
2631
+ ` def Shader "PreviewSurface"`,
2632
+ ` {`,
2633
+ ` uniform token info:id = "UsdPreviewSurface"`,
2634
+ ` color3f inputs:diffuseColor = ${vec(color)}`,
2635
+ ` color3f inputs:emissiveColor = ${vec(emissive)}`,
2636
+ ` float inputs:opacity = ${num(opacity)}`,
2637
+ ` float inputs:roughness = 0.4`,
2638
+ ` token outputs:surface`,
2639
+ ` }`,
2640
+ ` }`
2641
+ );
2642
+ return name;
2643
+ }
2644
+ function meshPrim(o, indent) {
2645
+ const I = indent, I2 = indent + " ", I3 = indent + " ";
2646
+ const pts = o.mesh.points.map((p) => vec(mul(p, o.meshScale)));
2647
+ const counts = new Array(o.mesh.indices.length / 3).fill(3).join(", ");
2648
+ const lines = [
2649
+ `${I}def Xform ${usdaString(o.primName)} (`,
2650
+ `${I2}prepend apiSchemas = ["MaterialBindingAPI"]`,
2651
+ `${I})`,
2652
+ `${I}{`,
2653
+ `${I2}rel material:binding = </Materials/${o.material}>`
2654
+ ];
2655
+ for (const [k, v] of o.attrs ?? []) lines.push(`${I2}custom string ${k} = ${usdaString(v)}`);
2656
+ const ops = [];
2657
+ if (o.translate !== void 0) {
2658
+ const t = o.translate;
2659
+ lines.push(
2660
+ `${I2}matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (${num(t[0])}, ${num(t[1])}, ${num(t[2])}, 1) )`
2661
+ );
2662
+ ops.push('"xformOp:transform"');
2663
+ }
2664
+ if (o.translateSamples !== void 0 && o.translateSamples.length > 0) {
2665
+ lines.push(`${I2}double3 xformOp:translate = ${vec(o.translateSamples[0][1])}`);
2666
+ lines.push(`${I2}double3 xformOp:translate.timeSamples = {`);
2667
+ for (const [t, p] of o.translateSamples) lines.push(`${I3}${t}: ${vec(p)},`);
2668
+ lines.push(`${I2}}`);
2669
+ ops.push('"xformOp:translate"');
2670
+ }
2671
+ if (o.scaleSamples !== void 0 && o.scaleSamples.length > 0) {
2672
+ lines.push(`${I2}float3 xformOp:scale = (1, 1, 1)`);
2673
+ lines.push(`${I2}float3 xformOp:scale.timeSamples = {`);
2674
+ for (const [t, s] of o.scaleSamples) lines.push(`${I3}${t}: (${num(s)}, ${num(s)}, ${num(s)}),`);
2675
+ lines.push(`${I2}}`);
2676
+ ops.push('"xformOp:scale"');
2677
+ }
2678
+ if (ops.length > 0) lines.push(`${I2}uniform token[] xformOpOrder = [${ops.join(", ")}]`);
2679
+ if (o.visibilitySamples !== void 0 && o.visibilitySamples.length > 0) {
2680
+ lines.push(`${I2}token visibility.timeSamples = {`);
2681
+ for (const [t, v] of o.visibilitySamples) lines.push(`${I3}${t}: ${usdaString(v)},`);
2682
+ lines.push(`${I2}}`);
2683
+ }
2684
+ lines.push(
2685
+ `${I2}def Mesh "geo"`,
2686
+ `${I2}{`,
2687
+ `${I3}float3[] extent = ${extentOf(o.mesh.points, o.meshScale)}`,
2688
+ `${I3}int[] faceVertexCounts = [${counts}]`,
2689
+ `${I3}int[] faceVertexIndices = [${o.mesh.indices.join(", ")}]`,
2690
+ `${I3}point3f[] points = [${pts.join(", ")}]`,
2691
+ `${I3}uniform token subdivisionScheme = "none"`,
2692
+ `${I2}}`,
2693
+ `${I}}`
2694
+ );
2695
+ return lines;
2696
+ }
2697
+ function insetOffset(lng, lat) {
2698
+ return round3([lng / 360 * INSET_W, 0.05, -(lat / 180) * INSET_D]);
2699
+ }
2700
+ function emitSceneUsda(o) {
2701
+ const endT = o.frames.length - 1 + (o.refusal !== void 0 ? 1 : 0);
2702
+ const mats = { names: /* @__PURE__ */ new Map(), defs: [] };
2703
+ const body = [];
2704
+ const nodeById = new Map(o.graph.nodes.map((n) => [n.id, n]));
2705
+ const pulses = /* @__PURE__ */ new Map();
2706
+ for (const f of o.frames) {
2707
+ const domainHint = f.directive.split("/")[0] ?? "";
2708
+ for (const type of f.touched) {
2709
+ const candidates = (o.graph.typeIndex.get(type) ?? []).filter((id2) => {
2710
+ const n = nodeById.get(id2);
2711
+ return n.born <= f.t && (n.wither === void 0 || n.wither > f.t);
2712
+ });
2713
+ const id = candidates.find((c) => nodeById.get(c).domain === domainHint) ?? candidates[0];
2714
+ if (id === void 0) continue;
2715
+ const track = pulses.get(id) ?? /* @__PURE__ */ new Map();
2716
+ if (track.size === 0 && f.t > 0) track.set(0, 1);
2717
+ track.set(f.t, 1.45);
2718
+ if (!track.has(f.t + 1) || track.get(f.t + 1) === 1) track.set(f.t + 1, 1);
2719
+ pulses.set(id, track);
2720
+ }
2721
+ }
2722
+ body.push(` def Scope "Law"`, ` {`);
2723
+ const sortedNodes = [...o.graph.nodes].sort((a, b) => a.primName < b.primName ? -1 : 1);
2724
+ for (const n of sortedNodes) {
2725
+ const mesh = n.kind === "aggregate" ? GEO_ICOSPHERE_FINE : n.kind === "directive" ? GEO_HEX : n.kind === "valueObject" ? facetedPolyhedron(n.glyph.facetCount, n.glyph.elongation) : n.kind === "geoField" ? GEO_PIN : GEO_ICOSPHERE;
2726
+ const vis = [];
2727
+ if (n.born > 0) vis.push([0, "invisible"], [n.born, "inherited"]);
2728
+ if (n.wither !== void 0) {
2729
+ if (vis.length === 0) vis.push([0, "inherited"]);
2730
+ vis.push([Math.min(endT, n.wither + 1), "invisible"]);
2731
+ }
2732
+ const scaleSamples = [];
2733
+ const track = pulses.get(n.id);
2734
+ if (track !== void 0) for (const t of [...track.keys()].sort((a, b) => a - b)) scaleSamples.push([t, track.get(t)]);
2735
+ if (n.wither !== void 0) {
2736
+ if (scaleSamples.length === 0 && n.wither > 0) scaleSamples.push([0, 1]);
2737
+ scaleSamples.push([n.wither, 0.05]);
2738
+ }
2739
+ body.push(
2740
+ ...meshPrim(
2741
+ {
2742
+ primName: n.primName,
2743
+ mesh,
2744
+ meshScale: n.scale,
2745
+ translate: n.pos,
2746
+ material: materialFor(mats, n.glyph.color, 1, 0.25),
2747
+ ...vis.length > 0 ? { visibilitySamples: vis } : {},
2748
+ ...scaleSamples.length > 0 ? { scaleSamples } : {},
2749
+ attrs: [
2750
+ ["nomos:law:domain", n.domain],
2751
+ ["nomos:law:kind", n.kind],
2752
+ ["nomos:law:name", n.name],
2753
+ ["nomos:law:structureHash", n.glyph.structureHash]
2754
+ ]
2755
+ },
2756
+ " "
2757
+ )
2758
+ );
2759
+ }
2760
+ body.push(` }`);
2761
+ body.push(` def Scope "Edges"`, ` {`);
2762
+ const sortedEdges = [...o.graph.edges].sort((a, b) => a.id < b.id ? -1 : 1);
2763
+ sortedEdges.forEach((e, i) => {
2764
+ const a = nodeById.get(e.from);
2765
+ const b = nodeById.get(e.to);
2766
+ const vis = [];
2767
+ if (e.born > 0) vis.push([0, "invisible"], [e.born, "inherited"]);
2768
+ if (e.wither !== void 0) {
2769
+ if (vis.length === 0) vis.push([0, "inherited"]);
2770
+ vis.push([e.wither, "invisible"]);
2771
+ }
2772
+ body.push(
2773
+ ...meshPrim(
2774
+ {
2775
+ primName: `e${String(i).padStart(3, "0")}_${sanitize(e.label).slice(0, 40)}`,
2776
+ mesh: segmentStrip(a.pos, b.pos, 0.02),
2777
+ meshScale: 1,
2778
+ material: materialFor(mats, EDGE_COLORS[e.kind], 0.8, 0.1),
2779
+ ...vis.length > 0 ? { visibilitySamples: vis } : {},
2780
+ attrs: [
2781
+ ["nomos:edge:kind", e.kind],
2782
+ ["nomos:edge:label", e.label]
2783
+ ]
2784
+ },
2785
+ " "
2786
+ )
2787
+ );
2788
+ });
2789
+ body.push(` }`);
2790
+ body.push(` def Scope "Chain"`, ` {`);
2791
+ const N = o.frames.length + (o.refusal !== void 0 ? 1 : 0);
2792
+ for (const f of o.frames) {
2793
+ const stepsBehindHead = N - 1 - f.t;
2794
+ body.push(
2795
+ ...meshPrim(
2796
+ {
2797
+ primName: `link_${String(f.t).padStart(4, "0")}`,
2798
+ mesh: GEO_ICOSPHERE,
2799
+ meshScale: 0.12,
2800
+ translate: trunkPosition(stepsBehindHead),
2801
+ material: materialFor(mats, KIND_COLORS[f.kind], 1, 0.5),
2802
+ ...f.t > 0 ? { visibilitySamples: [[0, "invisible"], [f.t, "inherited"]] } : {},
2803
+ attrs: [
2804
+ ["nomos:chain:intent", f.intentId],
2805
+ ["nomos:chain:directive", f.directive],
2806
+ ["nomos:chain:kind", f.kind],
2807
+ ["nomos:chain:verdict", "admitted"]
2808
+ ]
2809
+ },
2810
+ " "
2811
+ )
2812
+ );
2813
+ }
2814
+ if (o.refusal !== void 0) {
2815
+ body.push(
2816
+ ...meshPrim(
2817
+ {
2818
+ primName: `link_refused`,
2819
+ mesh: GEO_ICOSPHERE,
2820
+ meshScale: 0.16,
2821
+ translate: trunkPosition(0),
2822
+ material: materialFor(mats, REFUSED_COLOR, 1, 0.8),
2823
+ visibilitySamples: [[0, "invisible"], [endT, "inherited"]],
2824
+ attrs: [
2825
+ ["nomos:chain:intent", o.refusal.intentId],
2826
+ ["nomos:chain:directive", o.refusal.directive],
2827
+ ["nomos:chain:verdict", `refused: ${o.refusal.reason}`]
2828
+ ]
2829
+ },
2830
+ " "
2831
+ )
2832
+ );
2833
+ }
2834
+ if (N > 1) {
2835
+ body.push(
2836
+ ...meshPrim(
2837
+ {
2838
+ primName: "rail",
2839
+ mesh: segmentStrip(add(trunkPosition(0), [0, -0.1, 0]), add(trunkPosition(N - 1), [0, -0.1, 0]), 0.03),
2840
+ meshScale: 1,
2841
+ material: materialFor(mats, [0.267, 0.325, 0.416], 0.7, 0),
2842
+ attrs: [["nomos:chain:role", "rail"]]
2843
+ },
2844
+ " "
2845
+ )
2846
+ );
2847
+ }
2848
+ const cursorSamples = [];
2849
+ for (let t = 0; t <= endT; t++) cursorSamples.push([t, add(trunkPosition(N - 1 - Math.min(t, N - 1)), [0, 0.3, 0])]);
2850
+ body.push(
2851
+ ...meshPrim(
2852
+ {
2853
+ primName: "cursor",
2854
+ mesh: GEO_ICOSPHERE,
2855
+ meshScale: 0.09,
2856
+ material: materialFor(mats, o.refusal !== void 0 ? REFUSED_COLOR : CURSOR_COLOR, 1, 1),
2857
+ translateSamples: cursorSamples,
2858
+ attrs: [["nomos:chain:role", "cursor"]]
2859
+ },
2860
+ " "
2861
+ )
2862
+ );
2863
+ body.push(` }`);
2864
+ body.push(` def Scope "Instances"`, ` {`);
2865
+ const perNodeCount = /* @__PURE__ */ new Map();
2866
+ const insetBorn = /* @__PURE__ */ new Map();
2867
+ const budLines = [];
2868
+ for (const f of o.frames) {
2869
+ const domainHint = f.directive.split("/")[0] ?? "";
2870
+ for (const inst of f.born) {
2871
+ const candidates = (o.graph.typeIndex.get(inst.type) ?? []).filter((id) => {
2872
+ const n = nodeById.get(id);
2873
+ return n.born <= f.t && (n.wither === void 0 || n.wither > f.t);
2874
+ });
2875
+ const anchorId = candidates.find((c) => nodeById.get(c).domain === domainHint) ?? candidates[0];
2876
+ if (anchorId === void 0) continue;
2877
+ const anchor = nodeById.get(anchorId);
2878
+ const k = perNodeCount.get(anchorId) ?? 0;
2879
+ perNodeCount.set(anchorId, k + 1);
2880
+ let translate;
2881
+ if (inst.point !== void 0) {
2882
+ const [lng, lat] = inst.point;
2883
+ translate = round3(add(add(anchor.pos, INSET_DROP), insetOffset(lng, lat)));
2884
+ if (!insetBorn.has(anchorId) || insetBorn.get(anchorId) > f.t) insetBorn.set(anchorId, f.t);
2885
+ } else {
2886
+ const ang = k * 2.399963229728653;
2887
+ const r = 0.45 + 0.06 * Math.floor(k / 9);
2888
+ const jz = 0.25 + (fnv1a32(inst.id) & 255) / 255 * 0.1;
2889
+ translate = round3(add(anchor.pos, [Math.cos(ang) * r, Math.sin(ang) * r * 0.7, jz]));
2890
+ }
2891
+ budLines.push(
2892
+ ...meshPrim(
2893
+ {
2894
+ primName: `inst_${String(f.t).padStart(4, "0")}_${String(k).padStart(2, "0")}_${sanitize(inst.id).slice(0, 32)}`,
2895
+ mesh: GEO_ICOSPHERE,
2896
+ meshScale: 0.05,
2897
+ translate,
2898
+ material: materialFor(mats, INSTANCE_COLOR, 1, 0.6),
2899
+ ...f.t > 0 ? { visibilitySamples: [[0, "invisible"], [f.t, "inherited"]] } : {},
2900
+ attrs: [
2901
+ ["nomos:id", inst.id],
2902
+ ["nomos:type", inst.type],
2903
+ ...inst.point !== void 0 ? [["nomos:geo:point", `${num(inst.point[0])},${num(inst.point[1])}`]] : []
2904
+ ]
2905
+ },
2906
+ " "
2907
+ )
2908
+ );
2909
+ }
2910
+ }
2911
+ for (const anchorId of [...insetBorn.keys()].sort()) {
2912
+ const anchor = nodeById.get(anchorId);
2913
+ const bornAt = insetBorn.get(anchorId);
2914
+ budLines.push(
2915
+ ...meshPrim(
2916
+ {
2917
+ primName: `inset_${anchor.primName}`,
2918
+ mesh: INSET_FRAME,
2919
+ meshScale: 1,
2920
+ translate: round3(add(anchor.pos, INSET_DROP)),
2921
+ material: materialFor(mats, [0.353, 0.42, 0.494], 0.55, 0),
2922
+ ...bornAt > 0 ? { visibilitySamples: [[0, "invisible"], [bornAt, "inherited"]] } : {},
2923
+ attrs: [
2924
+ ["nomos:inset:aggregate", anchor.name],
2925
+ ["nomos:inset:projection", "equirectangular lng/lat"]
2926
+ ]
2927
+ },
2928
+ " "
2929
+ )
2930
+ );
2931
+ }
2932
+ body.push(...budLines, ` }`);
2933
+ const header = [
2934
+ `#usda 1.0`,
2935
+ `(`,
2936
+ ` doc = "Nomos ledger scene (portable profile): the chain-carried law canopy, the validated replay as timeSamples. timeCode is the chain index."`,
2937
+ ` customLayerData = {`,
2938
+ ` string "nomos:format" = "nomos.usd-portable-scene.v1"`,
2939
+ ` string "nomos:scene" = ${usdaString(o.name)}`,
2940
+ ` string "nomos:packages" = ${usdaString(o.packageHashes.join(","))}`,
2941
+ ` string "nomos:verifyChain" = ${usdaString(o.verdictLine)}`,
2942
+ ` string "nomos:timeCode" = "chain index (intent position on main)"`,
2943
+ ` }`,
2944
+ ` defaultPrim = "Scene"`,
2945
+ ` startTimeCode = 0`,
2946
+ ` endTimeCode = ${endT}`,
2947
+ ` timeCodesPerSecond = 1`,
2948
+ ` framesPerSecond = 1`,
2949
+ ` metersPerUnit = 1`,
2950
+ ` upAxis = "Y"`,
2951
+ `)`,
2952
+ ``,
2953
+ `def Scope "Materials"`,
2954
+ `{`,
2955
+ ...mats.defs,
2956
+ `}`,
2957
+ ``,
2958
+ `def Xform "Scene"`,
2959
+ `{`
2960
+ ];
2961
+ return [...header, ...body, `}`, ``].join("\n");
2962
+ }
2963
+ function lintPortableProfile(usda) {
2964
+ const violations = [];
2965
+ const defRe = /^\s*def\s+(\w+)\s+"/gm;
2966
+ let m;
2967
+ const ALLOWED_DEFS = /* @__PURE__ */ new Set(["Xform", "Scope", "Mesh", "Material", "Shader"]);
2968
+ while ((m = defRe.exec(usda)) !== null) {
2969
+ if (!ALLOWED_DEFS.has(m[1])) violations.push(`forbidden prim schema: def ${m[1]}`);
2970
+ }
2971
+ if (/\bsubLayers\b/.test(usda)) violations.push("subLayers in a flattened stage");
2972
+ if (/\bprepend references\b|\bpayload\b/.test(usda)) violations.push("references/payloads in a flattened stage");
2973
+ const tsRe = /([\w:]+)\.timeSamples/g;
2974
+ const ALLOWED_TS = /* @__PURE__ */ new Set(["xformOp:translate", "xformOp:scale", "visibility"]);
2975
+ while ((m = tsRe.exec(usda)) !== null) {
2976
+ const attr = m[1].split(" ").pop();
2977
+ if (!ALLOWED_TS.has(attr)) violations.push(`forbidden timeSamples on ${attr}`);
2978
+ }
2979
+ const opRe = /xformOp:(\w+)/g;
2980
+ const ALLOWED_OPS = /* @__PURE__ */ new Set(["transform", "translate", "scale"]);
2981
+ while ((m = opRe.exec(usda)) !== null) {
2982
+ if (!ALLOWED_OPS.has(m[1])) violations.push(`forbidden xformOp:${m[1]}`);
2983
+ }
2984
+ return [...new Set(violations)];
2985
+ }
2986
+ function crc32(bytes) {
2987
+ let c = 4294967295;
2988
+ for (let i = 0; i < bytes.length; i++) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
2989
+ return (c ^ 4294967295) >>> 0;
2990
+ }
2991
+ function usdzPack(entries) {
2992
+ const chunks = [];
2993
+ const central = [];
2994
+ let offset = 0;
2995
+ const w16 = (v) => [v & 255, v >>> 8 & 255];
2996
+ const w32 = (v) => [v & 255, v >>> 8 & 255, v >>> 16 & 255, v >>> 24 & 255];
2997
+ for (const [name, data] of entries) {
2998
+ const nameBytes = new TextEncoder().encode(name);
2999
+ const crc = crc32(data);
3000
+ const headerLen = 30 + nameBytes.length;
3001
+ const mis = (offset + headerLen) % 64;
3002
+ let extraLen = 0;
3003
+ if (mis !== 0) {
3004
+ extraLen = 64 - mis;
3005
+ if (extraLen < 4) extraLen += 64;
3006
+ }
3007
+ const pad = extraLen === 0 ? 0 : extraLen - 4;
3008
+ const local = new Uint8Array([
3009
+ ...w32(67324752),
3010
+ ...w16(20),
3011
+ // version needed
3012
+ ...w16(0),
3013
+ // flags
3014
+ ...w16(0),
3015
+ // method: stored
3016
+ ...w16(0),
3017
+ // dos time (fixed)
3018
+ ...w16(33),
3019
+ // dos date: 1980-01-01
3020
+ ...w32(crc),
3021
+ ...w32(data.length),
3022
+ ...w32(data.length),
3023
+ ...w16(nameBytes.length),
3024
+ ...w16(extraLen),
3025
+ ...nameBytes,
3026
+ ...extraLen > 0 ? [...w16(6534), ...w16(pad), ...new Array(pad).fill(0)] : []
3027
+ ]);
3028
+ chunks.push(local, data);
3029
+ central.push(
3030
+ new Uint8Array([
3031
+ ...w32(33639248),
3032
+ ...w16(20),
3033
+ ...w16(20),
3034
+ ...w16(0),
3035
+ ...w16(0),
3036
+ ...w16(0),
3037
+ ...w16(33),
3038
+ ...w32(crc),
3039
+ ...w32(data.length),
3040
+ ...w32(data.length),
3041
+ ...w16(nameBytes.length),
3042
+ ...w16(0),
3043
+ // no extra in central
3044
+ ...w16(0),
3045
+ // comment
3046
+ ...w16(0),
3047
+ // disk
3048
+ ...w16(0),
3049
+ // internal attrs
3050
+ ...w32(0),
3051
+ // external attrs
3052
+ ...w32(offset),
3053
+ ...nameBytes
3054
+ ])
3055
+ );
3056
+ offset += local.length + data.length;
3057
+ }
3058
+ const centralStart = offset;
3059
+ let centralLen = 0;
3060
+ for (const c of central) centralLen += c.length;
3061
+ const eocd = new Uint8Array([
3062
+ ...w32(101010256),
3063
+ ...w16(0),
3064
+ ...w16(0),
3065
+ ...w16(entries.length),
3066
+ ...w16(entries.length),
3067
+ ...w32(centralLen),
3068
+ ...w32(centralStart),
3069
+ ...w16(0)
3070
+ ]);
3071
+ const total = offset + centralLen + eocd.length;
3072
+ const outBytes = new Uint8Array(total);
3073
+ let at = 0;
3074
+ for (const c of [...chunks, ...central, eocd]) {
3075
+ outBytes.set(c, at);
3076
+ at += c.length;
3077
+ }
3078
+ return outBytes;
3079
+ }
3080
+ var sha256Hex, add, sub, mul, norm, cross, seededAngle, r3, round3, sanitize, KIND_COLORS, REFUSED_COLOR, EDGE_COLORS, INSTANCE_COLOR, CURSOR_COLOR, num, vec, usdaString, extentOf, GEO_ICOSPHERE, GEO_ICOSPHERE_FINE, GEO_BOX, GEO_HEX, GEO_PIN, INSET_W, INSET_D, INSET_DROP, INSET_FRAME, CRC_TABLE;
3081
+ var init_scene_projector = __esm({
3082
+ "src/scene_projector.ts"() {
3083
+ "use strict";
3084
+ sha256Hex = (s) => createHash4("sha256").update(s).digest("hex");
3085
+ add = (a, b) => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
3086
+ sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
3087
+ mul = (a, k) => [a[0] * k, a[1] * k, a[2] * k];
3088
+ norm = (a) => {
3089
+ const l = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]) || 1;
3090
+ return [a[0] / l, a[1] / l, a[2] / l];
3091
+ };
3092
+ cross = (a, b) => [
3093
+ a[1] * b[2] - a[2] * b[1],
3094
+ a[2] * b[0] - a[0] * b[2],
3095
+ a[0] * b[1] - a[1] * b[0]
3096
+ ];
3097
+ seededAngle = (id, spread = Math.PI * 2) => fnv1a32(id) % 3600 / 3600 * spread;
3098
+ r3 = (v) => {
3099
+ const x = Math.round(v * 1e3) / 1e3;
3100
+ return Object.is(x, -0) ? 0 : x;
3101
+ };
3102
+ round3 = (p) => [r3(p[0]), r3(p[1]), r3(p[2])];
3103
+ sanitize = (s) => s.replace(/[^A-Za-z0-9_]/g, "_");
3104
+ KIND_COLORS = {
3105
+ install: [0.953, 0.714, 0.302],
3106
+ // amber
3107
+ order: [0.431, 0.659, 1],
3108
+ // blue
3109
+ receipt: [0.773, 0.549, 1],
3110
+ // violet
3111
+ offer: [0.604, 0.655, 0.71]
3112
+ // neutral
3113
+ };
3114
+ REFUSED_COLOR = [1, 0.373, 0.337];
3115
+ EDGE_COLORS = {
3116
+ nameRef: [0.45, 0.47, 0.5],
3117
+ relationGate: [0.63, 0.26, 0.96],
3118
+ targets: [1, 0.42, 0.42]
3119
+ };
3120
+ INSTANCE_COLOR = [0.22, 0.85, 0.51];
3121
+ CURSOR_COLOR = [0.24, 0.72, 0.35];
3122
+ num = (n) => {
3123
+ if (!Number.isFinite(n)) throw new Error("non-finite number in a usda literal");
3124
+ const v = Math.round(n * 1e3) / 1e3;
3125
+ return String(Object.is(v, -0) ? 0 : v);
3126
+ };
3127
+ vec = (p) => `(${p.map(num).join(", ")})`;
3128
+ usdaString = (s) => JSON.stringify(s);
3129
+ extentOf = (points, scale) => {
3130
+ let min = [Infinity, Infinity, Infinity];
3131
+ let max = [-Infinity, -Infinity, -Infinity];
3132
+ for (const p of points) {
3133
+ for (let i = 0; i < 3; i++) {
3134
+ if (p[i] * scale < min[i]) min[i] = p[i] * scale;
3135
+ if (p[i] * scale > max[i]) max[i] = p[i] * scale;
3136
+ }
3137
+ }
3138
+ return `[${vec(round3(min))}, ${vec(round3(max))}]`;
3139
+ };
3140
+ GEO_ICOSPHERE = icosphere(1);
3141
+ GEO_ICOSPHERE_FINE = icosphere(2);
3142
+ GEO_BOX = boxMesh();
3143
+ GEO_HEX = prism(6);
3144
+ GEO_PIN = pinLeaf();
3145
+ INSET_W = 1.6;
3146
+ INSET_D = 0.8;
3147
+ INSET_DROP = [0, -0.85, 0];
3148
+ INSET_FRAME = {
3149
+ points: GEO_BOX.points.map((p) => [p[0] * INSET_W, p[1] * 0.02, p[2] * INSET_D]),
3150
+ indices: GEO_BOX.indices
3151
+ };
3152
+ CRC_TABLE = (() => {
3153
+ const t = new Uint32Array(256);
3154
+ for (let n = 0; n < 256; n++) {
3155
+ let c = n;
3156
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
3157
+ t[n] = c >>> 0;
3158
+ }
3159
+ return t;
3160
+ })();
3161
+ }
3162
+ });
3163
+
3164
+ // src/scene.ts
3165
+ var scene_exports = {};
3166
+ __export(scene_exports, {
3167
+ scene: () => scene
3168
+ });
3169
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "node:fs";
3170
+ import { join as join10, resolve as resolve2 } from "node:path";
3171
+ import { createHash as createHash5 } from "node:crypto";
3172
+ import git3 from "isomorphic-git";
3173
+ async function walkChain2(eng, ws) {
3174
+ const gitdir = gitdirOf(ws);
3175
+ const dec4 = new TextDecoder();
3176
+ const entries = [];
3177
+ for (const c of await git3.log({ fs: eng.fs, gitdir, ref: "main" })) {
3178
+ try {
3179
+ const { blob } = await git3.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" });
3180
+ entries.push({ oid: c.oid, intent: dec4.decode(blob) });
3181
+ } catch {
3182
+ }
3183
+ }
3184
+ entries.reverse();
3185
+ return entries;
3186
+ }
3187
+ async function projectOnce(cloud, target, isDir, name) {
3188
+ const { eng } = await holonEngine(cloud, void 0, "githolon-scene");
3189
+ const SOURCE3 = "source";
3190
+ if (isDir) {
3191
+ const gitDir = existsSync9(join10(target, ".git")) ? join10(target, ".git") : target;
3192
+ if (!existsSync9(join10(gitDir, "HEAD"))) throw new Error(`${target} is not a git repo (no HEAD)`);
3193
+ await mountFresh(eng, SOURCE3);
3194
+ eng.preopen.dir.contents.get("ws").contents.get(SOURCE3).contents.set("nomos.git", readTreeFromDisk(gitDir));
3195
+ } else {
3196
+ const m = await mountWorkspace(eng, SOURCE3, { remote: `${cloud}/v2/workspaces/${target}/git`, headers: {} });
3197
+ if (m.restored !== true) {
3198
+ throw new Error(
3199
+ `workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to project` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "")
3200
+ );
3201
+ }
3202
+ }
3203
+ const entries = await walkChain2(eng, SOURCE3);
3204
+ if (entries.length === 0) throw new Error(`the chain on '${target}' carries no intents \u2014 nothing to project`);
3205
+ const verdict = verifyChainLocal(eng, SOURCE3);
3206
+ let verdictLine;
3207
+ let upTo;
3208
+ let refusal;
3209
+ if (verdict["valid"] === true) {
3210
+ verdictLine = `GREEN (${verdict["plansRerun"]} plans re-run)`;
3211
+ upTo = entries.length;
3212
+ } else {
3213
+ const at = Number.isInteger(verdict["atIndex"]) ? verdict["atIndex"] : 0;
3214
+ upTo = Math.max(0, at);
3215
+ const bad = JSON.parse(entries[Math.min(at, entries.length - 1)].intent);
3216
+ refusal = {
3217
+ index: at,
3218
+ reason: `${String(verdict["check"] ?? "replay")}: ${String(verdict["error"] ?? "invalid").slice(0, 200)}`,
3219
+ directive: `${bad.payload?.domain ?? "?"}/${bad.payload?.directiveId ?? "?"}`,
3220
+ intentId: bad.id ?? ""
3221
+ };
3222
+ verdictLine = `REFUSED at index ${at} \u2014 ${refusal.reason}`;
3223
+ }
3224
+ if (upTo === 0 && refusal === void 0) throw new Error(`${name}: nothing to emit`);
3225
+ const consideredEntries = entries.slice(0, Math.max(upTo, 1));
3226
+ const installs = lawInstallsFromChain(consideredEntries);
3227
+ if (installs.length === 0) throw new Error("the chain installs no openusd-ir packages \u2014 nothing to draw");
3228
+ const graph = buildSceneGraph(installs);
3229
+ const frames = framesFromChain(entries, upTo, geoFieldsOfInstalls(installs));
3230
+ const usda = emitSceneUsda({
3231
+ name,
3232
+ graph,
3233
+ frames,
3234
+ verdictLine,
3235
+ packageHashes: installs.map((i) => i.domainHash),
3236
+ refusal
3237
+ });
3238
+ return {
3239
+ usda,
3240
+ stats: {
3241
+ frames: frames.length,
3242
+ nodes: graph.nodes.length,
3243
+ edges: graph.edges.length,
3244
+ endT: frames.length - 1 + (refusal !== void 0 ? 1 : 0),
3245
+ verdictLine
3246
+ }
3247
+ };
3248
+ }
3249
+ async function scene(target, opts) {
3250
+ const cloud = cloudBase(opts.cloud);
3251
+ const isDir = existsSync9(target) || target.includes("/") || target.startsWith(".");
3252
+ const name = isDir ? (resolve2(target).split("/").filter(Boolean).pop() ?? "scene").replace(/\.git$/, "") : target;
3253
+ out11(`scene \u2014 ${name} (${isDir ? resolve2(target) : `${cloud} :: ${target}`})`);
3254
+ let first, second;
3255
+ try {
3256
+ first = await projectOnce(cloud, target, isDir, name);
3257
+ second = await projectOnce(cloud, target, isDir, name);
3258
+ } catch (e) {
3259
+ err11(String(e.message ?? e));
3260
+ return 1;
3261
+ }
3262
+ if (first.usda !== second.usda) {
3263
+ err11(`two replays of the SAME chain emitted DIFFERENT bytes \u2014 the projector is nondeterministic (refusing to write)`);
3264
+ return 1;
3265
+ }
3266
+ const violations = lintPortableProfile(first.usda);
3267
+ if (violations.length > 0) {
3268
+ err11(`the emitted stage violates the portable profile: ${violations.join("; ")}`);
3269
+ return 1;
3270
+ }
3271
+ const outDir = resolve2(opts.out ?? join10("scene", name));
3272
+ mkdirSync4(outDir, { recursive: true });
3273
+ const usdaPath = join10(outDir, `${name}.usda`);
3274
+ const usdzPath = join10(outDir, `${name}.usdz`);
3275
+ const usdaBytes = new TextEncoder().encode(first.usda);
3276
+ writeFileSync6(usdaPath, usdaBytes);
3277
+ writeFileSync6(usdzPath, usdzPack([[`${name}.usda`, usdaBytes]]));
3278
+ const s = first.stats;
3279
+ out11(` verify_chain ${s.verdictLine}`);
3280
+ out11(` law canopy ${s.nodes} node(s), ${s.edges} edge(s) \u2014 from the chain's own installed packages`);
3281
+ out11(` chain tier ${s.frames} frame(s), timeCodes 0..${s.endT} (timeCode = chain index)`);
3282
+ out11(` determinism two fresh replays \u2192 BYTE-IDENTICAL sha256 ${sha256(first.usda).slice(0, 16)}\u2026`);
3283
+ out11(` profile portable (Mesh-only, UsdPreviewSurface, translate/scale/visibility timeSamples)`);
3284
+ out11(` \u2192 ${usdaPath} (${usdaBytes.length} bytes)`);
3285
+ out11(` \u2192 ${usdzPath}`);
3286
+ return s.verdictLine.startsWith("GREEN") ? 0 : 2;
3287
+ }
3288
+ var out11, err11, sha256;
3289
+ var init_scene = __esm({
3290
+ "src/scene.ts"() {
3291
+ "use strict";
3292
+ init_engine();
3293
+ init_cloud();
3294
+ init_local_holon();
3295
+ init_scene_projector();
3296
+ out11 = (s) => void process.stdout.write(s + "\n");
3297
+ err11 = (s) => void process.stderr.write("error: " + s + "\n");
3298
+ sha256 = (s) => createHash5("sha256").update(s).digest("hex");
3299
+ }
3300
+ });
3301
+
2115
3302
  // src/cli.ts
2116
- import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
3303
+ import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
2117
3304
  import { spawnSync as spawnSync4 } from "node:child_process";
2118
3305
  import { createRequire as createRequire2 } from "node:module";
2119
- import { dirname as dirname5, join as join10, resolve as resolve2 } from "node:path";
3306
+ import { dirname as dirname5, join as join11, resolve as resolve3 } from "node:path";
2120
3307
  import { pathToFileURL as pathToFileURL4 } from "node:url";
2121
3308
 
2122
3309
  // src/generate.ts
@@ -2856,6 +4043,16 @@ var HELP = {
2856
4043
  ],
2857
4044
  examples: ["githolon replay my-guestbook", "githolon replay ./my-holon --step 10", "githolon replay my-guestbook --json | jq .directive"]
2858
4045
  },
4046
+ scene: {
4047
+ usage: "githolon scene <ws|dir> [--out <dir>] [--cloud <url>]",
4048
+ what: "The ledger as a PORTABLE USD SCENE \u2014 the thin, canonical chain\u2192USD projector. Clones the chain\n(cloud workspace or local holon directory), GATES it on the wasm verify_chain (full emission only\non GREEN; a refusal emits up to the failing index with the red cursor verdict), and projects the\nCHAIN-CARRIED law (the packages' USD-IR \u2014 never TS source) + the validated replay into ONE\nflattened stage: UsdGeomMesh-only glyphs (icosphere aggregates, hex-prism directives, faceted\nvalue-object molecules, the kind-colored commit-link trunk), UsdPreviewSurface materials, and\ntranslate/scale/visibility timeSamples \u2014 timeCode = chain index. Deterministic: the scene is\nemitted twice through fresh engines and byte-compared. Writes <name>.usda + <name>.usdz (a stored\n64-byte-aligned zip \u2014 loads in three.js, usdview, Blender, QuickLook).\nExit codes: 0 green \xB7 2 emitted-with-refusal \xB7 1 error.",
4049
+ flags: [
4050
+ ["<ws|dir>", "a workspace name on the cloud, or a local ledger clone / holon directory"],
4051
+ ["--out <dir>", "the output directory (default: ./scene/<name>)"],
4052
+ ["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
4053
+ ],
4054
+ examples: ["githolon scene my-guestbook", "githolon scene ./co2-clone --out build/scene", "githolon scene co2-platform"]
4055
+ },
2859
4056
  status: {
2860
4057
  usage: "githolon status [<ws>] [--target <name>] [--file <deploy.json>]",
2861
4058
  what: "IS WHAT I BUILT WHAT IS DEPLOYED? Compares the local law hash (build/*.deploy.json) against\nthe workspace's installed law and names the remedy when they differ.\nExit codes: 0 in sync \xB7 2 drift \xB7 1 error.",
@@ -3131,9 +4328,9 @@ function capJsonStrings(v, max = 2048) {
3131
4328
  if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
3132
4329
  if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
3133
4330
  if (typeof v === "object" && v !== null) {
3134
- const out11 = {};
3135
- for (const [k, x] of Object.entries(v)) out11[k] = capJsonStrings(x, max);
3136
- return out11;
4331
+ const out12 = {};
4332
+ for (const [k, x] of Object.entries(v)) out12[k] = capJsonStrings(x, max);
4333
+ return out12;
3137
4334
  }
3138
4335
  return v;
3139
4336
  }
@@ -3463,6 +4660,9 @@ The inner loop:
3463
4660
  githolon status [<ws>] local law hash vs the DEPLOYED law, remedy named
3464
4661
  githolon replay <ws|dir> the ledger as film: step the chain intent-by-
3465
4662
  intent; finale = the verify_chain verdict
4663
+ githolon scene <ws|dir> [--out <dir>] the ledger as a PORTABLE USD scene: verify_chain-
4664
+ gated chain\u2192USD projection (.usda + .usdz, loads
4665
+ in three.js/usdview/QuickLook; timeCode = index)
3466
4666
  githolon decrypt <ws> --as <p> --secret <b64> clone + decrypt as a principal: unwrap their scope
3467
4667
  --query <id> [--param k=v] keys from the chain, read private fields in cleartext
3468
4668
 
@@ -3539,7 +4739,7 @@ Options:
3539
4739
  function cloudArgs(rest) {
3540
4740
  const pos = [];
3541
4741
  const opts = {};
3542
- const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with"];
4742
+ const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with", "--out"];
3543
4743
  const BOOL_FLAGS = ["--sha256", "--stamp", "--yes", "--clear", "--keep-beside", "--retire-replaced", "--explain-current-interface"];
3544
4744
  for (let i = 0; i < rest.length; i++) {
3545
4745
  const a = rest[i];
@@ -3566,6 +4766,7 @@ function cloudArgs(rest) {
3566
4766
  else if (a === "--save") opts.save = v;
3567
4767
  else if (a === "--format") opts.format = v;
3568
4768
  else if (a === "--with") opts.with = v;
4769
+ else if (a === "--out") opts.out = v;
3569
4770
  else if (a === "--max") {
3570
4771
  const n = Number(v);
3571
4772
  if (!Number.isInteger(n) || n <= 0) return { pos, opts, bad: `--max requires a positive integer` };
@@ -3591,9 +4792,9 @@ ${USAGE}`);
3591
4792
  if (bad !== void 0) return usageFail(bad);
3592
4793
  const boundWs = async (explicit, hint) => resolveWorkspace(explicit, process.cwd(), opts.target, hint);
3593
4794
  if (command === "ws") {
3594
- const [sub2, name] = pos;
3595
- if (sub2 === "create" && name !== void 0) return wsCreate(name, opts);
3596
- if (sub2 === "status") {
4795
+ const [sub3, name] = pos;
4796
+ if (sub3 === "create" && name !== void 0) return wsCreate(name, opts);
4797
+ if (sub3 === "status") {
3597
4798
  try {
3598
4799
  return await wsStatus(await boundWs(name, `githolon ws status <name>`), opts);
3599
4800
  } catch (e) {
@@ -3602,8 +4803,8 @@ ${USAGE}`);
3602
4803
  return 1;
3603
4804
  }
3604
4805
  }
3605
- if (sub2 === "retire" && name !== void 0) return wsRetire(name, opts);
3606
- if (sub2 === "reclaim" && name !== void 0) return wsReclaim(name, opts);
4806
+ if (sub3 === "retire" && name !== void 0) return wsRetire(name, opts);
4807
+ if (sub3 === "reclaim" && name !== void 0) return wsReclaim(name, opts);
3607
4808
  return usageFail("expected: githolon ws <create|status|retire|reclaim> <name>");
3608
4809
  }
3609
4810
  if (command === "deploy") {
@@ -3615,9 +4816,9 @@ ${USAGE}`);
3615
4816
  return 1;
3616
4817
  }
3617
4818
  }
3618
- const [sub, ws, secret] = pos;
3619
- if (sub === "set" && ws !== void 0 && secret !== void 0) return secretSet(ws, secret, opts);
3620
- if (sub === "rotate" && ws !== void 0) return secretRotate(ws, opts);
4819
+ const [sub2, ws, secret] = pos;
4820
+ if (sub2 === "set" && ws !== void 0 && secret !== void 0) return secretSet(ws, secret, opts);
4821
+ if (sub2 === "rotate" && ws !== void 0) return secretRotate(ws, opts);
3621
4822
  return usageFail("expected: githolon secret <set <ws> <secret> | rotate <ws>>");
3622
4823
  }
3623
4824
  function isKind(s) {
@@ -3648,11 +4849,11 @@ async function runProof(args) {
3648
4849
  const explicit = args.find((a, i) => !a.startsWith("--") && i !== domainValueIdx);
3649
4850
  let proofPath;
3650
4851
  if (explicit !== void 0) {
3651
- proofPath = resolve2(process.cwd(), explicit);
3652
- if (!existsSync9(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
4852
+ proofPath = resolve3(process.cwd(), explicit);
4853
+ if (!existsSync10(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
3653
4854
  } else {
3654
- const buildDir = join10(process.cwd(), "build");
3655
- if (!existsSync9(buildDir)) {
4855
+ const buildDir = join11(process.cwd(), "build");
4856
+ if (!existsSync10(buildDir)) {
3656
4857
  return refuse("no build/ directory here \u2014 run `githolon compile` first (every compile generates build/<name>.proof.mts from your law)");
3657
4858
  }
3658
4859
  const proofs = readdirSync4(buildDir).filter((f) => f.endsWith(".proof.mts"));
@@ -3662,7 +4863,7 @@ async function runProof(args) {
3662
4863
  if (proofs.length > 1) {
3663
4864
  return refuse(`found ${proofs.length} proofs (${proofs.join(", ")}) \u2014 name one: githolon proof build/<name>.proof.mts`);
3664
4865
  }
3665
- proofPath = join10(buildDir, proofs[0]);
4866
+ proofPath = join11(buildDir, proofs[0]);
3666
4867
  }
3667
4868
  if (!live) {
3668
4869
  try {
@@ -3675,14 +4876,14 @@ async function runProof(args) {
3675
4876
  }
3676
4877
  const resolvePkgDir = (name, fromDir) => {
3677
4878
  try {
3678
- return dirname5(createRequire2(pathToFileURL4(join10(fromDir, "noop.js"))).resolve(`${name}/package.json`));
4879
+ return dirname5(createRequire2(pathToFileURL4(join11(fromDir, "noop.js"))).resolve(`${name}/package.json`));
3679
4880
  } catch {
3680
4881
  return void 0;
3681
4882
  }
3682
4883
  };
3683
4884
  const dslDir = (() => {
3684
4885
  try {
3685
- return dirname5(createRequire2(pathToFileURL4(join10(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
4886
+ return dirname5(createRequire2(pathToFileURL4(join11(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
3686
4887
  } catch {
3687
4888
  try {
3688
4889
  return dirname5(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
@@ -3695,9 +4896,9 @@ async function runProof(args) {
3695
4896
  if (tsxDir === void 0) {
3696
4897
  return refuse("tsx not found \u2014 add @githolon/dsl to your project's dependencies (tsx rides along) and npm install");
3697
4898
  }
3698
- const tsxPkg = JSON.parse(readFileSync11(join10(tsxDir, "package.json"), "utf8"));
4899
+ const tsxPkg = JSON.parse(readFileSync11(join11(tsxDir, "package.json"), "utf8"));
3699
4900
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
3700
- const tsxCli = join10(tsxDir, tsxBinRel ?? "dist/cli.mjs");
4901
+ const tsxCli = join11(tsxDir, tsxBinRel ?? "dist/cli.mjs");
3701
4902
  console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
3702
4903
  const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
3703
4904
  stdio: "inherit",
@@ -3710,7 +4911,7 @@ function parseArgs(argv) {
3710
4911
  const [command, ...rest] = argv;
3711
4912
  if (command !== "generate" && command !== "g") {
3712
4913
  throw new Error(
3713
- `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | domains | secret.`
4914
+ `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | scene | status | generate | ws | deploy | domains | secret.`
3714
4915
  );
3715
4916
  }
3716
4917
  let outDir = DEFAULT_OUT;
@@ -3825,6 +5026,24 @@ ${commandHelp("replay")}`);
3825
5026
  }
3826
5027
  return replay(target, { ...opts, ...step !== void 0 ? { step } : {}, ...json ? { json: true } : {} });
3827
5028
  }
5029
+ if (argv[0] === "scene") {
5030
+ const { scene: scene2 } = await Promise.resolve().then(() => (init_scene(), scene_exports));
5031
+ const { pos, opts, bad } = cloudArgs(argv.slice(1));
5032
+ if (bad !== void 0) {
5033
+ process.stderr.write(`error: ${bad}
5034
+
5035
+ ${commandHelp("scene")}`);
5036
+ return 1;
5037
+ }
5038
+ const target = pos[0];
5039
+ if (target === void 0) {
5040
+ process.stderr.write(`error: expected: githolon scene <ws|dir>
5041
+
5042
+ ${commandHelp("scene")}`);
5043
+ return 1;
5044
+ }
5045
+ return scene2(target, opts);
5046
+ }
3828
5047
  if (argv[0] === "decrypt") {
3829
5048
  const rest = argv.slice(1);
3830
5049
  const pull = (flag) => {
@@ -3885,10 +5104,10 @@ ${commandHelp("status")}`);
3885
5104
  ${commandHelp("domains") ?? USAGE}`);
3886
5105
  return 1;
3887
5106
  }
3888
- const [sub, ...tail] = pos;
5107
+ const [sub2, ...tail] = pos;
3889
5108
  const boundWs = async (explicit, hint) => resolveWorkspace(explicit, process.cwd(), opts.target, hint);
3890
5109
  try {
3891
- if (sub === "retire") {
5110
+ if (sub2 === "retire") {
3892
5111
  const [ws, target] = tail;
3893
5112
  if (ws === void 0 || target === void 0) {
3894
5113
  process.stderr.write(`error: expected: githolon domains retire <ws> <domainHashOrKey> [--with <hashOrKey>]
@@ -3898,7 +5117,7 @@ ${commandHelp("domains") ?? USAGE}`);
3898
5117
  }
3899
5118
  return await domainsRetire2(await boundWs(ws, "githolon domains retire <ws> <hashOrKey>"), target, opts);
3900
5119
  }
3901
- if (sub === "status") {
5120
+ if (sub2 === "status") {
3902
5121
  return await domainsStatus2(await boundWs(tail[0], "githolon domains status <ws>"), opts);
3903
5122
  }
3904
5123
  } catch (e) {
@@ -3920,11 +5139,11 @@ ${commandHelp("domains") ?? USAGE}`);
3920
5139
  ${USAGE}`);
3921
5140
  return 1;
3922
5141
  }
3923
- const [sub] = pos;
3924
- if (sub === "enroll") return keyEnroll2(opts);
3925
- if (sub === "show") return keyShow2(opts);
3926
- if (sub === "import") return keyImport2(opts);
3927
- if (sub === "export") return keyExport2(opts);
5142
+ const [sub2] = pos;
5143
+ if (sub2 === "enroll") return keyEnroll2(opts);
5144
+ if (sub2 === "show") return keyShow2(opts);
5145
+ if (sub2 === "import") return keyImport2(opts);
5146
+ if (sub2 === "export") return keyExport2(opts);
3928
5147
  process.stderr.write(`error: expected: githolon key <enroll|show|import|export> [--principal <uid>]
3929
5148
 
3930
5149
  ${USAGE}`);
@@ -4007,9 +5226,9 @@ ${USAGE}`);
4007
5226
  ${USAGE}`);
4008
5227
  return 1;
4009
5228
  }
4010
- const [sub, dir] = pos;
4011
- if (sub === "init" && dir !== void 0) return ledgerInit(dir, opts);
4012
- if (sub === "verify" && dir !== void 0) return ledgerVerify(dir, opts);
5229
+ const [sub2, dir] = pos;
5230
+ if (sub2 === "init" && dir !== void 0) return ledgerInit(dir, opts);
5231
+ if (sub2 === "verify" && dir !== void 0) return ledgerVerify(dir, opts);
4013
5232
  process.stderr.write(`error: expected: githolon ledger <init|verify> <dir>
4014
5233
 
4015
5234
  ${USAGE}`);
@@ -4024,9 +5243,9 @@ ${USAGE}`);
4024
5243
  ${USAGE}`);
4025
5244
  return 1;
4026
5245
  }
4027
- const [sub, ws, remoteName] = pos;
4028
- if (sub === "setup") return gitSetup(opts);
4029
- if (sub === "remote") {
5246
+ const [sub2, ws, remoteName] = pos;
5247
+ if (sub2 === "setup") return gitSetup(opts);
5248
+ if (sub2 === "remote") {
4030
5249
  try {
4031
5250
  return await gitRemote(await resolveWorkspace(ws, process.cwd(), opts.target, "githolon git remote <ws>"), remoteName ?? "nomos", opts);
4032
5251
  } catch (e) {
@@ -4043,8 +5262,8 @@ ${USAGE}`);
4043
5262
  let parsed;
4044
5263
  try {
4045
5264
  parsed = parseArgs(argv);
4046
- } catch (err11) {
4047
- process.stderr.write(`error: ${err11.message}
5265
+ } catch (err12) {
5266
+ process.stderr.write(`error: ${err12.message}
4048
5267
 
4049
5268
  ${USAGE}`);
4050
5269
  return 1;
@@ -4063,8 +5282,8 @@ ${USAGE}`);
4063
5282
  process.stdout.write(`created ${path}
4064
5283
  `);
4065
5284
  return 0;
4066
- } catch (err11) {
4067
- process.stderr.write(`error: ${err11.message}
5285
+ } catch (err12) {
5286
+ process.stderr.write(`error: ${err12.message}
4068
5287
  `);
4069
5288
  return 1;
4070
5289
  }