githolon 0.92.0 → 0.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +295 -167
- package/package.json +3 -3
- package/dist/dev-server.mjs +0 -2484
- package/dist/framework/domain_bootstrap.package.usda +0 -13
- package/dist/framework/domain_nomos.package.usda +0 -13
package/dist/dev-server.mjs
DELETED
|
@@ -1,2484 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
-
};
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
// ../adapter/git-fs.mjs
|
|
12
|
-
function fsErr(code, p) {
|
|
13
|
-
const e = new Error(`${code}: ${p}`);
|
|
14
|
-
e.code = code;
|
|
15
|
-
return e;
|
|
16
|
-
}
|
|
17
|
-
function makeGitFs(preopen, mount, { File: File2, Directory: Directory2 }) {
|
|
18
|
-
const root = preopen.dir;
|
|
19
|
-
const isDir = (n) => n && n.contents instanceof Map;
|
|
20
|
-
function parts(p) {
|
|
21
|
-
if (p === mount) return [];
|
|
22
|
-
if (!p.startsWith(mount + "/")) throw fsErr("ENOENT", p);
|
|
23
|
-
const rel = p.slice(mount.length + 1).replace(/\/+$/, "");
|
|
24
|
-
return rel === "" ? [] : rel.split("/");
|
|
25
|
-
}
|
|
26
|
-
function resolve(ps) {
|
|
27
|
-
let cur = root;
|
|
28
|
-
for (const part of ps) {
|
|
29
|
-
if (!isDir(cur)) return null;
|
|
30
|
-
const next = cur.contents.get(part);
|
|
31
|
-
if (next === void 0) return null;
|
|
32
|
-
cur = next;
|
|
33
|
-
}
|
|
34
|
-
return cur;
|
|
35
|
-
}
|
|
36
|
-
const parent = (ps) => resolve(ps.slice(0, -1));
|
|
37
|
-
const leaf = (ps) => ps[ps.length - 1];
|
|
38
|
-
const promises = {
|
|
39
|
-
async readFile(p, opts) {
|
|
40
|
-
const n = resolve(parts(p));
|
|
41
|
-
if (n === null) throw fsErr("ENOENT", p);
|
|
42
|
-
if (isDir(n)) throw fsErr("EISDIR", p);
|
|
43
|
-
const data = n.data ?? new Uint8Array(0);
|
|
44
|
-
const encoding = typeof opts === "string" ? opts : opts && opts.encoding;
|
|
45
|
-
return encoding ? new TextDecoder().decode(data) : new Uint8Array(data);
|
|
46
|
-
},
|
|
47
|
-
async writeFile(p, data) {
|
|
48
|
-
const ps = parts(p);
|
|
49
|
-
const par = parent(ps);
|
|
50
|
-
if (!isDir(par)) throw fsErr("ENOENT", p);
|
|
51
|
-
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
|
|
52
|
-
par.contents.set(leaf(ps), new File2(bytes));
|
|
53
|
-
},
|
|
54
|
-
async unlink(p) {
|
|
55
|
-
const ps = parts(p), par = parent(ps);
|
|
56
|
-
if (!isDir(par) || !par.contents.delete(leaf(ps))) throw fsErr("ENOENT", p);
|
|
57
|
-
},
|
|
58
|
-
async readdir(p) {
|
|
59
|
-
const n = resolve(parts(p));
|
|
60
|
-
if (n === null) throw fsErr("ENOENT", p);
|
|
61
|
-
if (!isDir(n)) throw fsErr("ENOTDIR", p);
|
|
62
|
-
return [...n.contents.keys()];
|
|
63
|
-
},
|
|
64
|
-
async mkdir(p) {
|
|
65
|
-
const ps = parts(p), par = parent(ps);
|
|
66
|
-
if (!isDir(par)) throw fsErr("ENOENT", p);
|
|
67
|
-
if (par.contents.has(leaf(ps))) throw fsErr("EEXIST", p);
|
|
68
|
-
par.contents.set(leaf(ps), new Directory2(/* @__PURE__ */ new Map()));
|
|
69
|
-
},
|
|
70
|
-
async rmdir(p) {
|
|
71
|
-
const ps = parts(p), par = parent(ps);
|
|
72
|
-
if (!isDir(par)) throw fsErr("ENOENT", p);
|
|
73
|
-
par.contents.delete(leaf(ps));
|
|
74
|
-
},
|
|
75
|
-
async stat(p) {
|
|
76
|
-
return promises.lstat(p);
|
|
77
|
-
},
|
|
78
|
-
async lstat(p) {
|
|
79
|
-
const n = resolve(parts(p));
|
|
80
|
-
if (n === null) throw fsErr("ENOENT", p);
|
|
81
|
-
const dir = isDir(n);
|
|
82
|
-
return {
|
|
83
|
-
type: dir ? "dir" : "file",
|
|
84
|
-
mode: dir ? 16384 : 33188,
|
|
85
|
-
size: dir ? 0 : n.data ? n.data.length : 0,
|
|
86
|
-
ino: 0,
|
|
87
|
-
mtimeMs: 0,
|
|
88
|
-
ctimeMs: 0,
|
|
89
|
-
uid: 1,
|
|
90
|
-
gid: 1,
|
|
91
|
-
dev: 1,
|
|
92
|
-
isFile: () => !dir,
|
|
93
|
-
isDirectory: () => dir,
|
|
94
|
-
isSymbolicLink: () => false
|
|
95
|
-
};
|
|
96
|
-
},
|
|
97
|
-
async readlink(p) {
|
|
98
|
-
throw fsErr("EINVAL", p);
|
|
99
|
-
},
|
|
100
|
-
async symlink() {
|
|
101
|
-
throw fsErr("EPERM", "symlink unsupported");
|
|
102
|
-
},
|
|
103
|
-
async chmod() {
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
return { promises };
|
|
107
|
-
}
|
|
108
|
-
var init_git_fs = __esm({
|
|
109
|
-
"../adapter/git-fs.mjs"() {
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// ../adapter/tree.mjs
|
|
114
|
-
function flatten(dir, prefix, out) {
|
|
115
|
-
for (const [name, inode] of dir.contents) {
|
|
116
|
-
const path2 = prefix ? prefix + "/" + name : name;
|
|
117
|
-
if (inode.contents instanceof Map) {
|
|
118
|
-
out.push({ path: path2, dir: true });
|
|
119
|
-
flatten(inode, path2, out);
|
|
120
|
-
} else {
|
|
121
|
-
out.push({ path: path2, dir: false, bytes: inode.data ?? new Uint8Array(0) });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
function serializeTree(workTree) {
|
|
126
|
-
const entries = [];
|
|
127
|
-
flatten(workTree, "", entries);
|
|
128
|
-
const chunks = [];
|
|
129
|
-
const header = new Uint8Array(4);
|
|
130
|
-
new DataView(header.buffer).setUint32(0, entries.length, true);
|
|
131
|
-
chunks.push(header);
|
|
132
|
-
for (const e of entries) {
|
|
133
|
-
const pathBytes = enc.encode(e.path);
|
|
134
|
-
const body = e.dir ? new Uint8Array(0) : e.bytes ?? new Uint8Array(0);
|
|
135
|
-
const meta = new Uint8Array(9);
|
|
136
|
-
const dv = new DataView(meta.buffer);
|
|
137
|
-
dv.setUint8(0, e.dir ? 1 : 0);
|
|
138
|
-
dv.setUint32(1, pathBytes.length, true);
|
|
139
|
-
dv.setUint32(5, body.length, true);
|
|
140
|
-
chunks.push(meta, pathBytes, body);
|
|
141
|
-
}
|
|
142
|
-
let total = 0;
|
|
143
|
-
for (const c of chunks) total += c.length;
|
|
144
|
-
const out = new Uint8Array(total);
|
|
145
|
-
let off = 0;
|
|
146
|
-
for (const c of chunks) {
|
|
147
|
-
out.set(c, off);
|
|
148
|
-
off += c.length;
|
|
149
|
-
}
|
|
150
|
-
return out;
|
|
151
|
-
}
|
|
152
|
-
var enc, dec;
|
|
153
|
-
var init_tree = __esm({
|
|
154
|
-
"../adapter/tree.mjs"() {
|
|
155
|
-
enc = new TextEncoder();
|
|
156
|
-
dec = new TextDecoder();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// ../adapter/custody-transport.mjs
|
|
161
|
-
function concatBytes(chunks) {
|
|
162
|
-
let n = 0;
|
|
163
|
-
for (const c of chunks) n += c.length;
|
|
164
|
-
const out = new Uint8Array(n);
|
|
165
|
-
let o = 0;
|
|
166
|
-
for (const c of chunks) {
|
|
167
|
-
out.set(c, o);
|
|
168
|
-
o += c.length;
|
|
169
|
-
}
|
|
170
|
-
return out;
|
|
171
|
-
}
|
|
172
|
-
function pkt(payload) {
|
|
173
|
-
const body = typeof payload === "string" ? enc2.encode(payload) : payload;
|
|
174
|
-
const len = (body.length + 4).toString(16).padStart(4, "0");
|
|
175
|
-
return concatBytes([enc2.encode(len), body]);
|
|
176
|
-
}
|
|
177
|
-
function readPktLines(buf, off = 0) {
|
|
178
|
-
const lines = [];
|
|
179
|
-
while (off + 4 <= buf.length) {
|
|
180
|
-
const hex = dec2.decode(buf.subarray(off, off + 4));
|
|
181
|
-
if (!/^[0-9a-f]{4}$/.test(hex)) break;
|
|
182
|
-
const len = parseInt(hex, 16);
|
|
183
|
-
if (len === 0) {
|
|
184
|
-
off += 4;
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
if (len < 4 || off + len > buf.length) break;
|
|
188
|
-
lines.push(buf.subarray(off + 4, off + len));
|
|
189
|
-
off += len;
|
|
190
|
-
}
|
|
191
|
-
return { lines, rest: off };
|
|
192
|
-
}
|
|
193
|
-
async function lsRefs(remote, headers = {}, { service = "git-upload-pack", prefix = null } = {}) {
|
|
194
|
-
const r = await fetch(`${remote}/info/refs?service=${service}`, { headers });
|
|
195
|
-
if (!r.ok) {
|
|
196
|
-
if (r.status === 404) return {};
|
|
197
|
-
throw new Error(`info/refs ${r.status}: ${(await r.text()).slice(0, 180)}`);
|
|
198
|
-
}
|
|
199
|
-
const buf = new Uint8Array(await r.arrayBuffer());
|
|
200
|
-
const banner = readPktLines(buf, 0);
|
|
201
|
-
const { lines } = readPktLines(buf, banner.rest);
|
|
202
|
-
const refs = {};
|
|
203
|
-
for (const line of lines) {
|
|
204
|
-
let s = dec2.decode(line).replace(/\n$/, "");
|
|
205
|
-
if (s.startsWith("#")) continue;
|
|
206
|
-
const nul = s.indexOf("\0");
|
|
207
|
-
if (nul >= 0) s = s.slice(0, nul);
|
|
208
|
-
const sp = s.indexOf(" ");
|
|
209
|
-
if (sp !== 40) continue;
|
|
210
|
-
const oid = s.slice(0, 40), name = s.slice(41);
|
|
211
|
-
if (!/^[0-9a-f]{40}$/.test(oid)) continue;
|
|
212
|
-
if (prefix && !name.startsWith(prefix)) continue;
|
|
213
|
-
refs[name] = oid;
|
|
214
|
-
}
|
|
215
|
-
return refs;
|
|
216
|
-
}
|
|
217
|
-
async function fetchPack(remote, headers = {}, { wants = [], haves = [] } = {}) {
|
|
218
|
-
wants = wants.filter((w) => /^[0-9a-f]{40}$/.test(w) && w !== ZERO_OID);
|
|
219
|
-
if (!wants.length) return null;
|
|
220
|
-
const caps = "thin-pack ofs-delta agent=nomos-cloud";
|
|
221
|
-
const parts = [];
|
|
222
|
-
wants.forEach((w, i) => parts.push(pkt(`want ${w}${i === 0 ? " " + caps : ""}
|
|
223
|
-
`)));
|
|
224
|
-
parts.push(FLUSH);
|
|
225
|
-
for (const h of haves) if (/^[0-9a-f]{40}$/.test(h) && h !== ZERO_OID) parts.push(pkt(`have ${h}
|
|
226
|
-
`));
|
|
227
|
-
parts.push(pkt("done\n"));
|
|
228
|
-
const r = await fetch(`${remote}/git-upload-pack`, {
|
|
229
|
-
method: "POST",
|
|
230
|
-
headers: { "content-type": "application/x-git-upload-pack-request", "accept": "application/x-git-upload-pack-result", ...headers },
|
|
231
|
-
body: concatBytes(parts)
|
|
232
|
-
});
|
|
233
|
-
if (!r.ok) throw new Error(`upload-pack ${r.status}: ${(await r.text()).slice(0, 180)}`);
|
|
234
|
-
const buf = new Uint8Array(await r.arrayBuffer());
|
|
235
|
-
const { lines, rest } = readPktLines(buf, 0);
|
|
236
|
-
const packChunks = [];
|
|
237
|
-
let sideband = false;
|
|
238
|
-
for (const line of lines) {
|
|
239
|
-
if (line.length === 0) continue;
|
|
240
|
-
const head = dec2.decode(line.subarray(0, Math.min(line.length, 3)));
|
|
241
|
-
if (head === "NAK" || head === "ACK" || head === "ERR" || head.startsWith("sha")) continue;
|
|
242
|
-
const band = line[0];
|
|
243
|
-
if (band === 1) {
|
|
244
|
-
packChunks.push(line.subarray(1));
|
|
245
|
-
sideband = true;
|
|
246
|
-
} else if (band === 2) {
|
|
247
|
-
} else if (band === 3) {
|
|
248
|
-
throw new Error("upload-pack fatal: " + dec2.decode(line.subarray(1)).slice(0, 180));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
const pack = sideband ? concatBytes(packChunks) : buf.subarray(rest);
|
|
252
|
-
if (pack.length < 12 || dec2.decode(pack.subarray(0, 4)) !== "PACK") {
|
|
253
|
-
throw new Error(`upload-pack: no pack in response (${pack.length}B, sideband=${sideband})`);
|
|
254
|
-
}
|
|
255
|
-
return pack;
|
|
256
|
-
}
|
|
257
|
-
async function pushPack(remote, headers = {}, { updates = [], pack = null } = {}) {
|
|
258
|
-
if (!updates.length) return { ok: true, noop: true };
|
|
259
|
-
const cmds = [];
|
|
260
|
-
updates.forEach((u, i) => {
|
|
261
|
-
const old = u.oldOid || ZERO_OID;
|
|
262
|
-
const neu = u.newOid || ZERO_OID;
|
|
263
|
-
const caps = i === 0 ? "\0report-status" : "";
|
|
264
|
-
cmds.push(pkt(`${old} ${neu} ${u.ref}${caps}
|
|
265
|
-
`));
|
|
266
|
-
});
|
|
267
|
-
cmds.push(FLUSH);
|
|
268
|
-
const body = concatBytes([...cmds, pack && pack.length ? pack : EMPTY_PACK]);
|
|
269
|
-
const r = await fetch(`${remote}/git-receive-pack`, {
|
|
270
|
-
method: "POST",
|
|
271
|
-
headers: { "content-type": "application/x-git-receive-pack-request", ...headers },
|
|
272
|
-
body
|
|
273
|
-
});
|
|
274
|
-
const txt = dec2.decode(new Uint8Array(await r.arrayBuffer()));
|
|
275
|
-
const unpacked = /unpack ok/.test(txt);
|
|
276
|
-
const everyRefOk = updates.every((u) => new RegExp(`ok ${u.ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`).test(txt));
|
|
277
|
-
if (!(r.ok && unpacked && everyRefOk && !/\bng /.test(txt))) {
|
|
278
|
-
throw new Error(`receive-pack ${r.status}: ${txt.replace(/[^\x20-\x7e]/g, " ").trim().slice(0, 200)}`);
|
|
279
|
-
}
|
|
280
|
-
return { ok: true };
|
|
281
|
-
}
|
|
282
|
-
var enc2, dec2, ZERO_OID, EMPTY_PACK, FLUSH;
|
|
283
|
-
var init_custody_transport = __esm({
|
|
284
|
-
"../adapter/custody-transport.mjs"() {
|
|
285
|
-
enc2 = new TextEncoder();
|
|
286
|
-
dec2 = new TextDecoder();
|
|
287
|
-
ZERO_OID = "0".repeat(40);
|
|
288
|
-
EMPTY_PACK = new Uint8Array([
|
|
289
|
-
80,
|
|
290
|
-
65,
|
|
291
|
-
67,
|
|
292
|
-
75,
|
|
293
|
-
0,
|
|
294
|
-
0,
|
|
295
|
-
0,
|
|
296
|
-
2,
|
|
297
|
-
0,
|
|
298
|
-
0,
|
|
299
|
-
0,
|
|
300
|
-
0,
|
|
301
|
-
// "PACK", ver 2, 0 objects
|
|
302
|
-
2,
|
|
303
|
-
157,
|
|
304
|
-
8,
|
|
305
|
-
130,
|
|
306
|
-
59,
|
|
307
|
-
216,
|
|
308
|
-
168,
|
|
309
|
-
234,
|
|
310
|
-
181,
|
|
311
|
-
16,
|
|
312
|
-
173,
|
|
313
|
-
106,
|
|
314
|
-
// sha1(the 12 header bytes)
|
|
315
|
-
199,
|
|
316
|
-
92,
|
|
317
|
-
130,
|
|
318
|
-
60,
|
|
319
|
-
253,
|
|
320
|
-
62,
|
|
321
|
-
211,
|
|
322
|
-
30
|
|
323
|
-
]);
|
|
324
|
-
FLUSH = enc2.encode("0000");
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// ../adapter/engine.mjs
|
|
329
|
-
import { WASI, File, Directory, OpenFile, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
|
|
330
|
-
function b64Bytes(bytes) {
|
|
331
|
-
let s = "";
|
|
332
|
-
for (let i = 0; i < bytes.length; i += 32768) {
|
|
333
|
-
s += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
334
|
-
}
|
|
335
|
-
return btoa(s);
|
|
336
|
-
}
|
|
337
|
-
function b64Json(o) {
|
|
338
|
-
return b64Bytes(enc3.encode(JSON.stringify(o)));
|
|
339
|
-
}
|
|
340
|
-
async function sha256hex(t) {
|
|
341
|
-
const b = await crypto.subtle.digest("SHA-256", enc3.encode(t));
|
|
342
|
-
return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
343
|
-
}
|
|
344
|
-
function osEntropyBuffer(n) {
|
|
345
|
-
const bytes = new Uint8Array(n * 8);
|
|
346
|
-
for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
|
|
347
|
-
const out = new Array(n), T = 2 ** 53;
|
|
348
|
-
for (let i = 0; i < n; i++) {
|
|
349
|
-
let z = 0n;
|
|
350
|
-
for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
|
|
351
|
-
out[i] = Number(z >> 11n) / T;
|
|
352
|
-
}
|
|
353
|
-
return out;
|
|
354
|
-
}
|
|
355
|
-
function stringifyBig(o) {
|
|
356
|
-
return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
|
|
357
|
-
}
|
|
358
|
-
function call(ex, mode, fields, STDERR) {
|
|
359
|
-
const req = enc3.encode(JSON.stringify({ mode, ...fields }));
|
|
360
|
-
const reqPtr = ex.git_holon_alloc(req.length);
|
|
361
|
-
try {
|
|
362
|
-
new Uint8Array(ex.memory.buffer, reqPtr, req.length).set(req);
|
|
363
|
-
let packed;
|
|
364
|
-
try {
|
|
365
|
-
packed = ex.git_holon_call(reqPtr, req.length);
|
|
366
|
-
} catch (e) {
|
|
367
|
-
const tail = STDERR.length ? ` | stderr: ${STDERR.slice(-8).join(" / ")}` : "";
|
|
368
|
-
throw new Error(`${String(e && e.stack || e)}${tail}`);
|
|
369
|
-
}
|
|
370
|
-
const { ptr, len } = unpack(packed);
|
|
371
|
-
try {
|
|
372
|
-
const e = JSON.parse(dec3.decode(new Uint8Array(ex.memory.buffer, ptr, len).slice()));
|
|
373
|
-
if (!e.ok) throw new Error((e.error || "holon error") + (STDERR.length ? ` | ${STDERR.slice(-4).join(" / ")}` : ""));
|
|
374
|
-
return e.result;
|
|
375
|
-
} finally {
|
|
376
|
-
ex.git_holon_dealloc(ptr, len);
|
|
377
|
-
}
|
|
378
|
-
} finally {
|
|
379
|
-
ex.git_holon_dealloc(reqPtr, req.length);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, replica, installedBy = "nomos-cloud" }) {
|
|
383
|
-
const hashes = { bootstrap: await sha256hex(bootstrapPkg), nomos: await sha256hex(nomosPkg) };
|
|
384
|
-
const root = /* @__PURE__ */ new Map();
|
|
385
|
-
root.set("domain.package.usda", new File(enc3.encode(bootstrapPkg)));
|
|
386
|
-
root.set("ws", new Directory(/* @__PURE__ */ new Map()));
|
|
387
|
-
const preopen = new PreopenDirectory("/work", root);
|
|
388
|
-
const STDERR = [];
|
|
389
|
-
const pushStderr = (l) => {
|
|
390
|
-
if (STDERR.length >= 500) STDERR.splice(0, STDERR.length - 499);
|
|
391
|
-
STDERR.push(l);
|
|
392
|
-
};
|
|
393
|
-
const fds = [new OpenFile(new File([])), ConsoleStdout.lineBuffered(() => {
|
|
394
|
-
}), ConsoleStdout.lineBuffered(pushStderr), preopen];
|
|
395
|
-
const wasi = new WASI(["wasm_git_holon", "reactor"], [], fds, { debug: false });
|
|
396
|
-
const inst = await WebAssembly.instantiate(wasmModule, { wasi_snapshot_preview1: wasi.wasiImport });
|
|
397
|
-
const code = wasi.start(inst);
|
|
398
|
-
if (code !== 0) throw new Error(`reactor init exited ${code}; stderr=${STDERR.join("\n")}`);
|
|
399
|
-
for (const n of ["memory", "git_holon_alloc", "git_holon_dealloc", "git_holon_call"]) if (!inst.exports[n]) throw new Error(`missing export ${n}`);
|
|
400
|
-
const eng = {
|
|
401
|
-
ex: inst.exports,
|
|
402
|
-
preopen,
|
|
403
|
-
STDERR,
|
|
404
|
-
seq: 0,
|
|
405
|
-
replica,
|
|
406
|
-
hashes,
|
|
407
|
-
bootstrapPkg,
|
|
408
|
-
nomosPkg,
|
|
409
|
-
installedBy,
|
|
410
|
-
fs: makeGitFs(preopen, "/work", { File, Directory }),
|
|
411
|
-
mounted: /* @__PURE__ */ new Map(),
|
|
412
|
-
// ws -> { restored, remoteMain, restoreError }
|
|
413
|
-
mainIntents: /* @__PURE__ */ new Map()
|
|
414
|
-
// ws -> { head, ids:Set, oids:Set } (admission idempotence)
|
|
415
|
-
};
|
|
416
|
-
return eng;
|
|
417
|
-
}
|
|
418
|
-
function directiveRoutes(eng) {
|
|
419
|
-
return /* @__PURE__ */ new Map();
|
|
420
|
-
}
|
|
421
|
-
function routeTagHexOfMintedId(id) {
|
|
422
|
-
const i = id.indexOf("_");
|
|
423
|
-
if (i <= 0) return void 0;
|
|
424
|
-
const body = id.slice(i + 1).replace(/-/g, "").toLowerCase();
|
|
425
|
-
return /^[0-9a-f]{32}$/.test(body) ? body.slice(0, 12) : void 0;
|
|
426
|
-
}
|
|
427
|
-
async function wrongHomeVerdict(shardRouting, route, payload) {
|
|
428
|
-
const value = payload ? payload[route.key] : void 0;
|
|
429
|
-
if (typeof value !== "string" || !value) return null;
|
|
430
|
-
const rows = await shardRouting.assignments() || [];
|
|
431
|
-
let assignment = null;
|
|
432
|
-
if (route.via === "axis") {
|
|
433
|
-
assignment = rows.find((a) => a.homeKey === value) || null;
|
|
434
|
-
} else {
|
|
435
|
-
const tag = routeTagHexOfMintedId(value);
|
|
436
|
-
if (tag !== void 0) {
|
|
437
|
-
for (const a of rows) {
|
|
438
|
-
if (await routeTagHexOfHomeKey(a.homeKey) === tag) {
|
|
439
|
-
assignment = a;
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (!assignment) {
|
|
446
|
-
return {
|
|
447
|
-
error: `no shard placement for this write's home ('${route.key}': ${value.slice(0, 80)}) \u2014 author the derived placement intent (birth<Type>) on the coordinator '${shardRouting.coordinatorWs}' first, then POST /dead-letters/retry`,
|
|
448
|
-
deadLettered: true
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
const movingTo = typeof assignment.status === "string" && assignment.status.startsWith("moving:") ? assignment.status.slice("moving:".length) : null;
|
|
452
|
-
if (movingTo) {
|
|
453
|
-
return {
|
|
454
|
-
error: `home-moving: home '${value.slice(0, 80)}' is migrating ${assignment.shard} -> ${movingTo} (shard map v${assignment.mapVersion ?? 1}) \u2014 parked; a routed client retries automatically after the handoff seals`,
|
|
455
|
-
homeMoving: { homeKey: value, from: assignment.shard, target: movingTo, workspace: `${shardRouting.coordinatorWs}--${movingTo}` },
|
|
456
|
-
deadLettered: true
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
if (shardRouting.selfLabel === assignment.shard) return null;
|
|
460
|
-
const correct = `${shardRouting.coordinatorWs}--${assignment.shard}`;
|
|
461
|
-
return {
|
|
462
|
-
error: `wrong-home: this write's home is assigned to '${correct}' (shard map v${assignment.mapVersion ?? 1}) \u2014 the client's shard map is stale; a routed client re-routes and retries automatically`,
|
|
463
|
-
wrongHome: { workspace: correct, ...assignment.mapVersion !== void 0 ? { mapVersion: assignment.mapVersion } : {} }
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function tenantTypes(eng) {
|
|
467
|
-
return /* @__PURE__ */ new Set();
|
|
468
|
-
}
|
|
469
|
-
function placementDirectives(eng) {
|
|
470
|
-
return /* @__PURE__ */ new Map();
|
|
471
|
-
}
|
|
472
|
-
function scopedReads(eng) {
|
|
473
|
-
return [];
|
|
474
|
-
}
|
|
475
|
-
function globalAggregates(eng) {
|
|
476
|
-
return /* @__PURE__ */ new Set();
|
|
477
|
-
}
|
|
478
|
-
function parseIntentEvents(blob) {
|
|
479
|
-
let doc;
|
|
480
|
-
try {
|
|
481
|
-
doc = JSON.parse(dec3.decode(blob));
|
|
482
|
-
} catch {
|
|
483
|
-
return [];
|
|
484
|
-
}
|
|
485
|
-
const events = Array.isArray(doc && doc.events) ? doc.events : [];
|
|
486
|
-
return events.map((ev) => {
|
|
487
|
-
const sets = /* @__PURE__ */ new Map();
|
|
488
|
-
for (const op of ev.ops || []) {
|
|
489
|
-
const v = op && op.op && op.op.Set;
|
|
490
|
-
if (v === void 0) continue;
|
|
491
|
-
if (typeof v.Str === "string") sets.set(op.field, v.Str);
|
|
492
|
-
else if (typeof v.Int === "number") sets.set(op.field, v.Int);
|
|
493
|
-
}
|
|
494
|
-
return { aggregate: String(ev.aggregate || ""), sets };
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
function beginSummaryCapture(eng, ws, ctx, blob) {
|
|
498
|
-
const events = parseIntentEvents(blob);
|
|
499
|
-
if (events.length === 0) return null;
|
|
500
|
-
const pre = /* @__PURE__ */ new Map();
|
|
501
|
-
const candidates = /* @__PURE__ */ new Set();
|
|
502
|
-
for (const ev of events) {
|
|
503
|
-
if (!pre.has(ev.aggregate)) {
|
|
504
|
-
const rows = qById(eng, ws, ev.aggregate);
|
|
505
|
-
pre.set(ev.aggregate, rows.length ? { type: rows[0].type, data: rows[0].data || {} } : null);
|
|
506
|
-
}
|
|
507
|
-
const before = pre.get(ev.aggregate);
|
|
508
|
-
const evType = ev.sets.get("__type") ?? (before ? before.type : void 0);
|
|
509
|
-
for (const read of ctx.reads) {
|
|
510
|
-
if (read.of !== evType) continue;
|
|
511
|
-
const oldGroup = before ? groupOf(read, before.data) : void 0;
|
|
512
|
-
if (oldGroup !== void 0) candidates.add(summaryKey(read.id, oldGroup));
|
|
513
|
-
if (read.by === null) candidates.add(summaryKey(read.id, ""));
|
|
514
|
-
else if (ev.sets.has(read.by)) candidates.add(summaryKey(read.id, String(ev.sets.get(read.by))));
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
const preTallies = /* @__PURE__ */ new Map();
|
|
518
|
-
for (const key of candidates) {
|
|
519
|
-
const [readId, group] = splitSummaryKey(key);
|
|
520
|
-
const read = ctx.reads.find((r) => r.id === readId);
|
|
521
|
-
const v = tallyOf(eng, ws, read, group);
|
|
522
|
-
preTallies.set(key, v);
|
|
523
|
-
if (!ctx.prevs.has(key)) ctx.prevs.set(key, { read, group, value: v });
|
|
524
|
-
}
|
|
525
|
-
return { events, pre, candidates, preTallies };
|
|
526
|
-
}
|
|
527
|
-
function finishSummaryCapture(eng, ws, ctx, cap, oid, intentId) {
|
|
528
|
-
const deltas = [];
|
|
529
|
-
for (const key of cap.candidates) {
|
|
530
|
-
const [readId, group] = splitSummaryKey(key);
|
|
531
|
-
const read = ctx.reads.find((r) => r.id === readId);
|
|
532
|
-
const after = tallyOf(eng, ws, read, group);
|
|
533
|
-
if (isExtremumRead(read)) {
|
|
534
|
-
const before = cap.preTallies.has(key) ? cap.preTallies.get(key) : null;
|
|
535
|
-
if (after !== before) {
|
|
536
|
-
deltas.push({ readId, group, delta: after });
|
|
537
|
-
if (!extremumMonotone(read, before, after)) ctx.rederived.add(key);
|
|
538
|
-
}
|
|
539
|
-
} else {
|
|
540
|
-
const before = cap.preTallies.get(key) ?? 0;
|
|
541
|
-
if (after !== before) deltas.push({ readId, group, delta: after - before });
|
|
542
|
-
}
|
|
543
|
-
ctx.values.set(key, { read, group, value: after });
|
|
544
|
-
}
|
|
545
|
-
let rowsDelta = 0;
|
|
546
|
-
for (const [aggregateId, before] of cap.pre) {
|
|
547
|
-
const rows = qById(eng, ws, aggregateId);
|
|
548
|
-
const type = rows.length ? rows[0].type : before ? before.type : void 0;
|
|
549
|
-
if (type === void 0 || !tenantTypes(eng).has(type)) continue;
|
|
550
|
-
const existsAfter = rows.length > 0;
|
|
551
|
-
const flip = !before && existsAfter ? 1 : before && !existsAfter ? -1 : 0;
|
|
552
|
-
if (flip === 0) continue;
|
|
553
|
-
rowsDelta += flip;
|
|
554
|
-
const homeKey = ctx.homeKeySet && ctx.homeKeySet.has(aggregateId) ? aggregateId : ctx.homeTags ? ctx.homeTags.get(routeTagHexOfMintedId(aggregateId) ?? "") : void 0;
|
|
555
|
-
if (homeKey !== void 0) {
|
|
556
|
-
deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: homeKey, delta: flip });
|
|
557
|
-
ctx.rowsByHome.set(homeKey, (ctx.rowsByHome.get(homeKey) ?? 0) + flip);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
if (rowsDelta !== 0) {
|
|
561
|
-
deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: "", delta: rowsDelta });
|
|
562
|
-
ctx.rowsDelta += rowsDelta;
|
|
563
|
-
}
|
|
564
|
-
if (deltas.length > 0) ctx.events.push({ oid, intentId: intentId || "", deltas });
|
|
565
|
-
}
|
|
566
|
-
function summaryOfCapture(ctx, fromOid, toOid) {
|
|
567
|
-
if (ctx.events.length === 0 || !toOid || fromOid === toOid) return null;
|
|
568
|
-
const touched = /* @__PURE__ */ new Set();
|
|
569
|
-
for (const ev of ctx.events) for (const d of ev.deltas) touched.add(summaryKey(d.readId, d.group));
|
|
570
|
-
const subtotals = [];
|
|
571
|
-
for (const [key, cur] of ctx.values) {
|
|
572
|
-
const extremum2 = isExtremumRead(cur.read);
|
|
573
|
-
const prev = ctx.prevs.get(key);
|
|
574
|
-
const prevValue = prev ? prev.value : extremum2 ? null : 0;
|
|
575
|
-
if (cur.value === prevValue && !touched.has(key)) continue;
|
|
576
|
-
subtotals.push({
|
|
577
|
-
readId: cur.read.id,
|
|
578
|
-
group: cur.group,
|
|
579
|
-
kind: cur.read.kind,
|
|
580
|
-
prev: prevValue,
|
|
581
|
-
value: cur.value,
|
|
582
|
-
...extremum2 && ctx.rederived.has(key) ? { rederived: true } : {}
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
if (subtotals.length === 0 && ctx.rowsDelta === 0 && ctx.rowsByHome.size === 0) return null;
|
|
586
|
-
return {
|
|
587
|
-
fromOid: fromOid || "",
|
|
588
|
-
toOid,
|
|
589
|
-
subtotals: subtotals.sort((a, b) => summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1),
|
|
590
|
-
events: ctx.events,
|
|
591
|
-
rowsDelta: ctx.rowsDelta,
|
|
592
|
-
// SLICE 8: the exact per-home row deltas — the adapter closes each against the
|
|
593
|
-
// coordinator-held per-home subtotal exactly like the "" grand total.
|
|
594
|
-
rowsByHome: Object.fromEntries([...ctx.rowsByHome.entries()].sort((a, b) => a[0] < b[0] ? -1 : 1))
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null, shardRouting = null, clientBase = null, defer = false } = {}) {
|
|
598
|
-
const main = await mainIntentIds(eng, ws);
|
|
599
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
600
|
-
const deadLetters = [];
|
|
601
|
-
let admittedTotal = 0;
|
|
602
|
-
const born = [];
|
|
603
|
-
const birthOutcomes = [];
|
|
604
|
-
const timings = { spans: [], intents: 0, sessions: 0 };
|
|
605
|
-
const summaryCtx = shardRouting && shardRouting.selfLabel && scopedReads(eng).length > 0 ? { reads: scopedReads(eng), prevs: /* @__PURE__ */ new Map(), values: /* @__PURE__ */ new Map(), events: [], rowsDelta: 0, rederived: /* @__PURE__ */ new Set(), rowsByHome: /* @__PURE__ */ new Map(), homeTags: /* @__PURE__ */ new Map(), homeKeySet: /* @__PURE__ */ new Set() } : null;
|
|
606
|
-
if (summaryCtx) {
|
|
607
|
-
try {
|
|
608
|
-
for (const r of query(eng, ws, "aggregatesByType", JSON.stringify({ __type: "NomosHomeReceipt" }))) {
|
|
609
|
-
const hk = r.data && typeof r.data.homeKey === "string" ? r.data.homeKey : null;
|
|
610
|
-
if (!hk) continue;
|
|
611
|
-
summaryCtx.homeKeySet.add(hk);
|
|
612
|
-
summaryCtx.homeTags.set(await routeTagHexOfHomeKey(hk), hk);
|
|
613
|
-
}
|
|
614
|
-
} catch (e) {
|
|
615
|
-
log("home-tag-table-error", { ws, error: String(e).slice(0, 200) });
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
const gitdir0 = gitdirOf(ws);
|
|
619
|
-
const headBefore = summaryCtx ? kgit.resolveRef(eng, gitdir0, BRANCH) : null;
|
|
620
|
-
const t0 = Date.now();
|
|
621
|
-
let seq = 0;
|
|
622
|
-
const span = (name, kind, depth = 1, extra = {}) => {
|
|
623
|
-
const s0 = Date.now();
|
|
624
|
-
return (end = {}) => timings.spans.push({ id: `intent-offer-${t0}-${seq++}`, track: "cloud", name, kind, depth, t0: s0, t1: Date.now(), ...extra, ...end });
|
|
625
|
-
};
|
|
626
|
-
for (const offer of Array.isArray(offers) ? offers : []) {
|
|
627
|
-
const cid = typeof offer?.session === "string" && offer.session ? offer.session : "direct";
|
|
628
|
-
const rec = sessions.get(cid) || { session: cid, admitted: [], rejected: [], skipped: [] };
|
|
629
|
-
sessions.set(cid, rec);
|
|
630
|
-
const done = span(`intent offer ${cid.slice(0, 14)}`, "cadmit", 1, { client: cid });
|
|
631
|
-
let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
|
|
632
|
-
try {
|
|
633
|
-
if (!blob && typeof offer?.intentB64 === "string") blob = Uint8Array.from(atob(offer.intentB64), (c) => c.charCodeAt(0));
|
|
634
|
-
const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 : 32 * 1024 * 1024;
|
|
635
|
-
if (!blob || blob.byteLength === 0 || blob.byteLength > maxOfferBytes) {
|
|
636
|
-
rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: `intent offer must carry intentB64 <= ${maxOfferBytes / (1024 * 1024)} MiB` });
|
|
637
|
-
continue;
|
|
638
|
-
}
|
|
639
|
-
let doc = null;
|
|
640
|
-
try {
|
|
641
|
-
doc = JSON.parse(dec3.decode(blob));
|
|
642
|
-
} catch {
|
|
643
|
-
}
|
|
644
|
-
const intentId = typeof doc?.id === "string" ? doc.id : null;
|
|
645
|
-
const dom = typeof doc?.payload?.domain === "string" ? doc.payload.domain : null;
|
|
646
|
-
const dirId = typeof doc?.payload?.directiveId === "string" ? doc.payload.directiveId : null;
|
|
647
|
-
const oid = String(offer?.oid || intentId || "").slice(0, 10);
|
|
648
|
-
if (!dom) {
|
|
649
|
-
rec.rejected.push({ oid, error: "non-authored intent \u2014 governance enters its own admin lane" });
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
|
|
653
|
-
const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes. Author it via POST /v1/workspaces/${ws}/author (owner Bearer), or \u2014 if this parked receipt is sanctioned \u2014 the owner unjams it: POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
654
|
-
rec.rejected.push({ oid, error, deadLettered: true });
|
|
655
|
-
deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
if (intentId && main.ids.has(intentId)) {
|
|
659
|
-
rec.skipped.push({ oid, intent: intentId.slice(0, 12), alreadyOnMain: true });
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
const cap = summaryCtx ? beginSummaryCapture(eng, ws, summaryCtx, blob) : null;
|
|
663
|
-
const _tOffer = Date.now();
|
|
664
|
-
const res = offerAdmit(eng, ws, blob, { defer: !summaryCtx });
|
|
665
|
-
log("offer-admit-ms", { ws, ms: Date.now() - _tOffer, deferred: !summaryCtx, ok: res.ok });
|
|
666
|
-
if (res.ok) {
|
|
667
|
-
if (intentId) main.ids.add(intentId);
|
|
668
|
-
main.oids.add(res.head);
|
|
669
|
-
main.head = res.head;
|
|
670
|
-
rec.admitted.push({ oid, head: res.head });
|
|
671
|
-
admittedTotal += 1;
|
|
672
|
-
if (Array.isArray(res.born) && res.born.length) born.push(...res.born);
|
|
673
|
-
if (Array.isArray(res.birthOutcomes) && res.birthOutcomes.length) birthOutcomes.push(...res.birthOutcomes);
|
|
674
|
-
if (cap) finishSummaryCapture(eng, ws, summaryCtx, cap, res.head, intentId);
|
|
675
|
-
} else {
|
|
676
|
-
const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
677
|
-
rec.rejected.push({ oid, error, deadLettered: true });
|
|
678
|
-
deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
679
|
-
}
|
|
680
|
-
} catch (e) {
|
|
681
|
-
rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: String(e).slice(0, 200) });
|
|
682
|
-
} finally {
|
|
683
|
-
done();
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
timings.sessions = sessions.size;
|
|
687
|
-
timings.intents = admittedTotal;
|
|
688
|
-
if (admittedTotal && !summaryCtx) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
689
|
-
if (deadLetters.length) {
|
|
690
|
-
try {
|
|
691
|
-
await materializeRefused(eng, ws, ledger, deadLetters);
|
|
692
|
-
} catch (e) {
|
|
693
|
-
log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
const deferDurability = defer && !summaryCtx;
|
|
697
|
-
if (admittedTotal && !deferDurability) {
|
|
698
|
-
const sealDone = span("seal offered intents", "cseal", 1, { admitted: admittedTotal });
|
|
699
|
-
await commitAndPush(eng, ws, ledger);
|
|
700
|
-
sealDone();
|
|
701
|
-
}
|
|
702
|
-
const gitdir = gitdirOf(ws);
|
|
703
|
-
const head = kgit.resolveRef(eng, gitdir, BRANCH) ?? main.head;
|
|
704
|
-
const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore, head) : null;
|
|
705
|
-
let resultPackB64 = null;
|
|
706
|
-
if (admittedTotal && head && /^[0-9a-f]{40}$/i.test(clientBase || "") && clientBase !== head) {
|
|
707
|
-
try {
|
|
708
|
-
const p = servePack(eng, ws, clientBase);
|
|
709
|
-
if (p && p.packB64 && p.bytes && p.bytes <= RESULT_PACK_INLINE_MAX) resultPackB64 = p.packB64;
|
|
710
|
-
} catch {
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
log("admit-intent-offers", { ws, sessions: sessions.size, admitted: admittedTotal, rejected: [...sessions.values()].reduce((n, s) => n + s.rejected.length, 0), resultPack: resultPackB64 ? "inline" : "none" });
|
|
714
|
-
const bornPacked = born.length ? packBornChildren(eng, born) : born;
|
|
715
|
-
return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, deadLetters, timings, ...summary ? { summary } : {}, ...bornPacked.length ? { born: bornPacked } : {}, ...birthOutcomes.length ? { birthOutcomes } : {}, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {}, ...deferDurability && admittedTotal && head ? { deferred: true, head } : {} };
|
|
716
|
-
}
|
|
717
|
-
async function authorOn(eng, ws, ledger, domain, directiveId, payload, lawHash, opts = {}) {
|
|
718
|
-
await mountWorkspace(eng, ws, ledger);
|
|
719
|
-
let res;
|
|
720
|
-
try {
|
|
721
|
-
res = author(eng, ws, domain, directiveId, payload, lawHash, opts);
|
|
722
|
-
} catch (e) {
|
|
723
|
-
return { ok: false, error: String(e && e.message || e).slice(0, 500) };
|
|
724
|
-
}
|
|
725
|
-
if (res && res.ok === false) return { ok: false, error: String(res.error || "the law refused this intent").slice(0, 500) };
|
|
726
|
-
await commitAndPush(eng, ws, ledger);
|
|
727
|
-
const bornPacked = Array.isArray(res.born) && res.born.length ? packBornChildren(eng, res.born) : res.born;
|
|
728
|
-
return { ok: true, head: res.head, ...bornPacked ? { born: bornPacked } : {}, ...res.birthOutcomes ? { birthOutcomes: res.birthOutcomes } : {} };
|
|
729
|
-
}
|
|
730
|
-
function stageWorkspaceDir(eng, ws) {
|
|
731
|
-
const wsContents = /* @__PURE__ */ new Map();
|
|
732
|
-
eng.preopen.dir.contents.get("ws").contents.set(ws, new Directory(wsContents));
|
|
733
|
-
}
|
|
734
|
-
async function mountWorkspace(eng, ws, ledger) {
|
|
735
|
-
if (eng.mounted.has(ws)) return eng.mounted.get(ws);
|
|
736
|
-
stageWorkspaceDir(eng, ws);
|
|
737
|
-
const gitdir = gitdirOf(ws);
|
|
738
|
-
let restored = false, remoteMain = null, restoreError = null;
|
|
739
|
-
try {
|
|
740
|
-
await custodySkeleton(eng, gitdir);
|
|
741
|
-
remoteMain = await custodyFetch(eng, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
742
|
-
if (remoteMain) {
|
|
743
|
-
kgit.writeRef(eng, gitdir, "HEAD", "ref: refs/heads/main");
|
|
744
|
-
restored = true;
|
|
745
|
-
}
|
|
746
|
-
} catch (e) {
|
|
747
|
-
restoreError = String(e && e.stack || e).split("\n").slice(0, 5).join(" | ");
|
|
748
|
-
}
|
|
749
|
-
const m = { restored, remoteMain, restoreError };
|
|
750
|
-
eng.mounted.set(ws, m);
|
|
751
|
-
return m;
|
|
752
|
-
}
|
|
753
|
-
function unmount(eng, ws) {
|
|
754
|
-
eng.mounted.delete(ws);
|
|
755
|
-
eng.mainIntents.delete(ws);
|
|
756
|
-
const wsRoot = eng.preopen.dir.contents.get("ws");
|
|
757
|
-
if (wsRoot) wsRoot.contents.delete(ws);
|
|
758
|
-
}
|
|
759
|
-
function writeWork(eng, name, bytes) {
|
|
760
|
-
eng.preopen.dir.contents.set(name, new File(bytes));
|
|
761
|
-
}
|
|
762
|
-
function currentLaw(eng, ws) {
|
|
763
|
-
const out = JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "currentLaw" }), branch: BRANCH }, eng.STDERR));
|
|
764
|
-
return out.keyMap || {};
|
|
765
|
-
}
|
|
766
|
-
function installedLaw(eng, ws) {
|
|
767
|
-
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "installedLaw" }), branch: BRANCH }, eng.STDERR));
|
|
768
|
-
}
|
|
769
|
-
function domainPackage(eng, ws, hash) {
|
|
770
|
-
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "domainPackage", hash }), branch: BRANCH }, eng.STDERR));
|
|
771
|
-
}
|
|
772
|
-
function evolveDryRun(eng, ws, usda, domainHash, dispositions = null) {
|
|
773
|
-
const r = author(eng, ws, "nomos", "installDomain", installPayload(domainHash, usda, eng.installedBy, dispositions), eng.hashes.nomos, { dryRun: true, deferProjection: false });
|
|
774
|
-
return r.ok ? { admits: true } : { admits: false, detail: r.error };
|
|
775
|
-
}
|
|
776
|
-
function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}) {
|
|
777
|
-
const seq = eng.seq++;
|
|
778
|
-
const capPorts = { clock: { physical: Date.now(), logical: 0, replica: eng.replica }, rng: osEntropyBuffer(opts.rngDraws ?? 64) };
|
|
779
|
-
if (opts.validFromEpoch !== void 0) capPorts.valid_from_epoch = opts.validFromEpoch;
|
|
780
|
-
if (opts.principalVerified !== void 0) {
|
|
781
|
-
capPorts.principal = opts.actor ?? "";
|
|
782
|
-
capPorts.principal_verified = !!opts.principalVerified;
|
|
783
|
-
}
|
|
784
|
-
const envelope = { payload: { domain, directiveId, payload }, captured_ports: capPorts, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
|
|
785
|
-
if (opts.attestations !== void 0) envelope.attestations = opts.attestations;
|
|
786
|
-
writeWork(eng, `payload-${seq}.json`, enc3.encode(JSON.stringify(payload)));
|
|
787
|
-
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
788
|
-
const genesis = !controllerHash;
|
|
789
|
-
const attestorSecretFor = (from) => {
|
|
790
|
-
const s = opts.attestorSecrets;
|
|
791
|
-
if (!s) return null;
|
|
792
|
-
if (typeof s === "string") return s;
|
|
793
|
-
return typeof s[from] === "string" ? s[from] : null;
|
|
794
|
-
};
|
|
795
|
-
const defer = opts.deferProjection !== false;
|
|
796
|
-
const offerOnce = () => JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", ...opts.authToken ? { authToken: opts.authToken } : {}, domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...opts.authorSecret ? { authorSecret: opts.authorSecret } : {}, ...opts.spans ? { spans: true } : {}, ...opts.dryRun ? { dryRun: true } : defer ? { deferProjection: true } : {} }, eng.STDERR));
|
|
797
|
-
let v = offerOnce();
|
|
798
|
-
for (let pass = 0; pass < 8 && v.outcome === "refused"; pass++) {
|
|
799
|
-
const need = parseAttestedReadNeed(v.verdict?.reason ?? v.error);
|
|
800
|
-
if (!need || !eng.mounted.has(need.from)) break;
|
|
801
|
-
const secret = attestorSecretFor(need.from);
|
|
802
|
-
if (!secret) break;
|
|
803
|
-
const served = attestedRead(eng, need.from, {
|
|
804
|
-
queryId: need.queryId,
|
|
805
|
-
paramsJson: JSON.stringify(need.args),
|
|
806
|
-
attestorSecret: secret,
|
|
807
|
-
nowMs: opts.attestNowMs ?? Date.now(),
|
|
808
|
-
// K3 (§1.5.3): colocated custody is in-memory — always ask for the carried enrollment
|
|
809
|
-
// proof; the kernel produces-or-omits under its own bounds (the adapter decides nothing).
|
|
810
|
-
includeRosterProof: true
|
|
811
|
-
});
|
|
812
|
-
if (!served || served.ok !== true) break;
|
|
813
|
-
envelope.attestations = [...envelope.attestations ?? [], { queryId: need.queryId, result: served.rows, attestation: served.attestation }];
|
|
814
|
-
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
815
|
-
v = offerOnce();
|
|
816
|
-
}
|
|
817
|
-
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, ...v.born ? { born: v.born } : {}, ...v.birthOutcomes ? { birthOutcomes: v.birthOutcomes } : {}, ...v.kernelSpans ? { kernelSpans: v.kernelSpans } : {} } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
818
|
-
if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
819
|
-
return res;
|
|
820
|
-
}
|
|
821
|
-
function setTestKRoot(eng, kRootPublicB64) {
|
|
822
|
-
return JSON.parse(call(eng.ex, "set_test_k_root", { kRootPublicB64 }, eng.STDERR));
|
|
823
|
-
}
|
|
824
|
-
function refreshProjection(eng, ws) {
|
|
825
|
-
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "refresh" }), branch: BRANCH }, eng.STDERR));
|
|
826
|
-
}
|
|
827
|
-
async function custodySkeleton(eng, gitdir) {
|
|
828
|
-
const p = eng.fs.promises;
|
|
829
|
-
for (const d of ["", "/objects", "/objects/pack", "/objects/info", "/refs", "/refs/heads"]) {
|
|
830
|
-
try {
|
|
831
|
-
await p.mkdir(`${gitdir}${d}`);
|
|
832
|
-
} catch (e) {
|
|
833
|
-
if (e && e.code !== "EEXIST") throw e;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
try {
|
|
837
|
-
await p.writeFile(`${gitdir}/HEAD`, `ref: refs/heads/${BRANCH}
|
|
838
|
-
`);
|
|
839
|
-
} catch {
|
|
840
|
-
}
|
|
841
|
-
try {
|
|
842
|
-
await p.writeFile(`${gitdir}/config`, "[core]\n repositoryformatversion = 0\n bare = true\n");
|
|
843
|
-
} catch {
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", localRef = null, have = null } = {}) {
|
|
847
|
-
const remoteRefFull = fullRef(remoteRef);
|
|
848
|
-
const refs = await custodyLsRefs(ledger, remoteRefFull);
|
|
849
|
-
const tip = refs[remoteRefFull] || null;
|
|
850
|
-
if (!tip) return null;
|
|
851
|
-
if (tip !== have) applyPackInto(eng, ws, await fetchPack(ledger.remote, ledger.headers || {}, { wants: [tip], haves: have ? [have] : [] }));
|
|
852
|
-
kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
|
|
853
|
-
return tip;
|
|
854
|
-
}
|
|
855
|
-
async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null } = {}) {
|
|
856
|
-
const headers = ledger.headers || {};
|
|
857
|
-
const remoteRefFull = fullRef(remoteRef);
|
|
858
|
-
const remote = await lsRefs(ledger.remote, headers, { service: "git-receive-pack", prefix: remoteRefFull });
|
|
859
|
-
const oldOid = remote[remoteRefFull] || ZERO_OID;
|
|
860
|
-
if (del) {
|
|
861
|
-
if (oldOid === ZERO_OID) return;
|
|
862
|
-
return pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: ZERO_OID }] }));
|
|
863
|
-
}
|
|
864
|
-
const tgt = newOid || kgit.resolveRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef));
|
|
865
|
-
if (!tgt) throw new Error(`custodyPush: local ref ${localRef || remoteRef} unresolved`);
|
|
866
|
-
if (tgt === oldOid) return;
|
|
867
|
-
let pack = null;
|
|
868
|
-
if (!pointer) {
|
|
869
|
-
pack = oldOid !== ZERO_OID ? packDelta(eng, ws, oldOid, tgt) : null;
|
|
870
|
-
if (!pack) pack = packDelta(eng, ws, "", tgt);
|
|
871
|
-
}
|
|
872
|
-
return pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
|
|
873
|
-
}
|
|
874
|
-
async function commitAndPush(eng, ws, ledger) {
|
|
875
|
-
try {
|
|
876
|
-
await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
877
|
-
} catch (e) {
|
|
878
|
-
unmount(eng, ws);
|
|
879
|
-
throw new Error(`ledger push failed \u2014 write NOT durable, rolled back to ledger: ${e}`);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
function describe(eng, ws) {
|
|
883
|
-
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "describe", workspace: ws }), branch: BRANCH }, eng.STDERR));
|
|
884
|
-
}
|
|
885
|
-
function parseAttestedReadNeed(error) {
|
|
886
|
-
const m = /attested-read-unreachable: read\('([^']+)', \{from: '([^']+)'\}\) args=(.*?) has no injected attestation/.exec(String(error || ""));
|
|
887
|
-
if (!m) return null;
|
|
888
|
-
let blob = m[3];
|
|
889
|
-
for (let i = 0; i < 4; i++) {
|
|
890
|
-
try {
|
|
891
|
-
return { queryId: m[1], from: m[2], args: JSON.parse(blob) };
|
|
892
|
-
} catch {
|
|
893
|
-
blob = blob.replace(/\\(["\\])/g, "$1");
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
return null;
|
|
897
|
-
}
|
|
898
|
-
function packDelta(eng, ws, base, head) {
|
|
899
|
-
try {
|
|
900
|
-
const r = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), base, head }, eng.STDERR));
|
|
901
|
-
if (!r || r.ok !== true || typeof r.packB64 !== "string") return null;
|
|
902
|
-
return Uint8Array.from(atob(r.packB64), (c) => c.charCodeAt(0));
|
|
903
|
-
} catch {
|
|
904
|
-
return null;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
function servePack(eng, ws, base = "") {
|
|
908
|
-
let head = null;
|
|
909
|
-
try {
|
|
910
|
-
head = kgit.resolveRef(eng, gitdirOf(ws), BRANCH);
|
|
911
|
-
} catch {
|
|
912
|
-
}
|
|
913
|
-
let r;
|
|
914
|
-
try {
|
|
915
|
-
r = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base, branch: BRANCH }, eng.STDERR));
|
|
916
|
-
} catch (e) {
|
|
917
|
-
const msg = String(e);
|
|
918
|
-
if (/nothing to pack|head == base/.test(msg)) return { packB64: "", bytes: 0, upToDate: true, ...head ? { head } : {} };
|
|
919
|
-
if (base && /reached root|before base|not found|unknown|no (?:merge )?base|missing|invalid/i.test(msg)) {
|
|
920
|
-
try {
|
|
921
|
-
const full = JSON.parse(call(eng.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base: "", branch: BRANCH }, eng.STDERR));
|
|
922
|
-
if (full && full.ok === true && typeof full.packB64 === "string") {
|
|
923
|
-
return { packB64: full.packB64, bytes: full.bytes, divergent: true, ...head ? { head } : {} };
|
|
924
|
-
}
|
|
925
|
-
} catch {
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
throw e;
|
|
929
|
-
}
|
|
930
|
-
if (!r || r.ok !== true || typeof r.packB64 !== "string") throw new Error("servePack: kernel produced no pack");
|
|
931
|
-
return { packB64: r.packB64, bytes: r.bytes, ...head ? { head } : {} };
|
|
932
|
-
}
|
|
933
|
-
function packBornChildren(eng, born) {
|
|
934
|
-
if (!Array.isArray(born) || !born.length) return born;
|
|
935
|
-
return born.map((b) => {
|
|
936
|
-
const child = b && typeof b.workspace === "string" ? b.workspace : null;
|
|
937
|
-
const head = b && typeof b.head === "string" ? b.head : null;
|
|
938
|
-
if (!child || !/^[0-9a-f]{40}$/i.test(head || "")) {
|
|
939
|
-
log("born-pack-skipped", { workspace: child, head, reason: "malformed born entry" });
|
|
940
|
-
return b;
|
|
941
|
-
}
|
|
942
|
-
try {
|
|
943
|
-
const pack = packDelta(eng, child, "", head);
|
|
944
|
-
if (!pack || !pack.length) {
|
|
945
|
-
log("born-pack-empty", { workspace: child, head });
|
|
946
|
-
return b;
|
|
947
|
-
}
|
|
948
|
-
return { ...b, packB64: b64Bytes(pack), packBytes: pack.length };
|
|
949
|
-
} catch (e) {
|
|
950
|
-
log("born-pack-failed", { workspace: child, head, error: String(e).slice(0, 200) });
|
|
951
|
-
return b;
|
|
952
|
-
}
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
function offerIntent(eng, ws, bytes, opts) {
|
|
956
|
-
const name = `offer-in-${eng.seq++}.json`;
|
|
957
|
-
writeWork(eng, name, bytes);
|
|
958
|
-
const deferProjection = !!(opts && opts.defer);
|
|
959
|
-
const branch = opts && opts.branch || BRANCH;
|
|
960
|
-
return JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, intentFile: `/work/${name}`, branch, deferProjection }, eng.STDERR));
|
|
961
|
-
}
|
|
962
|
-
function offerAdmit(eng, ws, bytes, opts) {
|
|
963
|
-
const v = offerIntent(eng, ws, bytes, opts);
|
|
964
|
-
return v.outcome === "admitted" ? { ok: true, head: v.head, ...v.born ? { born: v.born } : {} } : { ok: false, error: v.verdict?.reason ?? v.error ?? "the offer was refused" };
|
|
965
|
-
}
|
|
966
|
-
async function mainIntentIds(eng, ws) {
|
|
967
|
-
const gitdir = gitdirOf(ws);
|
|
968
|
-
const head = kgit.resolveRef(eng, gitdir, BRANCH);
|
|
969
|
-
if (!head) return { head: null, ids: /* @__PURE__ */ new Set(), oids: /* @__PURE__ */ new Set() };
|
|
970
|
-
const cached = eng.mainIntents.get(ws);
|
|
971
|
-
if (cached && cached.head === head) return cached;
|
|
972
|
-
const ids = /* @__PURE__ */ new Set(), oids = /* @__PURE__ */ new Set();
|
|
973
|
-
for (const c of kgit.intentsAbove(eng, gitdir, head, "", 500)) {
|
|
974
|
-
oids.add(c.oid);
|
|
975
|
-
try {
|
|
976
|
-
const id = JSON.parse(dec3.decode(bytesFromB64(c.intentB64))).id;
|
|
977
|
-
if (typeof id === "string" && id) ids.add(id);
|
|
978
|
-
} catch {
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
const m = { head, ids, oids };
|
|
982
|
-
eng.mainIntents.set(ws, m);
|
|
983
|
-
return m;
|
|
984
|
-
}
|
|
985
|
-
async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = null, defer = false } = {}) {
|
|
986
|
-
const routes = shardRouting ? directiveRoutes(eng) : null;
|
|
987
|
-
const gitdir = gitdirOf(ws);
|
|
988
|
-
const H = () => ({ ...ledger.headers || {} });
|
|
989
|
-
const _t0 = Date.now();
|
|
990
|
-
let _fetchMs = 0, _sealMs = 0, _intents = 0;
|
|
991
|
-
const _spans = [];
|
|
992
|
-
let _traceSeq = 0;
|
|
993
|
-
const _span = (name, kind, depth = 1, extra = {}) => {
|
|
994
|
-
const t0 = Date.now();
|
|
995
|
-
return (end = {}) => {
|
|
996
|
-
const t1 = Date.now();
|
|
997
|
-
_spans.push({ id: `admit-${_t0}-${_traceSeq++}`, track: "cloud", name, kind, depth, t0, t1, ...extra, ...end });
|
|
998
|
-
};
|
|
999
|
-
};
|
|
1000
|
-
const sessionRefPrefix = "refs/heads/session/";
|
|
1001
|
-
const sessions = {};
|
|
1002
|
-
const _scanDone = _span("scan session refs", "cscan", 1);
|
|
1003
|
-
for (const [ref, oid] of Object.entries(await custodyLsRefs(ledger, sessionRefPrefix))) {
|
|
1004
|
-
if (ref.startsWith(sessionRefPrefix) && oid) sessions[ref.slice(sessionRefPrefix.length)] = oid;
|
|
1005
|
-
}
|
|
1006
|
-
_scanDone({ sessions: Object.keys(sessions).length });
|
|
1007
|
-
const _scanMs = Date.now() - _t0;
|
|
1008
|
-
const results = [], deadLetters = [], placements = [], splits = [], globalsOut = [];
|
|
1009
|
-
const consumed = [];
|
|
1010
|
-
let totalAdmitted = 0;
|
|
1011
|
-
const globalsDeclared = shardRouting && !shardRouting.selfLabel ? globalAggregates(eng) : null;
|
|
1012
|
-
const summaryCtx = shardRouting && shardRouting.selfLabel && scopedReads(eng).length > 0 ? { reads: scopedReads(eng), prevs: /* @__PURE__ */ new Map(), values: /* @__PURE__ */ new Map(), events: [], rowsDelta: 0, rederived: /* @__PURE__ */ new Set(), rowsByHome: /* @__PURE__ */ new Map(), homeTags: /* @__PURE__ */ new Map(), homeKeySet: /* @__PURE__ */ new Set() } : null;
|
|
1013
|
-
const canDeferProjection = !summaryCtx && !(globalsDeclared && globalsDeclared.size > 0);
|
|
1014
|
-
if (summaryCtx) {
|
|
1015
|
-
try {
|
|
1016
|
-
for (const r of query(eng, ws, "aggregatesByType", JSON.stringify({ __type: "NomosHomeReceipt" }))) {
|
|
1017
|
-
const hk = r.data && typeof r.data.homeKey === "string" ? r.data.homeKey : null;
|
|
1018
|
-
if (!hk) continue;
|
|
1019
|
-
summaryCtx.homeKeySet.add(hk);
|
|
1020
|
-
summaryCtx.homeTags.set(await routeTagHexOfHomeKey(hk), hk);
|
|
1021
|
-
}
|
|
1022
|
-
} catch (e) {
|
|
1023
|
-
log("home-tag-table-error", { ws, error: String(e).slice(0, 200) });
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
const headBefore = summaryCtx ? kgit.resolveRef(eng, gitdir, BRANCH) : null;
|
|
1027
|
-
for (const [cid, head] of Object.entries(sessions)) {
|
|
1028
|
-
const ref = `refs/heads/session/${cid}`;
|
|
1029
|
-
const shortCid = cid.length > 14 ? `${cid.slice(0, 14)}...` : cid;
|
|
1030
|
-
const _sessionDone = _span(`session ${shortCid}`, "csession", 1, { client: cid });
|
|
1031
|
-
try {
|
|
1032
|
-
const _fetchDone = _span(`fetch ${shortCid}`, "cfetch", 2, { client: cid });
|
|
1033
|
-
const _f = Date.now();
|
|
1034
|
-
try {
|
|
1035
|
-
const localMain = kgit.resolveRef(eng, gitdir, BRANCH);
|
|
1036
|
-
try {
|
|
1037
|
-
await custodyFetch(eng, ws, ledger, { remoteRef: ref, have: localMain });
|
|
1038
|
-
} catch (thinErr) {
|
|
1039
|
-
log("thin-fetch-fallback", { ws, cid, error: String(thinErr && thinErr.message || thinErr).slice(0, 160) });
|
|
1040
|
-
await custodyFetch(eng, ws, ledger, { remoteRef: ref, have: null });
|
|
1041
|
-
}
|
|
1042
|
-
} finally {
|
|
1043
|
-
_fetchMs += Date.now() - _f;
|
|
1044
|
-
_fetchDone();
|
|
1045
|
-
}
|
|
1046
|
-
const _walkDone = _span(`walk ${shortCid}`, "cwalk", 2, { client: cid });
|
|
1047
|
-
const main = await mainIntentIds(eng, ws);
|
|
1048
|
-
const recent = kgit.intentsAbove(eng, gitdir, head, "", 100);
|
|
1049
|
-
const chain = [];
|
|
1050
|
-
for (let i = recent.length - 1; i >= 0; i--) {
|
|
1051
|
-
if (main.oids.has(recent[i].oid)) break;
|
|
1052
|
-
chain.push(recent[i]);
|
|
1053
|
-
}
|
|
1054
|
-
chain.reverse();
|
|
1055
|
-
_walkDone({ intents: chain.length });
|
|
1056
|
-
const admitted = [], rejected = [], skipped = [];
|
|
1057
|
-
const _gateDone = _span(`gate+fold ${shortCid}`, "cgate", 2, { client: cid, intents: chain.length });
|
|
1058
|
-
for (const c of chain) {
|
|
1059
|
-
const oid = c.oid;
|
|
1060
|
-
try {
|
|
1061
|
-
const blob = bytesFromB64(c.intentB64);
|
|
1062
|
-
let dom = null, intentId = null, dirId = null;
|
|
1063
|
-
try {
|
|
1064
|
-
const doc = JSON.parse(dec3.decode(blob));
|
|
1065
|
-
intentId = typeof doc.id === "string" ? doc.id : null;
|
|
1066
|
-
dom = doc && doc.payload && typeof doc.payload.domain === "string" ? doc.payload.domain : null;
|
|
1067
|
-
dirId = doc && doc.payload && typeof doc.payload.directiveId === "string" ? doc.payload.directiveId : null;
|
|
1068
|
-
} catch {
|
|
1069
|
-
}
|
|
1070
|
-
if (!dom) {
|
|
1071
|
-
rejected.push({ oid: oid.slice(0, 10), error: "non-authored intent \u2014 governance enters its own admin lane" });
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
|
|
1075
|
-
const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes. Author it via POST /v1/workspaces/${ws}/author (owner Bearer), or \u2014 if this parked receipt is sanctioned \u2014 the owner unjams it: POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
1076
|
-
rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
|
|
1077
|
-
deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
1078
|
-
continue;
|
|
1079
|
-
}
|
|
1080
|
-
if (routes && dom && dirId) {
|
|
1081
|
-
if (dirId === "nomosSealHome" || dirId === "nomosSealHandoff") {
|
|
1082
|
-
rejected.push({ oid: oid.slice(0, 10), error: `session lane refuses '${dirId}' \u2014 the \xA76 handoff seals are authored by the adapter after migration verifies (begin a move with nomosSplitShard, or POST /v1/workspaces/<coordinator>/split)` });
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
const route = routes.get(`${dom} ${dirId}`);
|
|
1086
|
-
if (route) {
|
|
1087
|
-
let payloadObj = null;
|
|
1088
|
-
try {
|
|
1089
|
-
payloadObj = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
1090
|
-
} catch {
|
|
1091
|
-
}
|
|
1092
|
-
const verdict2 = await wrongHomeVerdict(shardRouting, route, payloadObj);
|
|
1093
|
-
if (verdict2) {
|
|
1094
|
-
rejected.push({ oid: oid.slice(0, 10), error: verdict2.error, ...verdict2.wrongHome ? { wrongHome: verdict2.wrongHome } : {}, ...verdict2.homeMoving ? { homeMoving: verdict2.homeMoving } : {}, ...verdict2.deadLettered ? { deadLettered: true } : {} });
|
|
1095
|
-
if (verdict2.deadLettered) deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error: verdict2.error, blob, domain: dom, directiveId: dirId });
|
|
1096
|
-
continue;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
if (dirId === "nomosReceiveAssignment" && shardRouting) {
|
|
1100
|
-
let p = null;
|
|
1101
|
-
try {
|
|
1102
|
-
p = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
1103
|
-
} catch {
|
|
1104
|
-
}
|
|
1105
|
-
const homeKey = p && typeof p.homeKey === "string" ? p.homeKey : null;
|
|
1106
|
-
const rows = homeKey ? await shardRouting.assignments() || [] : [];
|
|
1107
|
-
const a = homeKey ? rows.find((x) => x.homeKey === homeKey) : null;
|
|
1108
|
-
if (!a) {
|
|
1109
|
-
const error = `assignment receipt for home '${String(homeKey).slice(0, 80)}' has NO placement on the coordinator '${shardRouting.coordinatorWs}' \u2014 author the derived placement intent (birth<Type>) first, then POST /dead-letters/retry`;
|
|
1110
|
-
rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
|
|
1111
|
-
deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
1112
|
-
continue;
|
|
1113
|
-
}
|
|
1114
|
-
if (a.shard !== shardRouting.selfLabel || p && p.shard !== shardRouting.selfLabel) {
|
|
1115
|
-
const correct = `${shardRouting.coordinatorWs}--${a.shard}`;
|
|
1116
|
-
rejected.push({
|
|
1117
|
-
oid: oid.slice(0, 10),
|
|
1118
|
-
error: `wrong-home: the coordinator's shard map assigns home '${homeKey.slice(0, 80)}' to '${correct}' (not this workspace) \u2014 a routed client re-routes and retries automatically`,
|
|
1119
|
-
wrongHome: { workspace: correct, ...a.mapVersion !== void 0 ? { mapVersion: a.mapVersion } : {} }
|
|
1120
|
-
});
|
|
1121
|
-
continue;
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
if (intentId && main.ids.has(intentId)) {
|
|
1126
|
-
skipped.push({ oid: oid.slice(0, 10), intent: intentId.slice(0, 12), alreadyOnMain: true });
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
|
-
const cap = summaryCtx ? beginSummaryCapture(eng, ws, summaryCtx, blob) : null;
|
|
1130
|
-
const verdict = offerIntent(eng, ws, blob, { defer: canDeferProjection });
|
|
1131
|
-
const res = verdict.outcome === "admitted" ? { ok: true, head: verdict.head } : { ok: false, error: verdict.verdict?.reason ?? verdict.error ?? "the offer was refused" };
|
|
1132
|
-
if (res.ok) {
|
|
1133
|
-
main.ids.add(intentId);
|
|
1134
|
-
main.oids.add(res.head);
|
|
1135
|
-
main.head = res.head;
|
|
1136
|
-
admitted.push({ oid: oid.slice(0, 10), head: res.head });
|
|
1137
|
-
if (cap) finishSummaryCapture(eng, ws, summaryCtx, cap, res.head, intentId);
|
|
1138
|
-
if (shardRouting && dirId) {
|
|
1139
|
-
const placement = placementDirectives(eng).get(dirId);
|
|
1140
|
-
if (placement) {
|
|
1141
|
-
let p = null;
|
|
1142
|
-
try {
|
|
1143
|
-
p = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
1144
|
-
} catch {
|
|
1145
|
-
}
|
|
1146
|
-
const homeKey = p ? p[placement.keyField] : void 0;
|
|
1147
|
-
if (typeof homeKey === "string" && typeof p?.shard === "string") {
|
|
1148
|
-
placements.push({ directive: dirId, homeKey, shard: p.shard, mapVersion: 1 });
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
if (dirId === "nomosSplitShard" && !shardRouting.selfLabel) {
|
|
1152
|
-
let p = null;
|
|
1153
|
-
try {
|
|
1154
|
-
p = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
1155
|
-
} catch {
|
|
1156
|
-
}
|
|
1157
|
-
if (p && typeof p.fromShard === "string" && typeof p.toShard === "string" && Array.isArray(p.homeKeys)) {
|
|
1158
|
-
splits.push({ fromShard: p.fromShard, toShard: p.toShard, homeKeys: p.homeKeys.filter((k) => typeof k === "string") });
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
if (globalsDeclared && globalsDeclared.size > 0) {
|
|
1163
|
-
let touchesGlobal = false;
|
|
1164
|
-
for (const ev of parseIntentEvents(blob)) {
|
|
1165
|
-
const rows = qById(eng, ws, ev.aggregate);
|
|
1166
|
-
const t = ev.sets.get("__type") ?? (rows.length ? rows[0].type : void 0);
|
|
1167
|
-
if (t !== void 0 && globalsDeclared.has(t)) {
|
|
1168
|
-
touchesGlobal = true;
|
|
1169
|
-
break;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
if (touchesGlobal) globalsOut.push({ intentId: intentId || "", oid: oid.slice(0, 10), blob });
|
|
1173
|
-
}
|
|
1174
|
-
} else {
|
|
1175
|
-
const wh = String(res.error || "").match(/wrong-home:(s\d+)/);
|
|
1176
|
-
if (wh) {
|
|
1177
|
-
const coordinator = shardRouting && shardRouting.coordinatorWs || (ws.match(/^(.*)--s\d+$/) || [])[1] || ws;
|
|
1178
|
-
rejected.push({
|
|
1179
|
-
oid: oid.slice(0, 10),
|
|
1180
|
-
error: `wrong-home: this write's home is assigned to '${coordinator}--${wh[1]}' \u2014 the chain gate's homing invariant refused it; a routed client re-routes and retries automatically`,
|
|
1181
|
-
wrongHome: { workspace: `${coordinator}--${wh[1]}` }
|
|
1182
|
-
});
|
|
1183
|
-
continue;
|
|
1184
|
-
}
|
|
1185
|
-
const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
1186
|
-
rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
|
|
1187
|
-
deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
1188
|
-
}
|
|
1189
|
-
} catch (e) {
|
|
1190
|
-
rejected.push({ oid: oid.slice(0, 10), error: String(e).slice(0, 200) });
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
_gateDone({ admitted: admitted.length, rejected: rejected.length, skipped: skipped.length });
|
|
1194
|
-
_intents += admitted.length;
|
|
1195
|
-
totalAdmitted += admitted.length;
|
|
1196
|
-
consumed.push({ cid, ref });
|
|
1197
|
-
results.push({ session: cid, admitted, rejected, ...skipped.length ? { skipped } : {} });
|
|
1198
|
-
} catch (e) {
|
|
1199
|
-
results.push({ session: cid, error: String(e).slice(0, 200) });
|
|
1200
|
-
} finally {
|
|
1201
|
-
_sessionDone();
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
if (canDeferProjection && totalAdmitted) {
|
|
1205
|
-
const _projDone = _span("projection flush (batch)", "cproject", 1, { admitted: totalAdmitted });
|
|
1206
|
-
try {
|
|
1207
|
-
refreshProjection(eng, ws);
|
|
1208
|
-
} catch (e) {
|
|
1209
|
-
log("admit-projection-flush-failed", { ws, error: String(e).slice(0, 200) });
|
|
1210
|
-
} finally {
|
|
1211
|
-
_projDone();
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
let unparkedSessions = /* @__PURE__ */ new Set();
|
|
1215
|
-
if (deadLetters.length) {
|
|
1216
|
-
try {
|
|
1217
|
-
unparkedSessions = (await materializeRefused(eng, ws, ledger, deadLetters)).unparkedSessions;
|
|
1218
|
-
} catch (e) {
|
|
1219
|
-
unparkedSessions = new Set(deadLetters.map((dl) => dl.session).filter(Boolean));
|
|
1220
|
-
log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
const prunable = unparkedSessions.size ? consumed.filter((c) => !unparkedSessions.has(c.cid)) : consumed;
|
|
1224
|
-
const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
|
|
1225
|
-
const deferDurability = defer && !hasCoordination;
|
|
1226
|
-
let _sealed = true;
|
|
1227
|
-
if (totalAdmitted && !deferDurability) {
|
|
1228
|
-
const _sealDone = _span(`seal (batch)`, "cseal", 1, { sessions: consumed.length, admitted: totalAdmitted });
|
|
1229
|
-
const _s = Date.now();
|
|
1230
|
-
try {
|
|
1231
|
-
await commitAndPush(eng, ws, ledger);
|
|
1232
|
-
} catch (e) {
|
|
1233
|
-
_sealed = false;
|
|
1234
|
-
log("admit-batch-push-failed", { ws, sessions: consumed.length, admitted: totalAdmitted, error: String(e).slice(0, 200) });
|
|
1235
|
-
} finally {
|
|
1236
|
-
_sealMs += Date.now() - _s;
|
|
1237
|
-
_sealDone();
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
if (_sealed && !deferDurability) {
|
|
1241
|
-
for (const { ref } of prunable) {
|
|
1242
|
-
try {
|
|
1243
|
-
await custodyPush(eng, ws, ledger, { remoteRef: ref, del: true });
|
|
1244
|
-
} catch {
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
const newMain = kgit.resolveRef(eng, gitdir, BRANCH);
|
|
1249
|
-
log("admit", { ws, sessions: results.length, main: newMain });
|
|
1250
|
-
const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore, newMain) : null;
|
|
1251
|
-
const _total = Date.now() - _t0;
|
|
1252
|
-
const timings = { totalMs: _total, scanMs: _scanMs, fetchMs: _fetchMs, sealMs: _sealMs, gateFoldMs: Math.max(0, _total - _scanMs - _fetchMs - _sealMs), intents: _intents, sessions: results.length, spans: _spans };
|
|
1253
|
-
return {
|
|
1254
|
-
sessions: results,
|
|
1255
|
-
main: newMain,
|
|
1256
|
-
deadLetters,
|
|
1257
|
-
timings,
|
|
1258
|
-
...summary ? { summary } : {},
|
|
1259
|
-
...placements.length ? { placements } : {},
|
|
1260
|
-
...splits.length ? { splits } : {},
|
|
1261
|
-
...globalsOut.length ? { globals: globalsOut } : {},
|
|
1262
|
-
// The adapter's backup lane consumes these: `deferred` ⇒ resident main carries totalAdmitted
|
|
1263
|
-
// un-backed-up commits and `consumed` are the session branches to delete AFTER the push lands.
|
|
1264
|
-
...deferDurability && totalAdmitted ? { deferred: true, consumed: prunable, admitted: totalAdmitted } : {}
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
async function writeRefusedRef(eng, ws, dl) {
|
|
1268
|
-
const gitdir = gitdirOf(ws);
|
|
1269
|
-
const blob = dl.blob instanceof Uint8Array ? dl.blob : enc3.encode(String(dl.blob || ""));
|
|
1270
|
-
const refForStamp = refusedRefName(dl.intentId || dl.oid);
|
|
1271
|
-
let parkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1272
|
-
try {
|
|
1273
|
-
const prevOid = kgit.resolveRef(eng, gitdir, refForStamp);
|
|
1274
|
-
if (prevOid) {
|
|
1275
|
-
const prev = JSON.parse(dec3.decode(kgit.readBlob(eng, gitdir, prevOid, "verdict.json")));
|
|
1276
|
-
if (prev && typeof prev.parkedAt === "string") parkedAt = prev.parkedAt;
|
|
1277
|
-
}
|
|
1278
|
-
} catch {
|
|
1279
|
-
}
|
|
1280
|
-
const verdict = enc3.encode(JSON.stringify({
|
|
1281
|
-
offerId: dl.intentId || null,
|
|
1282
|
-
oid: dl.oid || null,
|
|
1283
|
-
domain: dl.domain || null,
|
|
1284
|
-
directiveId: dl.directiveId || null,
|
|
1285
|
-
session: dl.session || null,
|
|
1286
|
-
parkedAt,
|
|
1287
|
-
verdict: { reason: String(dl.error || "refused") }
|
|
1288
|
-
}));
|
|
1289
|
-
const ref = refusedRefName(dl.intentId || dl.oid);
|
|
1290
|
-
kgit.writeMetaCommit(eng, gitdir, {
|
|
1291
|
-
parents: [],
|
|
1292
|
-
files: [{ path: "intent.json", bytes: blob }, { path: "verdict.json", bytes: verdict }],
|
|
1293
|
-
message: `refused: ${dl.domain || "?"}/${dl.directiveId || "?"} \u2014 ${String(dl.error || "").slice(0, 120)}`,
|
|
1294
|
-
timeSecs: 0,
|
|
1295
|
-
ref
|
|
1296
|
-
});
|
|
1297
|
-
return ref;
|
|
1298
|
-
}
|
|
1299
|
-
async function listRefusedRefs(eng, ws) {
|
|
1300
|
-
const gitdir = gitdirOf(ws);
|
|
1301
|
-
const refs = kgit.listRefs(eng, gitdir, "refs/heads/refused/");
|
|
1302
|
-
const out = [];
|
|
1303
|
-
for (const { ref, oid } of refs) {
|
|
1304
|
-
try {
|
|
1305
|
-
const vb = kgit.readBlob(eng, gitdir, oid, "verdict.json");
|
|
1306
|
-
const ib = kgit.readBlob(eng, gitdir, oid, "intent.json");
|
|
1307
|
-
const v = JSON.parse(dec3.decode(vb));
|
|
1308
|
-
out.push({ ref, ...v, intentB64: b64Bytes(ib) });
|
|
1309
|
-
} catch {
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
return out;
|
|
1313
|
-
}
|
|
1314
|
-
async function refusedSummary(eng, ws) {
|
|
1315
|
-
const gitdir = gitdirOf(ws);
|
|
1316
|
-
const refs = kgit.listRefs(eng, gitdir, "refs/heads/refused/");
|
|
1317
|
-
let count2 = 0, latestParkedAt = null;
|
|
1318
|
-
for (const { oid } of refs) {
|
|
1319
|
-
try {
|
|
1320
|
-
const v = JSON.parse(dec3.decode(kgit.readBlob(eng, gitdir, oid, "verdict.json")));
|
|
1321
|
-
count2++;
|
|
1322
|
-
if (v && typeof v.parkedAt === "string" && (!latestParkedAt || v.parkedAt > latestParkedAt)) latestParkedAt = v.parkedAt;
|
|
1323
|
-
} catch {
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
return { count: count2, latestParkedAt };
|
|
1327
|
-
}
|
|
1328
|
-
async function materializeRefused(eng, ws, ledger, deadLetters) {
|
|
1329
|
-
const refs = [];
|
|
1330
|
-
const unparkedSessions = /* @__PURE__ */ new Set();
|
|
1331
|
-
for (const dl of deadLetters || []) {
|
|
1332
|
-
try {
|
|
1333
|
-
const ref = await writeRefusedRef(eng, ws, dl);
|
|
1334
|
-
await custodyPush(eng, ws, ledger, { remoteRef: ref });
|
|
1335
|
-
refs.push(ref);
|
|
1336
|
-
} catch (e) {
|
|
1337
|
-
if (dl && dl.session) unparkedSessions.add(dl.session);
|
|
1338
|
-
log("refused-ref-materialize-failed", { ws, intentId: dl?.intentId, error: String(e).slice(0, 200) });
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
return { refs, unparkedSessions };
|
|
1342
|
-
}
|
|
1343
|
-
async function deleteRefusedRef(eng, ws, ledger, offerId) {
|
|
1344
|
-
const gitdir = gitdirOf(ws);
|
|
1345
|
-
const ref = refusedRefName(offerId);
|
|
1346
|
-
try {
|
|
1347
|
-
kgit.deleteRef(eng, gitdir, ref);
|
|
1348
|
-
} catch {
|
|
1349
|
-
}
|
|
1350
|
-
try {
|
|
1351
|
-
await custodyPush(eng, ws, ledger, { remoteRef: ref, del: true });
|
|
1352
|
-
} catch (e) {
|
|
1353
|
-
log("refused-ref-delete-push-failed", { ws, ref, error: String(e).slice(0, 200) });
|
|
1354
|
-
}
|
|
1355
|
-
return ref;
|
|
1356
|
-
}
|
|
1357
|
-
async function boot(eng, ws, ledger) {
|
|
1358
|
-
const m = await mountWorkspace(eng, ws, ledger);
|
|
1359
|
-
if (nomosActive(eng, ws)) {
|
|
1360
|
-
return { status: "already-initialized", controllerHash: eng.hashes.nomos, rows: qById(eng, ws, `domain-installation:${eng.hashes.nomos}`), mount: m };
|
|
1361
|
-
}
|
|
1362
|
-
const result = author(eng, ws, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, eng.installedBy), "");
|
|
1363
|
-
await commitAndPush(eng, ws, ledger);
|
|
1364
|
-
return { status: "created", head: result.head, controllerHash: eng.hashes.nomos, rows: qById(eng, ws, `domain-installation:${eng.hashes.nomos}`), mount: m };
|
|
1365
|
-
}
|
|
1366
|
-
async function deploy(eng, ws, ledger, usda, domainHash, dispositions, opts = {}) {
|
|
1367
|
-
const result = author(eng, ws, "nomos", "installDomain", installPayload(domainHash, usda, eng.installedBy, dispositions), eng.hashes.nomos, { actor: opts.actor ?? "", ...opts.authToken ? { authToken: opts.authToken } : {} });
|
|
1368
|
-
const installation = qById(eng, ws, `domain-installation:${domainHash}`);
|
|
1369
|
-
const keys = installation[0]?.data?.domainKeys ?? installation[0]?.domainKeys ?? [];
|
|
1370
|
-
if (keys.length > 0) {
|
|
1371
|
-
const law = installedLaw(eng, ws);
|
|
1372
|
-
const currentLaw2 = law?.currentLaw ?? {};
|
|
1373
|
-
const stale = keys.filter((k) => currentLaw2[k] !== void 0 && currentLaw2[k] !== domainHash);
|
|
1374
|
-
if (stale.length > 0) {
|
|
1375
|
-
throw new Error(
|
|
1376
|
-
`deploy-did-not-advance: the install of ${domainHash.slice(0, 12)}\u2026 ADMITTED but the workspace's current law still serves ${stale.map((k) => `'${k}' from ${String(currentLaw2[k]).slice(0, 12)}\u2026`).join(", ")} \u2014 refusing (custody unchanged) instead of a silent no-op. This usually means the existing installation's era/lineage prevents supersession: run \`githolon deploy --dry-run=evolve\` for the gate's verdict, supply #58 dispositions for a true removal/retype, or re-forge the workspace onto the current lineage.`
|
|
1377
|
-
);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
await commitAndPush(eng, ws, ledger);
|
|
1381
|
-
return { head: result.head, installation };
|
|
1382
|
-
}
|
|
1383
|
-
function snapshot(eng, ws) {
|
|
1384
|
-
const wsDir = eng.preopen.dir.contents.get("ws").contents.get(ws);
|
|
1385
|
-
return serializeTree(wsDir);
|
|
1386
|
-
}
|
|
1387
|
-
async function ledgerStats(eng, ws, ledger = null) {
|
|
1388
|
-
const gitdir = gitdirOf(ws);
|
|
1389
|
-
let head = kgit.resolveRef(eng, gitdir, BRANCH);
|
|
1390
|
-
if (!head && ledger?.remote) {
|
|
1391
|
-
try {
|
|
1392
|
-
await custodySkeleton(eng, gitdir);
|
|
1393
|
-
const remoteMain = await custodyFetch(eng, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
1394
|
-
if (!remoteMain) return { head: null, commits: 0 };
|
|
1395
|
-
kgit.writeRef(eng, gitdir, "HEAD", `ref: refs/heads/${BRANCH}`);
|
|
1396
|
-
head = kgit.resolveRef(eng, gitdir, BRANCH);
|
|
1397
|
-
const mounted2 = eng.mounted.get(ws);
|
|
1398
|
-
if (mounted2) {
|
|
1399
|
-
mounted2.restored = true;
|
|
1400
|
-
mounted2.remoteMain = remoteMain;
|
|
1401
|
-
mounted2.restoreError = null;
|
|
1402
|
-
}
|
|
1403
|
-
} catch {
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
if (!head) return { head: null, commits: 0 };
|
|
1407
|
-
const mounted = eng.mounted.get(ws);
|
|
1408
|
-
if (mounted?.ledgerStats?.head === head) return mounted.ledgerStats;
|
|
1409
|
-
const commits = kgit.intentsAbove(eng, gitdir, head).length;
|
|
1410
|
-
const out = { head, commits };
|
|
1411
|
-
if (mounted) mounted.ledgerStats = out;
|
|
1412
|
-
return out;
|
|
1413
|
-
}
|
|
1414
|
-
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, log, repoArgOfGitdir, bytesFromB64, kgit, installPayload, routeTagHexOfHomeKey, SUMMARY_SEP, summaryKey, splitSummaryKey, NOMOS_SHARD_ROWS_READ, groupOf, tallyOf, isExtremumRead, extremumMonotone, pushVia, fullRef, applyPackInto, custodyLsRefs, qById, query, authorKeygen, attestedRead, count, sum, pendingConflicts, spatialBboxParams, spatialWithin, extremum, nomosActive, refusedRefName;
|
|
1415
|
-
var init_engine = __esm({
|
|
1416
|
-
"../adapter/engine.mjs"() {
|
|
1417
|
-
init_git_fs();
|
|
1418
|
-
init_tree();
|
|
1419
|
-
init_custody_transport();
|
|
1420
|
-
enc3 = new TextEncoder();
|
|
1421
|
-
dec3 = new TextDecoder();
|
|
1422
|
-
BRANCH = "main";
|
|
1423
|
-
RESULT_PACK_INLINE_MAX = 256 * 1024;
|
|
1424
|
-
repoArgOf = (ws) => `/work/ws/${ws}`;
|
|
1425
|
-
gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
|
|
1426
|
-
unpack = (p) => {
|
|
1427
|
-
const v = typeof p === "bigint" ? p : BigInt(p);
|
|
1428
|
-
return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
|
|
1429
|
-
};
|
|
1430
|
-
log = (evt, fields = {}) => console.log(JSON.stringify({ evt, t: Date.now(), ...fields }));
|
|
1431
|
-
repoArgOfGitdir = (gitdir) => gitdir.replace(/\/nomos\.git$/, "");
|
|
1432
|
-
bytesFromB64 = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
1433
|
-
kgit = {
|
|
1434
|
-
// resolveRef → tip oid hex, or null if the ref is absent (replaces git.resolveRef(...).catch(()=>null)).
|
|
1435
|
-
resolveRef: (eng, gitdir, ref) => JSON.parse(call(eng.ex, "resolve_ref", { repoArg: repoArgOfGitdir(gitdir), ref }, eng.STDERR)).oid,
|
|
1436
|
-
// readBlob → the blob bytes at `path` inside commit `oid`, or null if not found (no throw).
|
|
1437
|
-
readBlob: (eng, gitdir, oid, path2) => {
|
|
1438
|
-
const r = JSON.parse(call(eng.ex, "read_blob", { repoArg: repoArgOfGitdir(gitdir), oid, path: path2 }, eng.STDERR));
|
|
1439
|
-
return r.ok ? bytesFromB64(r.blobB64) : null;
|
|
1440
|
-
},
|
|
1441
|
-
// intentsAbove → [{ oid, intentB64 }] oldest→newest over (base, head] (replaces log + readBlob intent.json).
|
|
1442
|
-
// `limit` (0 = all) caps to the most-recent N intent commits (replaces git.log depth).
|
|
1443
|
-
intentsAbove: (eng, gitdir, head, base = "", limit = 0) => JSON.parse(call(eng.ex, "intents_above", { repoArg: repoArgOfGitdir(gitdir), head, base, limit }, eng.STDERR)).intents,
|
|
1444
|
-
// listRefs → [{ ref, oid }] under prefix (replaces git.listRefs for LOCAL refs).
|
|
1445
|
-
listRefs: (eng, gitdir, prefix) => JSON.parse(call(eng.ex, "list_refs", { repoArg: repoArgOfGitdir(gitdir), prefix }, eng.STDERR)).refs,
|
|
1446
|
-
// writeRef → force-point a ref (plain oid or `ref:`-symbolic) (replaces git.writeRef).
|
|
1447
|
-
writeRef: (eng, gitdir, ref, value) => JSON.parse(call(eng.ex, "write_ref", { repoArg: repoArgOfGitdir(gitdir), ref, value }, eng.STDERR)),
|
|
1448
|
-
// deleteRef → remove a local ref, idempotent (replaces git.deleteRef).
|
|
1449
|
-
deleteRef: (eng, gitdir, ref) => JSON.parse(call(eng.ex, "delete_ref", { repoArg: repoArgOfGitdir(gitdir), ref }, eng.STDERR)),
|
|
1450
|
-
// applyPack → ingest a pack's objects into ws's repo (kernel apply_pack; refs are the caller's job).
|
|
1451
|
-
applyPack: (eng, ws, packBytes) => applyPackInto(eng, ws, packBytes),
|
|
1452
|
-
// writeMetaCommit → blobs→tree→commit→ref for a CUSTODY-META fact (checkpoint/refused/outbox/shape).
|
|
1453
|
-
// `files`: [{ path, bytes:Uint8Array }]; returns the commit oid (replaces writeBlob+writeTree+writeCommit[+writeRef]).
|
|
1454
|
-
writeMetaCommit: (eng, gitdir, { parents = [], files, message = "nomos custody meta", timeSecs = 0, ref = "" }) => JSON.parse(call(eng.ex, "write_meta_commit", {
|
|
1455
|
-
repoArg: repoArgOfGitdir(gitdir),
|
|
1456
|
-
parents,
|
|
1457
|
-
files: files.map((f) => ({ path: f.path, blobB64: b64Bytes(f.bytes) })),
|
|
1458
|
-
message,
|
|
1459
|
-
timeSecs,
|
|
1460
|
-
ref
|
|
1461
|
-
}, eng.STDERR)).oid
|
|
1462
|
-
};
|
|
1463
|
-
installPayload = (hash, usda, installedBy, dispositions) => ({ domainHash: hash, packageUsda: usda, installedBy, dependencies: [], finalizers: [], ...dispositions ? { dispositions } : {} });
|
|
1464
|
-
routeTagHexOfHomeKey = async (homeKey) => (await sha256hex(`nomos-route:${homeKey}`)).slice(0, 12);
|
|
1465
|
-
SUMMARY_SEP = "";
|
|
1466
|
-
summaryKey = (readId, group) => `${readId}${SUMMARY_SEP}${group}`;
|
|
1467
|
-
splitSummaryKey = (key) => {
|
|
1468
|
-
const i = key.indexOf(SUMMARY_SEP);
|
|
1469
|
-
return [key.slice(0, i), key.slice(i + 1)];
|
|
1470
|
-
};
|
|
1471
|
-
NOMOS_SHARD_ROWS_READ = "nomosShardRows";
|
|
1472
|
-
groupOf = (read, data) => {
|
|
1473
|
-
if (read.by === null) return "";
|
|
1474
|
-
const v = data ? data[read.by] : void 0;
|
|
1475
|
-
return v === void 0 || v === null ? void 0 : String(v);
|
|
1476
|
-
};
|
|
1477
|
-
tallyOf = (eng, ws, read, group) => read.kind === "count" ? count(eng, ws, read.id, group).count : read.kind === "sum" ? sum(eng, ws, read.id, group).sum : extremum(eng, ws, read.id, read.kind, group).value;
|
|
1478
|
-
isExtremumRead = (read) => read.kind === "min" || read.kind === "max";
|
|
1479
|
-
extremumMonotone = (read, prev, next) => next === null && prev === null || next !== null && (prev === null || (read.kind === "min" ? next <= prev : next >= prev));
|
|
1480
|
-
pushVia = (ledger, fn) => ledger && ledger.pushLane ? ledger.pushLane(fn) : fn();
|
|
1481
|
-
fullRef = (r) => r === "HEAD" || r.startsWith("refs/") ? r : `refs/heads/${r}`;
|
|
1482
|
-
applyPackInto = (eng, ws, pack) => {
|
|
1483
|
-
if (!pack || !pack.length) return;
|
|
1484
|
-
const r = JSON.parse(call(eng.ex, "apply_pack", { repoArg: repoArgOf(ws), packB64: b64Bytes(pack) }, eng.STDERR));
|
|
1485
|
-
if (!r || r.ok !== true) throw new Error(`apply_pack: ${r && r.error}`);
|
|
1486
|
-
};
|
|
1487
|
-
custodyLsRefs = (ledger, prefix = null) => lsRefs(ledger.remote, ledger.headers || {}, { service: "git-upload-pack", prefix });
|
|
1488
|
-
qById = (eng, ws, id, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "byId", aggregateId: id }), principal, keys, branch: BRANCH }, eng.STDERR));
|
|
1489
|
-
query = (eng, ws, queryId, paramsJson, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ kind: "query", queryId, paramsJson }), principal, keys, branch: BRANCH }, eng.STDERR));
|
|
1490
|
-
authorKeygen = (eng) => JSON.parse(call(eng.ex, "query", { queryBytes: b64Json({ op: "authorKeygen" }) }, eng.STDERR));
|
|
1491
|
-
attestedRead = (eng, ws, { queryId, paramsJson, attestorSecret, nowMs, includeRosterProof }) => JSON.parse(call(eng.ex, "query", {
|
|
1492
|
-
repoArg: repoArgOf(ws),
|
|
1493
|
-
workspace: ws,
|
|
1494
|
-
branch: BRANCH,
|
|
1495
|
-
queryBytes: b64Json({ op: "attestedRead", queryId, paramsJson: paramsJson || "{}", attestorSecret, nowMs: nowMs ?? Date.now(), sourceWorkspace: ws, ...includeRosterProof ? { includeRosterProof: true } : {} })
|
|
1496
|
-
}, eng.STDERR));
|
|
1497
|
-
count = (eng, ws, countId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "count", countId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
|
|
1498
|
-
sum = (eng, ws, sumId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "sum", sumId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
|
|
1499
|
-
pendingConflicts = (eng, ws, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "pendingConflicts" }), principal, branch: BRANCH }, eng.STDERR));
|
|
1500
|
-
spatialBboxParams = (bbox) => {
|
|
1501
|
-
const minX = bbox.minX ?? bbox.minLng, minY = bbox.minY ?? bbox.minLat, maxX = bbox.maxX ?? bbox.maxLng, maxY = bbox.maxY ?? bbox.maxLat;
|
|
1502
|
-
return { minX, minY, maxX, maxY, minLng: minX, minLat: minY, maxLng: maxX, maxLat: maxY };
|
|
1503
|
-
};
|
|
1504
|
-
spatialWithin = (eng, ws, spatialId, bbox, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "spatial", spatialId, ...spatialBboxParams(bbox) }), principal, branch: BRANCH }, eng.STDERR));
|
|
1505
|
-
extremum = (eng, ws, extremumId, kind, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "extremum", extremumId, kind, groupKey: groupKey ?? "" }), principal, branch: BRANCH }, eng.STDERR));
|
|
1506
|
-
nomosActive = (eng, ws) => qById(eng, ws, `domain-installation:${eng.hashes.nomos}`).length > 0;
|
|
1507
|
-
refusedRefName = (offerId) => `refs/heads/refused/${String(offerId || "unknown").replace(/[^A-Za-z0-9._-]/g, "_")}`;
|
|
1508
|
-
}
|
|
1509
|
-
});
|
|
1510
|
-
|
|
1511
|
-
// ../cloud/dev-server/bootstrap.mjs
|
|
1512
|
-
var bootstrap_exports = {};
|
|
1513
|
-
__export(bootstrap_exports, {
|
|
1514
|
-
bootstrapPlatform: () => bootstrapPlatform,
|
|
1515
|
-
isTestMint: () => isTestMint
|
|
1516
|
-
});
|
|
1517
|
-
import { readFileSync } from "node:fs";
|
|
1518
|
-
function isTestMint(eng) {
|
|
1519
|
-
try {
|
|
1520
|
-
setTestKRoot(eng, "");
|
|
1521
|
-
return true;
|
|
1522
|
-
} catch (e) {
|
|
1523
|
-
return !/poisoned|unknown|adapter-visible API/.test(String(e && e.message || e));
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
async function bootstrapPlatform(dev, opts) {
|
|
1527
|
-
const { eng, custody, ledgerFor, serialize, origin } = dev;
|
|
1528
|
-
const platform = opts.platform || "co2-platform-local";
|
|
1529
|
-
log2(`standing up platform '${platform}' on ${origin}`);
|
|
1530
|
-
await serialize(async () => {
|
|
1531
|
-
custody.create(`holon-${platform}`);
|
|
1532
|
-
const out = await boot(eng, platform, ledgerFor(platform));
|
|
1533
|
-
log2(`platform workspace boot: ${out.status} (head ${String(out.head || "").slice(0, 12)})`);
|
|
1534
|
-
});
|
|
1535
|
-
if (opts.deploy) {
|
|
1536
|
-
const body = JSON.parse(readFileSync(opts.deploy, "utf8"));
|
|
1537
|
-
const usda = body.packageUsda || body.usda;
|
|
1538
|
-
if (!usda) {
|
|
1539
|
-
log2(`WARN --deploy ${opts.deploy} has no packageUsda; skipping law deploy`);
|
|
1540
|
-
} else {
|
|
1541
|
-
await serialize(async () => {
|
|
1542
|
-
const ledger = ledgerFor(platform);
|
|
1543
|
-
const domainHash = await sha256hex(usda);
|
|
1544
|
-
try {
|
|
1545
|
-
const out = await deploy(eng, platform, ledger, usda, domainHash, body.dispositions || null, { actor: "user:dev-admin" });
|
|
1546
|
-
log2(`platform law deployed: ${domainHash.slice(0, 12)} (head ${String(out.head).slice(0, 12)})`);
|
|
1547
|
-
} catch (e) {
|
|
1548
|
-
log2(`WARN platform law deploy refused: ${String(e && e.message || e).slice(0, 200)}`);
|
|
1549
|
-
}
|
|
1550
|
-
});
|
|
1551
|
-
}
|
|
1552
|
-
} else {
|
|
1553
|
-
log2("no --deploy given; platform runs framework law only (deploy your platform package to enable births)");
|
|
1554
|
-
}
|
|
1555
|
-
const testMint = isTestMint(eng);
|
|
1556
|
-
if (!testMint) {
|
|
1557
|
-
log2("ATTESTED BIRTHS: this kernel is NOT test-mint (set_test_k_root is poisoned).");
|
|
1558
|
-
log2(" \u2192 build one: ./git-holon/build_wasm.sh --release --features test-mint --bin wasm_git_holon");
|
|
1559
|
-
log2(" \u2192 re-run: NOMOS_LOCAL_WASM=<that.wasm> githolon cloud dev --platform " + platform + " --bootstrap");
|
|
1560
|
-
log2(" Without it the platform cannot roots-at-K_root-attest home births (LEG rejected by design).");
|
|
1561
|
-
} else {
|
|
1562
|
-
const kRoot = authorKeygen(eng);
|
|
1563
|
-
setTestKRoot(eng, kRoot.public);
|
|
1564
|
-
log2(`K_root ceremony: pinned LOCAL test K_root ${kRoot.public.slice(0, 16)}\u2026`);
|
|
1565
|
-
log2(" (deliver signed DelegationCerts via the platform's grant directives; see home_delegated_estate.smoke.mjs)");
|
|
1566
|
-
dev.testKRoot = kRoot;
|
|
1567
|
-
}
|
|
1568
|
-
if (opts.authProvider || opts.firebaseProject) {
|
|
1569
|
-
log2(`AuthProvider: ${opts.authProvider || `firebase:${opts.firebaseProject}`} \u2014 register it via the platform's setAuthProvider governance offer;`);
|
|
1570
|
-
log2(" the kernel verifies x-nomos-auth JWTs against the law-declared key (host stays blind).");
|
|
1571
|
-
}
|
|
1572
|
-
log2("bootstrap complete (verifiable local legs done; attested-birth + Firebase legs gated as noted above).");
|
|
1573
|
-
return { platform, testMint };
|
|
1574
|
-
}
|
|
1575
|
-
var log2;
|
|
1576
|
-
var init_bootstrap = __esm({
|
|
1577
|
-
"../cloud/dev-server/bootstrap.mjs"() {
|
|
1578
|
-
init_engine();
|
|
1579
|
-
log2 = (m) => console.log(`[bootstrap] ${m}`);
|
|
1580
|
-
}
|
|
1581
|
-
});
|
|
1582
|
-
|
|
1583
|
-
// ../cloud/dev-server/server.mjs
|
|
1584
|
-
init_engine();
|
|
1585
|
-
init_custody_transport();
|
|
1586
|
-
import http from "node:http";
|
|
1587
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
1588
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, mkdtempSync } from "node:fs";
|
|
1589
|
-
import { fileURLToPath } from "node:url";
|
|
1590
|
-
import path from "node:path";
|
|
1591
|
-
import os from "node:os";
|
|
1592
|
-
|
|
1593
|
-
// ../cloud/cloudflare-shell/src/gateway-route-resolver.mjs
|
|
1594
|
-
function aliasIdsFor(x, path2) {
|
|
1595
|
-
const ids = [];
|
|
1596
|
-
for (const key of ["directiveId", "queryId", "countId", "sumId", "extremumId", "spatialId"]) {
|
|
1597
|
-
const value = x && x[key];
|
|
1598
|
-
if (typeof value === "string" && value) ids.push(value);
|
|
1599
|
-
}
|
|
1600
|
-
const leaf = String(path2 || "").replace(/^\/+/, "").split("/").filter(Boolean).at(-1);
|
|
1601
|
-
if (leaf) ids.push(leaf);
|
|
1602
|
-
return new Set(ids);
|
|
1603
|
-
}
|
|
1604
|
-
function resolveGatewayOperation(spec, opName, method) {
|
|
1605
|
-
if (!spec || !spec.paths || !opName || !method) return { op: null };
|
|
1606
|
-
const normalizedMethod = String(method).toLowerCase();
|
|
1607
|
-
const exactPath = `/${String(opName).replace(/^\/+/, "")}`;
|
|
1608
|
-
const exact = spec.paths[exactPath]?.[normalizedMethod];
|
|
1609
|
-
if (exact) return { op: exact, path: exactPath, ambiguous: null };
|
|
1610
|
-
if (String(opName).includes("/")) return { op: null };
|
|
1611
|
-
const matches = [];
|
|
1612
|
-
for (const [path2, methods] of Object.entries(spec.paths)) {
|
|
1613
|
-
const op = methods?.[normalizedMethod];
|
|
1614
|
-
const x = op?.["x-nomos"];
|
|
1615
|
-
if (!x) continue;
|
|
1616
|
-
if (aliasIdsFor(x, path2).has(opName)) matches.push({ op, path: path2 });
|
|
1617
|
-
}
|
|
1618
|
-
if (matches.length === 1) return { ...matches[0], ambiguous: null };
|
|
1619
|
-
if (matches.length > 1) {
|
|
1620
|
-
return { op: null, ambiguous: matches.map((m) => m.path).sort() };
|
|
1621
|
-
}
|
|
1622
|
-
return { op: null, ambiguous: null };
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
// ../cloud/cloudflare-shell/src/staged-intents.mjs
|
|
1626
|
-
var STAGE_THRESHOLD_CHARS = 4 * 1024 * 1024;
|
|
1627
|
-
var MAX_INLINE_INTENT_CHARS = 8 * 1024 * 1024;
|
|
1628
|
-
var MAX_STAGED_INTENT_BYTES = 64 * 1024 * 1024;
|
|
1629
|
-
var stagedIntentKey = (sha256) => typeof sha256 === "string" && /^[0-9a-f]{64}$/.test(sha256) ? `staged-intents/${sha256}` : null;
|
|
1630
|
-
async function sha256hexBytes(bytes) {
|
|
1631
|
-
const d = await crypto.subtle.digest("SHA-256", bytes);
|
|
1632
|
-
return [...new Uint8Array(d)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
1633
|
-
}
|
|
1634
|
-
var b64OfBytes = (bytes) => {
|
|
1635
|
-
let s = "";
|
|
1636
|
-
for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode.apply(null, bytes.subarray(i, i + 32768));
|
|
1637
|
-
return btoa(s);
|
|
1638
|
-
};
|
|
1639
|
-
async function stagePut(bucket, sha256, bytes) {
|
|
1640
|
-
const key = stagedIntentKey(sha256);
|
|
1641
|
-
if (key === null) return { status: 400, body: { ok: false, error: "stage-bad-ref: the path must name sha256 as 64 lowercase hex" } };
|
|
1642
|
-
if (!(bytes instanceof Uint8Array) || bytes.length === 0) return { status: 400, body: { ok: false, error: "stage-empty: PUT a non-empty raw body (the sealed intent bytes)" } };
|
|
1643
|
-
if (bytes.length > MAX_STAGED_INTENT_BYTES) return { status: 413, body: { ok: false, error: `stage-too-large: staged bytes are capped at ${MAX_STAGED_INTENT_BYTES} bytes` } };
|
|
1644
|
-
const got = await sha256hexBytes(bytes);
|
|
1645
|
-
if (got !== sha256) return { status: 400, body: { ok: false, error: `stage-hash-mismatch: sha256(body) is ${got}, not the named ${sha256} \u2014 stage bytes under their own hash` } };
|
|
1646
|
-
const existing = typeof bucket.head === "function" ? await bucket.head(key) : await bucket.get(key);
|
|
1647
|
-
if (existing) return { status: 200, body: { ok: true, sha256, size: bytes.length, alreadyStaged: true } };
|
|
1648
|
-
await bucket.put(key, bytes);
|
|
1649
|
-
return { status: 200, body: { ok: true, sha256, size: bytes.length, staged: true } };
|
|
1650
|
-
}
|
|
1651
|
-
async function resolveStagedIntent(bucket, ref) {
|
|
1652
|
-
const sha256 = ref && typeof ref === "object" && typeof ref.sha256 === "string" ? ref.sha256 : "";
|
|
1653
|
-
const key = stagedIntentKey(sha256);
|
|
1654
|
-
if (key === null) return { ok: false, status: 400, error: "staged-intent-bad-ref: intentRef must be { sha256: <64 lowercase hex>, size? }" };
|
|
1655
|
-
const obj = await bucket.get(key);
|
|
1656
|
-
if (!obj) return { ok: false, status: 422, error: `staged-intent-missing: no staged bytes for sha256 ${sha256} \u2014 PUT /v2/stage/${sha256} (raw sealed intent bytes) first, then re-offer { intentRef }` };
|
|
1657
|
-
const bytes = new Uint8Array(await obj.arrayBuffer());
|
|
1658
|
-
if (bytes.length > MAX_STAGED_INTENT_BYTES) return { ok: false, status: 413, error: `staged-intent-too-large: staged bytes exceed the ${MAX_STAGED_INTENT_BYTES}-byte cap` };
|
|
1659
|
-
if (typeof ref.size === "number" && ref.size !== bytes.length) return { ok: false, status: 422, error: `staged-intent-size-mismatch: the ref names ${ref.size} bytes but ${bytes.length} are staged` };
|
|
1660
|
-
const got = await sha256hexBytes(bytes);
|
|
1661
|
-
if (got !== sha256) return { ok: false, status: 422, error: `staged-intent-hash-mismatch: staged bytes hash to ${got}, not the offered ${sha256} \u2014 the signed offer's hash is the binding; refuse` };
|
|
1662
|
-
return { ok: true, intentB64: b64OfBytes(bytes), sha256, size: bytes.length };
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
// ../cloud/dev-server/custody.mjs
|
|
1666
|
-
import { spawn, execFileSync } from "node:child_process";
|
|
1667
|
-
import { existsSync, mkdirSync, rmSync, readdirSync } from "node:fs";
|
|
1668
|
-
import { join } from "node:path";
|
|
1669
|
-
var GIT_HTTP_BACKEND = join(
|
|
1670
|
-
execFileSync("git", ["--exec-path"], { encoding: "utf8" }).trim(),
|
|
1671
|
-
"git-http-backend"
|
|
1672
|
-
);
|
|
1673
|
-
var FilesystemGitRepository = class {
|
|
1674
|
-
constructor(name, dir, origin) {
|
|
1675
|
-
this.name = name;
|
|
1676
|
-
this.dir = dir;
|
|
1677
|
-
this.remote = `${origin}/git/nomos/${name}.git`;
|
|
1678
|
-
}
|
|
1679
|
-
// Dev custody is open (git-http-backend export-all): a token is inert but we keep the
|
|
1680
|
-
// production SHAPE so callers that thread `createToken(...).plaintext` are unchanged.
|
|
1681
|
-
createToken(_scope = "read", ttlSec = 3600) {
|
|
1682
|
-
return { plaintext: "dev-inert-token", expiresAt: Date.now() + ttlSec * 1e3 };
|
|
1683
|
-
}
|
|
1684
|
-
};
|
|
1685
|
-
var FilesystemGitCustody = class {
|
|
1686
|
-
/** @param {string} dataDir the --data root; repos live under <dataDir>/custody */
|
|
1687
|
-
constructor(dataDir, origin) {
|
|
1688
|
-
this.root = join(dataDir, "custody");
|
|
1689
|
-
this.origin = origin;
|
|
1690
|
-
mkdirSync(this.root, { recursive: true });
|
|
1691
|
-
}
|
|
1692
|
-
_dirOf(name) {
|
|
1693
|
-
return join(this.root, `${name}.git`);
|
|
1694
|
-
}
|
|
1695
|
-
get(name) {
|
|
1696
|
-
const dir = this._dirOf(name);
|
|
1697
|
-
return existsSync(dir) ? new FilesystemGitRepository(name, dir, this.origin) : null;
|
|
1698
|
-
}
|
|
1699
|
-
/** Idempotent: init a bare repo (with http.receivepack so the client can push). */
|
|
1700
|
-
create(name) {
|
|
1701
|
-
const dir = this._dirOf(name);
|
|
1702
|
-
if (!existsSync(dir)) {
|
|
1703
|
-
mkdirSync(dir, { recursive: true });
|
|
1704
|
-
execFileSync("git", ["init", "--bare", "--quiet", dir]);
|
|
1705
|
-
execFileSync("git", ["--git-dir", dir, "config", "http.receivepack", "true"]);
|
|
1706
|
-
execFileSync("git", ["--git-dir", dir, "config", "http.uploadpack", "true"]);
|
|
1707
|
-
}
|
|
1708
|
-
return new FilesystemGitRepository(name, dir, this.origin);
|
|
1709
|
-
}
|
|
1710
|
-
delete(name) {
|
|
1711
|
-
const dir = this._dirOf(name);
|
|
1712
|
-
if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
|
|
1713
|
-
}
|
|
1714
|
-
list() {
|
|
1715
|
-
if (!existsSync(this.root)) return [];
|
|
1716
|
-
return readdirSync(this.root).filter((f) => f.endsWith(".git")).map((f) => f.slice(0, -4));
|
|
1717
|
-
}
|
|
1718
|
-
};
|
|
1719
|
-
function runGitHttpBackend(projectRoot, { method, pathInfo, query: query2, contentType, body }) {
|
|
1720
|
-
return new Promise((resolve, reject) => {
|
|
1721
|
-
const env = {
|
|
1722
|
-
...process.env,
|
|
1723
|
-
GIT_PROJECT_ROOT: projectRoot,
|
|
1724
|
-
GIT_HTTP_EXPORT_ALL: "1",
|
|
1725
|
-
PATH_INFO: pathInfo,
|
|
1726
|
-
REQUEST_METHOD: method,
|
|
1727
|
-
QUERY_STRING: query2 || "",
|
|
1728
|
-
CONTENT_TYPE: contentType || "",
|
|
1729
|
-
CONTENT_LENGTH: String(body ? body.length : 0),
|
|
1730
|
-
REMOTE_ADDR: "127.0.0.1",
|
|
1731
|
-
REMOTE_USER: "nomos-dev",
|
|
1732
|
-
GIT_HTTP_MAX_REQUEST_BUFFER: String(64 * 1024 * 1024)
|
|
1733
|
-
};
|
|
1734
|
-
const cgi = spawn(GIT_HTTP_BACKEND, [], { env });
|
|
1735
|
-
const chunks = [];
|
|
1736
|
-
cgi.stdout.on("data", (d) => chunks.push(d));
|
|
1737
|
-
cgi.stderr.on("data", () => {
|
|
1738
|
-
});
|
|
1739
|
-
cgi.on("error", reject);
|
|
1740
|
-
cgi.on("close", () => {
|
|
1741
|
-
const out = Buffer.concat(chunks);
|
|
1742
|
-
let sep = out.indexOf("\r\n\r\n");
|
|
1743
|
-
let sepLen = 4;
|
|
1744
|
-
if (sep < 0) {
|
|
1745
|
-
sep = out.indexOf("\n\n");
|
|
1746
|
-
sepLen = 2;
|
|
1747
|
-
}
|
|
1748
|
-
const headerText = sep >= 0 ? out.slice(0, sep).toString("utf8") : out.toString("utf8");
|
|
1749
|
-
const payload = sep >= 0 ? out.slice(sep + sepLen) : Buffer.alloc(0);
|
|
1750
|
-
const headers = {};
|
|
1751
|
-
let status = 200;
|
|
1752
|
-
for (const line of headerText.split(/\r?\n/)) {
|
|
1753
|
-
const i = line.indexOf(":");
|
|
1754
|
-
if (i < 0) continue;
|
|
1755
|
-
const k = line.slice(0, i).trim();
|
|
1756
|
-
const v = line.slice(i + 1).trim();
|
|
1757
|
-
if (/^status$/i.test(k)) {
|
|
1758
|
-
status = parseInt(v, 10) || 200;
|
|
1759
|
-
continue;
|
|
1760
|
-
}
|
|
1761
|
-
headers[k] = v;
|
|
1762
|
-
}
|
|
1763
|
-
resolve({ status, headers, body: payload });
|
|
1764
|
-
});
|
|
1765
|
-
if (body && body.length) cgi.stdin.write(body);
|
|
1766
|
-
cgi.stdin.end();
|
|
1767
|
-
});
|
|
1768
|
-
}
|
|
1769
|
-
function receivePackCommands(raw) {
|
|
1770
|
-
const cmds = [];
|
|
1771
|
-
let off = 0;
|
|
1772
|
-
const dec4 = new TextDecoder();
|
|
1773
|
-
while (off + 4 <= raw.length) {
|
|
1774
|
-
const hex = dec4.decode(raw.subarray(off, off + 4));
|
|
1775
|
-
if (!/^[0-9a-f]{4}$/.test(hex)) break;
|
|
1776
|
-
const len = parseInt(hex, 16);
|
|
1777
|
-
if (len === 0) {
|
|
1778
|
-
off += 4;
|
|
1779
|
-
break;
|
|
1780
|
-
}
|
|
1781
|
-
let line = dec4.decode(raw.subarray(off + 4, off + len));
|
|
1782
|
-
off += len;
|
|
1783
|
-
const nul = line.indexOf("\0");
|
|
1784
|
-
if (nul >= 0) line = line.slice(0, nul);
|
|
1785
|
-
const m = line.match(/^([0-9a-f]{40}) ([0-9a-f]{40}) (.+?)\s*$/);
|
|
1786
|
-
if (m) cmds.push({ oldOid: m[1], newOid: m[2], ref: m[3] });
|
|
1787
|
-
}
|
|
1788
|
-
return cmds;
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
// ../cloud/dev-server/server.mjs
|
|
1792
|
-
var DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
1793
|
-
var REPO_ROOT = path.resolve(DIR, "../..");
|
|
1794
|
-
var RECOGNIZED_UNSUPPORTED = /* @__PURE__ */ new Set([
|
|
1795
|
-
"container-status",
|
|
1796
|
-
"recycle-container",
|
|
1797
|
-
"hostnames",
|
|
1798
|
-
"rebirth-custody",
|
|
1799
|
-
"upload-begin",
|
|
1800
|
-
"upload-gate",
|
|
1801
|
-
"upload-verify",
|
|
1802
|
-
"drain-reclaims",
|
|
1803
|
-
"drain-rebirths",
|
|
1804
|
-
"git-read-lease",
|
|
1805
|
-
"poke-admit",
|
|
1806
|
-
"clear-checkpoint",
|
|
1807
|
-
"domain-package",
|
|
1808
|
-
"capabilities"
|
|
1809
|
-
]);
|
|
1810
|
-
var UNSUPPORTED_WHY = {
|
|
1811
|
-
"container-status": "the heavy container tier is Cloudflare-only (the dev cloud is one in-process engine)",
|
|
1812
|
-
"recycle-container": "the heavy container tier is Cloudflare-only (the dev cloud is one in-process engine)",
|
|
1813
|
-
"hostnames": "platform custom-hostname routing is a multi-tenant edge concern",
|
|
1814
|
-
"rebirth-custody": "the custodian rebirth lane is bound to Cloudflare Artifacts custody",
|
|
1815
|
-
"upload-begin": "the staged-upload birth variant \u2014 push to /v2/workspaces/<ws>/git instead (the upload-birth lane works locally)",
|
|
1816
|
-
"upload-gate": "the staged-upload birth variant \u2014 push to /v2/workspaces/<ws>/git instead",
|
|
1817
|
-
"upload-verify": "the staged-upload birth variant \u2014 push to /v2/workspaces/<ws>/git instead",
|
|
1818
|
-
"drain-reclaims": "an operator lawful-recovery drain (multi-node lifecycle)",
|
|
1819
|
-
"drain-rebirths": "an operator lawful-recovery drain (multi-node lifecycle)",
|
|
1820
|
-
"capabilities": "retired everywhere \u2014 host-side capability storage is gone (capabilities are holon-offered obligations now)"
|
|
1821
|
-
};
|
|
1822
|
-
var _realFetch = globalThis.fetch;
|
|
1823
|
-
globalThis.fetch = (input, init) => {
|
|
1824
|
-
const u = typeof input === "string" ? input : input && input.url || "";
|
|
1825
|
-
if (/artifacts\.cloudflare\.net|\.workers\.dev/i.test(u)) {
|
|
1826
|
-
throw new Error(`REMOTE CUSTODY FORBIDDEN in dev cloud: refused fetch to ${u}`);
|
|
1827
|
-
}
|
|
1828
|
-
return _realFetch(input, init);
|
|
1829
|
-
};
|
|
1830
|
-
var unb64 = (s) => new Uint8Array(Buffer.from(s, "base64"));
|
|
1831
|
-
function configDir() {
|
|
1832
|
-
return process.env.HOLON_CONFIG_DIR || path.join(os.homedir(), ".holon");
|
|
1833
|
-
}
|
|
1834
|
-
function firstExisting(...paths) {
|
|
1835
|
-
for (const p of paths) {
|
|
1836
|
-
if (p && existsSync2(p)) return p;
|
|
1837
|
-
}
|
|
1838
|
-
return null;
|
|
1839
|
-
}
|
|
1840
|
-
var TEST_MINT_MARKER = Buffer.from("set_test_k_root");
|
|
1841
|
-
var isTestMintBytes = (bytes) => bytes.includes(TEST_MINT_MARKER);
|
|
1842
|
-
var DEFAULT_KERNEL_SOURCE = "https://nomos.captainapp.co.uk";
|
|
1843
|
-
async function resolveTestMintWasm(kernelSource) {
|
|
1844
|
-
const runtimeCache = path.join(configDir(), "runtime");
|
|
1845
|
-
const cachePath = path.join(runtimeCache, "holon-testmint.wasm");
|
|
1846
|
-
const local = process.env.NOMOS_LOCAL_WASM;
|
|
1847
|
-
if (local && existsSync2(local)) {
|
|
1848
|
-
const bytes = readFileSync2(local);
|
|
1849
|
-
if (isTestMintBytes(bytes)) return { wasmPath: local, wasmBytes: bytes, kernelKind: "test-mint", kernelSource: "NOMOS_LOCAL_WASM" };
|
|
1850
|
-
}
|
|
1851
|
-
if (existsSync2(cachePath)) {
|
|
1852
|
-
const bytes = readFileSync2(cachePath);
|
|
1853
|
-
if (isTestMintBytes(bytes)) return { wasmPath: cachePath, wasmBytes: bytes, kernelKind: "test-mint", kernelSource: "cache" };
|
|
1854
|
-
}
|
|
1855
|
-
const src = String(kernelSource || process.env.NOMOS_KERNEL_SOURCE || DEFAULT_KERNEL_SOURCE).replace(/\/+$/, "");
|
|
1856
|
-
const url = `${src}/v1/runtime/holon-testmint.wasm`;
|
|
1857
|
-
try {
|
|
1858
|
-
const resp = await _realFetch(url);
|
|
1859
|
-
if (resp.ok) {
|
|
1860
|
-
const bytes = Buffer.from(await resp.arrayBuffer());
|
|
1861
|
-
if (isTestMintBytes(bytes)) {
|
|
1862
|
-
mkdirSync2(runtimeCache, { recursive: true });
|
|
1863
|
-
writeFileSync(cachePath, bytes);
|
|
1864
|
-
return { wasmPath: cachePath, wasmBytes: bytes, kernelKind: "test-mint", kernelSource: `fetched ${url}` };
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
} catch {
|
|
1868
|
-
}
|
|
1869
|
-
throw new Error(
|
|
1870
|
-
`attested births need the test-mint kernel; it couldn't be fetched from ${src} (offline?) \u2014 build one: git-holon/build_wasm.sh --release --features test-mint --bin wasm_git_holon and set NOMOS_LOCAL_WASM`
|
|
1871
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
async function loadRuntime(opts = {}) {
|
|
1874
|
-
const runtimeCache = path.join(configDir(), "runtime");
|
|
1875
|
-
const repoDomainsDir = path.join(REPO_ROOT, "cloud/cloudflare-shell/container/domains");
|
|
1876
|
-
const vendoredFrameworkDir = path.join(DIR, "framework");
|
|
1877
|
-
let wasmPath, wasmBytes, kernelKind, kernelSource;
|
|
1878
|
-
if (opts.attested) {
|
|
1879
|
-
({ wasmPath, wasmBytes, kernelKind, kernelSource } = await resolveTestMintWasm(opts.kernelSource));
|
|
1880
|
-
} else {
|
|
1881
|
-
wasmPath = firstExisting(
|
|
1882
|
-
process.env.NOMOS_LOCAL_WASM,
|
|
1883
|
-
path.join(REPO_ROOT, "cloud/cloudflare-shell/container/vendor/wasm_git_holon.wasm"),
|
|
1884
|
-
path.join(runtimeCache, "holon.wasm")
|
|
1885
|
-
);
|
|
1886
|
-
if (!wasmPath) {
|
|
1887
|
-
throw new Error(
|
|
1888
|
-
"no kernel wasm found. Warm the kernel first:\n \u2022 set NOMOS_LOCAL_WASM=<path to holon.wasm>, OR\n \u2022 run any `githolon` cloud command once (e.g. `githolon ledger verify \u2026`) to cache ~/.holon/runtime/holon.wasm\n (attested births additionally require a test-mint wasm \u2014 pass --bootstrap or --attested)"
|
|
1889
|
-
);
|
|
1890
|
-
}
|
|
1891
|
-
wasmBytes = readFileSync2(wasmPath);
|
|
1892
|
-
kernelKind = isTestMintBytes(wasmBytes) ? "test-mint" : "production";
|
|
1893
|
-
kernelSource = wasmPath === process.env.NOMOS_LOCAL_WASM ? "NOMOS_LOCAL_WASM" : wasmPath.startsWith(runtimeCache) ? "cache" : "repo-vendor";
|
|
1894
|
-
}
|
|
1895
|
-
const repoBootstrap = firstExisting(path.join(repoDomainsDir, "domain_bootstrap.package.usda"));
|
|
1896
|
-
const repoNomos = firstExisting(path.join(repoDomainsDir, "domain_nomos.package.usda"));
|
|
1897
|
-
let bootstrapPkg, nomosPkg;
|
|
1898
|
-
if (repoBootstrap && repoNomos) {
|
|
1899
|
-
bootstrapPkg = readFileSync2(repoBootstrap, "utf8");
|
|
1900
|
-
nomosPkg = readFileSync2(repoNomos, "utf8");
|
|
1901
|
-
} else if (existsSync2(path.join(vendoredFrameworkDir, "domain_bootstrap.package.usda"))) {
|
|
1902
|
-
bootstrapPkg = readFileSync2(path.join(vendoredFrameworkDir, "domain_bootstrap.package.usda"), "utf8");
|
|
1903
|
-
nomosPkg = readFileSync2(path.join(vendoredFrameworkDir, "domain_nomos.package.usda"), "utf8");
|
|
1904
|
-
} else {
|
|
1905
|
-
const pkgsCache = path.join(runtimeCache, "packages.json");
|
|
1906
|
-
if (!existsSync2(pkgsCache)) {
|
|
1907
|
-
throw new Error(
|
|
1908
|
-
"no framework packages found (domain_bootstrap/domain_nomos). Warm the runtime cache by running any `githolon` cloud command once, or run from the monorepo checkout."
|
|
1909
|
-
);
|
|
1910
|
-
}
|
|
1911
|
-
const pkgs = JSON.parse(readFileSync2(pkgsCache, "utf8"));
|
|
1912
|
-
bootstrapPkg = pkgs.bootstrap;
|
|
1913
|
-
nomosPkg = pkgs.nomos;
|
|
1914
|
-
}
|
|
1915
|
-
return { wasmPath, wasmBytes, bootstrapPkg, nomosPkg, kernelKind, kernelSource };
|
|
1916
|
-
}
|
|
1917
|
-
async function startDevCloud(opts = {}) {
|
|
1918
|
-
const port = opts.port || 8787;
|
|
1919
|
-
const host = opts.host || "127.0.0.1";
|
|
1920
|
-
const origin = `http://${host}:${port}`;
|
|
1921
|
-
const ephemeral = !!opts.ephemeral;
|
|
1922
|
-
const dataDir = ephemeral ? mkdtempSync(path.join(os.tmpdir(), "nomos-dev-cloud-")) : path.resolve(opts.data || ".nomos/dev-cloud");
|
|
1923
|
-
mkdirSync2(dataDir, { recursive: true });
|
|
1924
|
-
const attested = !!(opts.attested || opts.bootstrap);
|
|
1925
|
-
const { wasmPath, wasmBytes, bootstrapPkg, nomosPkg, kernelKind, kernelSource } = await loadRuntime({ attested, kernelSource: opts.kernelSource });
|
|
1926
|
-
console.log(`[dev-cloud] kernel : ${kernelKind} (from ${kernelSource})`);
|
|
1927
|
-
const wasmModule = await WebAssembly.compile(wasmBytes);
|
|
1928
|
-
const kernelSha = createHash("sha256").update(wasmBytes).digest("hex");
|
|
1929
|
-
const enc4 = new TextEncoder();
|
|
1930
|
-
const replicaDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", enc4.encode(`dev:${randomBytes(8).toString("hex")}`)));
|
|
1931
|
-
let replica = 0n;
|
|
1932
|
-
for (let i = 0; i < 8; i++) replica = replica << 8n | BigInt(replicaDigest[i]);
|
|
1933
|
-
replica &= (1n << 63n) - 1n;
|
|
1934
|
-
const eng = await createEngine({ wasmModule, bootstrapPkg, nomosPkg, replica, installedBy: "nomos-cloud-dev" });
|
|
1935
|
-
const custody = new FilesystemGitCustody(dataDir, origin);
|
|
1936
|
-
const stagedBytes = /* @__PURE__ */ new Map();
|
|
1937
|
-
const stageBucket = {
|
|
1938
|
-
async get(k) {
|
|
1939
|
-
const b = stagedBytes.get(k);
|
|
1940
|
-
return b ? { arrayBuffer: async () => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength) } : null;
|
|
1941
|
-
},
|
|
1942
|
-
async put(k, bytes) {
|
|
1943
|
-
stagedBytes.set(k, bytes);
|
|
1944
|
-
},
|
|
1945
|
-
async head(k) {
|
|
1946
|
-
const b = stagedBytes.get(k);
|
|
1947
|
-
return b ? { size: b.length } : null;
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
let chain = Promise.resolve();
|
|
1951
|
-
const serialize = (fn) => {
|
|
1952
|
-
const r = chain.then(fn, fn);
|
|
1953
|
-
chain = r.then(() => {
|
|
1954
|
-
}, () => {
|
|
1955
|
-
});
|
|
1956
|
-
return r;
|
|
1957
|
-
};
|
|
1958
|
-
const pushChains = /* @__PURE__ */ new Map();
|
|
1959
|
-
const pushLaneFor = (ws) => (fn) => {
|
|
1960
|
-
const prev = pushChains.get(ws) || Promise.resolve();
|
|
1961
|
-
const r = prev.then(fn, fn);
|
|
1962
|
-
pushChains.set(ws, r.then(() => {
|
|
1963
|
-
}, () => {
|
|
1964
|
-
}));
|
|
1965
|
-
return r;
|
|
1966
|
-
};
|
|
1967
|
-
const ledgerFor = (ws) => ({
|
|
1968
|
-
remote: `${origin}/git/nomos/holon-${ws}.git`,
|
|
1969
|
-
headers: {},
|
|
1970
|
-
pushLane: pushLaneFor(ws)
|
|
1971
|
-
});
|
|
1972
|
-
async function persistBorn(born) {
|
|
1973
|
-
if (!Array.isArray(born)) return [];
|
|
1974
|
-
const out = [];
|
|
1975
|
-
for (const b of born) {
|
|
1976
|
-
const child = b && b.workspace;
|
|
1977
|
-
const head = b && b.head;
|
|
1978
|
-
out.push({ workspace: child, head });
|
|
1979
|
-
if (!child || !/^[0-9a-f]{40}$/i.test(head || "") || !b.packB64) continue;
|
|
1980
|
-
const repo = custody.create(`holon-${child}`);
|
|
1981
|
-
const refs = await lsRefs(repo.remote, {}, { service: "git-receive-pack", prefix: "refs/heads/main" }).catch(() => ({}));
|
|
1982
|
-
const oldOid = refs["refs/heads/main"] || ZERO_OID;
|
|
1983
|
-
if (oldOid === head) continue;
|
|
1984
|
-
await pushLaneFor(child)(
|
|
1985
|
-
() => pushPack(repo.remote, {}, { updates: [{ ref: "refs/heads/main", oldOid, newOid: head }], pack: unb64(b.packB64) })
|
|
1986
|
-
);
|
|
1987
|
-
}
|
|
1988
|
-
return out;
|
|
1989
|
-
}
|
|
1990
|
-
const CORS = {
|
|
1991
|
-
"access-control-allow-origin": "*",
|
|
1992
|
-
"access-control-allow-methods": "GET,POST,PUT,DELETE,OPTIONS",
|
|
1993
|
-
"access-control-allow-headers": "content-type,x-nomos-auth,x-nomos-principal,authorization"
|
|
1994
|
-
};
|
|
1995
|
-
const sendJson = (res, obj, status = 200) => {
|
|
1996
|
-
const body = JSON.stringify(obj, null, 2) + "\n";
|
|
1997
|
-
res.writeHead(status, { "content-type": "application/json", ...CORS });
|
|
1998
|
-
res.end(body);
|
|
1999
|
-
};
|
|
2000
|
-
const sendBytes = (res, bytes, status = 200, headers = {}) => {
|
|
2001
|
-
res.writeHead(status, { ...CORS, ...headers });
|
|
2002
|
-
res.end(Buffer.from(bytes));
|
|
2003
|
-
};
|
|
2004
|
-
const readBody = (req) => new Promise((resolve) => {
|
|
2005
|
-
const chunks = [];
|
|
2006
|
-
req.on("data", (c) => chunks.push(c));
|
|
2007
|
-
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
2008
|
-
});
|
|
2009
|
-
const principalOf = (req) => String(req.headers["x-nomos-principal"] || "").slice(0, 128);
|
|
2010
|
-
const authTokenOf = (req) => String(req.headers["x-nomos-auth"] || "").replace(/^Bearer\s+/i, "");
|
|
2011
|
-
function openapiFor(ws) {
|
|
2012
|
-
const merged = { openapi: "3.1.0", info: { title: `${ws} (nomos workspace)`, version: "0", "x-nomos-workspace": ws }, paths: {} };
|
|
2013
|
-
const domOf = (o) => String(o && o["x-nomos"] && o["x-nomos"].domain ? o["x-nomos"].domain : "domain");
|
|
2014
|
-
const mergeInto = (frag) => {
|
|
2015
|
-
for (const [p, methods] of Object.entries(frag.paths || {})) {
|
|
2016
|
-
for (const [m, op] of Object.entries(methods)) {
|
|
2017
|
-
const existing = merged.paths[p] && merged.paths[p][m];
|
|
2018
|
-
if (existing === void 0) {
|
|
2019
|
-
(merged.paths[p] ||= {})[m] = op;
|
|
2020
|
-
} else if (JSON.stringify(existing) !== JSON.stringify(op)) {
|
|
2021
|
-
(merged.paths[`/${domOf(existing)}${p}`] ||= {})[m] = existing;
|
|
2022
|
-
(merged.paths[`/${domOf(op)}${p}`] ||= {})[m] = op;
|
|
2023
|
-
delete merged.paths[p][m];
|
|
2024
|
-
if (Object.keys(merged.paths[p]).length === 0) delete merged.paths[p];
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
};
|
|
2029
|
-
try {
|
|
2030
|
-
const spec = describe(eng, ws);
|
|
2031
|
-
if (spec && spec.paths) mergeInto(spec);
|
|
2032
|
-
} catch {
|
|
2033
|
-
}
|
|
2034
|
-
merged.paths["/openapi.json"] = {
|
|
2035
|
-
get: { operationId: "nomos__openapi", summary: "this workspace's live OpenAPI", responses: { "200": { description: "ok" } } }
|
|
2036
|
-
};
|
|
2037
|
-
return merged;
|
|
2038
|
-
}
|
|
2039
|
-
const server = http.createServer(async (req, res) => {
|
|
2040
|
-
try {
|
|
2041
|
-
const url = new URL(req.url, origin);
|
|
2042
|
-
const seg = url.pathname.split("/").filter(Boolean);
|
|
2043
|
-
if (req.method === "OPTIONS") {
|
|
2044
|
-
res.writeHead(204, CORS);
|
|
2045
|
-
return res.end();
|
|
2046
|
-
}
|
|
2047
|
-
if (url.pathname === "/health") return sendJson(res, { ok: true, kernelSha, dataDir, wasmPath, workspaces: custody.list() });
|
|
2048
|
-
if (url.pathname === "/v1/runtime/holon.wasm" || url.pathname === "/v1/runtime/holon-testmint.wasm") {
|
|
2049
|
-
res.writeHead(200, { ...CORS, "content-type": "application/wasm", "cache-control": "no-store", "x-nomos-kernel": kernelKind, "x-nomos-kernel-sha": kernelSha });
|
|
2050
|
-
return res.end(Buffer.from(wasmBytes));
|
|
2051
|
-
}
|
|
2052
|
-
if (url.pathname === "/v1/runtime/packages") {
|
|
2053
|
-
return sendJson(res, { bootstrap: bootstrapPkg, nomos: nomosPkg });
|
|
2054
|
-
}
|
|
2055
|
-
{
|
|
2056
|
-
const m = url.pathname.match(/^\/v2\/stage\/([0-9a-f]{64})$/);
|
|
2057
|
-
if (m) {
|
|
2058
|
-
const sha = m[1];
|
|
2059
|
-
if (req.method === "PUT" || req.method === "POST") {
|
|
2060
|
-
const bytes = new Uint8Array(await readBody(req));
|
|
2061
|
-
const r = await stagePut(stageBucket, sha, bytes);
|
|
2062
|
-
return sendJson(res, r.body, r.status);
|
|
2063
|
-
}
|
|
2064
|
-
if (req.method === "GET" || req.method === "HEAD") {
|
|
2065
|
-
const head = await stageBucket.head(`staged-intents/${sha}`);
|
|
2066
|
-
if (!head) return sendJson(res, { ok: false, staged: false, sha256: sha }, 404);
|
|
2067
|
-
return sendJson(res, { ok: true, staged: true, sha256: sha, size: head.size }, 200);
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
if (seg[0] === "git" && seg[1] === "nomos") {
|
|
2072
|
-
return await gitBackend(req, res, seg.slice(2).join("/"), null);
|
|
2073
|
-
}
|
|
2074
|
-
if (seg[0] === "repositories" && seg[1]) {
|
|
2075
|
-
const name = decodeURIComponent(seg[1]);
|
|
2076
|
-
if (req.method === "PUT") {
|
|
2077
|
-
const r2 = custody.create(name);
|
|
2078
|
-
return sendJson(res, { ok: true, name, remote: r2.remote }, 201);
|
|
2079
|
-
}
|
|
2080
|
-
if (req.method === "DELETE") {
|
|
2081
|
-
custody.delete(name);
|
|
2082
|
-
return sendJson(res, { ok: true, name, deleted: true });
|
|
2083
|
-
}
|
|
2084
|
-
const r = custody.get(name);
|
|
2085
|
-
return r ? sendJson(res, { ok: true, name, remote: r.remote }) : sendJson(res, { ok: false, error: "no such repository" }, 404);
|
|
2086
|
-
}
|
|
2087
|
-
if (seg[0] === "v2" && seg[1] === "workspaces" && seg[2]) {
|
|
2088
|
-
const ws = decodeURIComponent(seg[2]);
|
|
2089
|
-
const sub = seg.slice(3);
|
|
2090
|
-
if (sub[0] === "git") {
|
|
2091
|
-
return await gitBackend(req, res, `holon-${ws}.git/${sub.slice(1).join("/")}`, ws);
|
|
2092
|
-
}
|
|
2093
|
-
if (sub.length === 0 && req.method === "POST") {
|
|
2094
|
-
return serialize(async () => {
|
|
2095
|
-
custody.create(`holon-${ws}`);
|
|
2096
|
-
const ledger = ledgerFor(ws);
|
|
2097
|
-
const out = await boot(eng, ws, ledger);
|
|
2098
|
-
sendJson(res, { ok: true, ws, tier: "heavy", status: out.status, ledger: ledger.remote, head: out.head, controller: { domainHash: out.controllerHash, rows: out.rows } }, out.status === "already-initialized" ? 200 : 201);
|
|
2099
|
-
});
|
|
2100
|
-
}
|
|
2101
|
-
if (sub.length === 0 && req.method === "GET") {
|
|
2102
|
-
return serialize(async () => {
|
|
2103
|
-
const ledger = ledgerFor(ws);
|
|
2104
|
-
let initialized = false, ledgerState = null;
|
|
2105
|
-
if (custody.get(`holon-${ws}`)) {
|
|
2106
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2107
|
-
try {
|
|
2108
|
-
initialized = nomosActive(eng, ws);
|
|
2109
|
-
} catch {
|
|
2110
|
-
}
|
|
2111
|
-
ledgerState = await ledgerStats(eng, ws, ledger).catch(() => null);
|
|
2112
|
-
}
|
|
2113
|
-
sendJson(res, { ok: true, ws, tier: "heavy", initialized, ledger: ledger.remote, ledgerState, kernelSha });
|
|
2114
|
-
});
|
|
2115
|
-
}
|
|
2116
|
-
if (sub[0] === "domains" && sub.length === 1) {
|
|
2117
|
-
if (req.method === "GET") {
|
|
2118
|
-
return serialize(async () => {
|
|
2119
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2120
|
-
sendJson(res, { ok: true, ws, ...installedLaw(eng, ws) });
|
|
2121
|
-
});
|
|
2122
|
-
}
|
|
2123
|
-
if (req.method === "POST") {
|
|
2124
|
-
const raw = await readBody(req);
|
|
2125
|
-
return serialize(async () => {
|
|
2126
|
-
const ledger = ledgerFor(ws);
|
|
2127
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2128
|
-
if (!nomosActive(eng, ws)) return sendJson(res, { ok: false, error: `workspace '${ws}' is not initialized \u2014 POST /v2/workspaces/${ws} first` }, 409);
|
|
2129
|
-
let usda, dispositions = null;
|
|
2130
|
-
const text = raw.toString("utf8");
|
|
2131
|
-
if (text.trimStart().startsWith("#usda")) {
|
|
2132
|
-
usda = text;
|
|
2133
|
-
} else {
|
|
2134
|
-
const body = JSON.parse(text || "{}");
|
|
2135
|
-
usda = body.packageUsda || body.usda;
|
|
2136
|
-
dispositions = body.dispositions || null;
|
|
2137
|
-
}
|
|
2138
|
-
if (!usda) return sendJson(res, { ok: false, error: "deploy body must be raw #usda or JSON with packageUsda" }, 400);
|
|
2139
|
-
const domainHash = await sha256hex(usda);
|
|
2140
|
-
const dryRun = url.searchParams.get("dryRun");
|
|
2141
|
-
if (dryRun === "evolve") {
|
|
2142
|
-
const verdict = evolveDryRun(eng, ws, usda, domainHash, dispositions);
|
|
2143
|
-
return sendJson(res, { ok: true, dryRun: "evolve", verdict });
|
|
2144
|
-
}
|
|
2145
|
-
if (dryRun) return sendJson(res, { ok: true, dryRun: true, wouldInstall: domainHash, bytes: usda.length });
|
|
2146
|
-
const actor = principalOf(req);
|
|
2147
|
-
const authToken = authTokenOf(req);
|
|
2148
|
-
try {
|
|
2149
|
-
const out = await deploy(eng, ws, ledger, usda, domainHash, dispositions, { actor, ...authToken ? { authToken } : {} });
|
|
2150
|
-
sendJson(res, { ok: true, ws, domainHash, head: out.head, installation: out.installation }, 201);
|
|
2151
|
-
} catch (e) {
|
|
2152
|
-
const msg = String(e && e.message || e);
|
|
2153
|
-
const status = /deploy-did-not-advance/.test(msg) ? 409 : 422;
|
|
2154
|
-
sendJson(res, { ok: false, refused: true, error: msg.slice(0, 600) }, status);
|
|
2155
|
-
}
|
|
2156
|
-
});
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
if (sub[0] === "domains" && sub.length === 3 && sub[2] === "package.usda") {
|
|
2160
|
-
return serialize(async () => {
|
|
2161
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2162
|
-
const pkg = domainPackage(eng, ws, sub[1]);
|
|
2163
|
-
if (!pkg || !pkg.usda) return sendJson(res, { ok: false, error: "no such package" }, 404);
|
|
2164
|
-
res.writeHead(200, { "content-type": "text/plain", ...CORS });
|
|
2165
|
-
res.end(pkg.usda);
|
|
2166
|
-
});
|
|
2167
|
-
}
|
|
2168
|
-
if (sub[0] === "openapi.json" && req.method === "GET") {
|
|
2169
|
-
return serialize(async () => {
|
|
2170
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2171
|
-
sendJson(res, openapiFor(ws));
|
|
2172
|
-
});
|
|
2173
|
-
}
|
|
2174
|
-
if (sub[0] === "admit" && req.method === "POST") {
|
|
2175
|
-
return serialize(async () => {
|
|
2176
|
-
const ledger = ledgerFor(ws);
|
|
2177
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2178
|
-
const out = await admitAll(eng, ws, ledger, {});
|
|
2179
|
-
if (Array.isArray(out.born) && out.born.length) await persistBorn(packBornChildren(eng, out.born));
|
|
2180
|
-
sendJson(res, { ok: true, ws, sessions: out.sessions, main: out.main, ...out.timings ? { timings: out.timings } : {} });
|
|
2181
|
-
});
|
|
2182
|
-
}
|
|
2183
|
-
if (sub[0] === "intent-offers" && req.method === "POST") {
|
|
2184
|
-
return serialize(async () => {
|
|
2185
|
-
const ledger = ledgerFor(ws);
|
|
2186
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2187
|
-
const body = JSON.parse((await readBody(req)).toString("utf8") || "{}");
|
|
2188
|
-
const offers = Array.isArray(body.offers) ? body.offers.slice(0, 100) : [];
|
|
2189
|
-
if (!offers.length) return sendJson(res, { ok: false, error: "body must be { offers:[{session, oid, intentB64}] }" }, 400);
|
|
2190
|
-
const clientBase = /^[0-9a-f]{40}$/i.test(body.base || "") ? body.base : null;
|
|
2191
|
-
const out = await admitIntentOffers(eng, ws, ledger, offers, { ...clientBase ? { clientBase } : {} });
|
|
2192
|
-
if (Array.isArray(out.born) && out.born.length) await persistBorn(out.born);
|
|
2193
|
-
const bornOut = Array.isArray(out.born) ? out.born.map(({ packB64, packBytes, ...b }) => b) : null;
|
|
2194
|
-
sendJson(res, {
|
|
2195
|
-
ok: true,
|
|
2196
|
-
ws,
|
|
2197
|
-
sessions: out.sessions,
|
|
2198
|
-
main: out.main,
|
|
2199
|
-
...bornOut ? { born: bornOut } : {},
|
|
2200
|
-
...out.birthOutcomes ? { birthOutcomes: out.birthOutcomes } : {},
|
|
2201
|
-
...out.mainDelta ? { mainDelta: out.mainDelta } : {},
|
|
2202
|
-
...out.resultPackB64 ? { resultPackB64: out.resultPackB64, resultBase: out.resultBase } : {},
|
|
2203
|
-
...out.deadLetters && out.deadLetters.length ? { deadLetters: out.deadLetters.map(({ blob, ...m }) => m) } : {}
|
|
2204
|
-
});
|
|
2205
|
-
});
|
|
2206
|
-
}
|
|
2207
|
-
if (sub[0] === "dead-letters" && sub.length === 1 && req.method === "GET") {
|
|
2208
|
-
return serialize(async () => {
|
|
2209
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2210
|
-
sendJson(res, { ok: true, ws, deadLetters: await listRefusedRefs(eng, ws) });
|
|
2211
|
-
});
|
|
2212
|
-
}
|
|
2213
|
-
if (sub[0] === "dlq-summary" && req.method === "GET") {
|
|
2214
|
-
return serialize(async () => {
|
|
2215
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2216
|
-
sendJson(res, { ok: true, ws, ...await refusedSummary(eng, ws) });
|
|
2217
|
-
});
|
|
2218
|
-
}
|
|
2219
|
-
if (sub[0] === "dead-letters" && sub[1] === "retry" && req.method === "POST") {
|
|
2220
|
-
return serialize(async () => {
|
|
2221
|
-
const ledger = ledgerFor(ws);
|
|
2222
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2223
|
-
const body = JSON.parse((await readBody(req)).toString("utf8") || "{}");
|
|
2224
|
-
const only = body.id || null;
|
|
2225
|
-
const refs = await listRefusedRefs(eng, ws);
|
|
2226
|
-
const retried = [], failed = [];
|
|
2227
|
-
for (const e of refs) {
|
|
2228
|
-
if (only && e.offerId !== only && e.oid !== only) continue;
|
|
2229
|
-
if (!e.intentB64) {
|
|
2230
|
-
failed.push({ intentId: e.offerId, error: "refused ref carries no intent bytes" });
|
|
2231
|
-
continue;
|
|
2232
|
-
}
|
|
2233
|
-
try {
|
|
2234
|
-
const r2 = offerAdmit(eng, ws, unb64(e.intentB64));
|
|
2235
|
-
if (r2.ok) retried.push({ intentId: e.offerId, head: r2.head });
|
|
2236
|
-
else failed.push({ intentId: e.offerId, error: String(r2.error || "").slice(0, 300) });
|
|
2237
|
-
} catch (err) {
|
|
2238
|
-
failed.push({ intentId: e.offerId, error: String(err).slice(0, 300) });
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
if (retried.length) await commitAndPush(eng, ws, ledger);
|
|
2242
|
-
for (const r2 of retried) await deleteRefusedRef(eng, ws, ledger, r2.intentId);
|
|
2243
|
-
sendJson(res, { ok: true, ws, retried, failed, remaining: (await listRefusedRefs(eng, ws)).length });
|
|
2244
|
-
});
|
|
2245
|
-
}
|
|
2246
|
-
if (sub[0] === "dead-letters" && (sub[1] === "discard" || req.method === "DELETE")) {
|
|
2247
|
-
return serialize(async () => {
|
|
2248
|
-
const ledger = ledgerFor(ws);
|
|
2249
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2250
|
-
const body = req.method === "DELETE" ? {} : JSON.parse((await readBody(req)).toString("utf8") || "{}");
|
|
2251
|
-
const only = body.id || url.searchParams.get("id") || null;
|
|
2252
|
-
const targets = (await listRefusedRefs(eng, ws)).filter((e) => !only || e.offerId === only || e.oid === only);
|
|
2253
|
-
for (const e of targets) await deleteRefusedRef(eng, ws, ledger, e.offerId || e.oid);
|
|
2254
|
-
sendJson(res, { ok: true, ws, discarded: targets.length });
|
|
2255
|
-
});
|
|
2256
|
-
}
|
|
2257
|
-
if (sub[0] === "rotate-secret" && req.method === "POST") {
|
|
2258
|
-
return sendJson(res, { ok: false, retired: true, error: "rotate-secret is retired \u2014 ownership is a ledger fact (sha256 of the push password on the law record); re-own by re-birthing via the upload lane" }, 410);
|
|
2259
|
-
}
|
|
2260
|
-
if (sub[0] === "pack" && req.method === "GET") {
|
|
2261
|
-
return serialize(async () => {
|
|
2262
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2263
|
-
const have = url.searchParams.get("have") || "";
|
|
2264
|
-
let r;
|
|
2265
|
-
try {
|
|
2266
|
-
r = servePack(eng, ws, have);
|
|
2267
|
-
} catch (e) {
|
|
2268
|
-
return sendJson(res, { ok: false, custodyBreak: true, error: String(e && e.message || e).slice(0, 400), have }, 409);
|
|
2269
|
-
}
|
|
2270
|
-
if (!r.packB64) return sendBytes(res, Buffer.alloc(0), 204, r.head ? { "x-nomos-head": r.head } : {});
|
|
2271
|
-
const kind = r.divergent ? "full" : have ? "delta" : "full";
|
|
2272
|
-
sendBytes(res, unb64(r.packB64), 200, {
|
|
2273
|
-
"content-type": "application/x-git-packfile",
|
|
2274
|
-
"x-nomos-pack": kind,
|
|
2275
|
-
...r.divergent ? { "x-nomos-divergent": "1" } : {},
|
|
2276
|
-
...r.head ? { "x-nomos-head": r.head } : {}
|
|
2277
|
-
});
|
|
2278
|
-
});
|
|
2279
|
-
}
|
|
2280
|
-
if (sub[0] === "snapshot" && req.method === "GET") {
|
|
2281
|
-
return serialize(async () => {
|
|
2282
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2283
|
-
const bytes = snapshot(eng, ws);
|
|
2284
|
-
sendBytes(res, bytes, 200, { "content-type": "application/octet-stream", "x-nomos-layout": "flat-manifest-v1" });
|
|
2285
|
-
});
|
|
2286
|
-
}
|
|
2287
|
-
if (sub[0] === "aggregates" && sub[1] || sub[0] === "qbyid" && req.method === "GET") {
|
|
2288
|
-
const id = sub[0] === "aggregates" ? decodeURIComponent(sub.slice(1).join("/")) : url.searchParams.get("id");
|
|
2289
|
-
return serialize(async () => {
|
|
2290
|
-
await mountWorkspace(eng, ws, ledgerFor(ws));
|
|
2291
|
-
const rows = qById(eng, ws, id, principalOf(req));
|
|
2292
|
-
sendJson(res, { ok: true, ws, rows });
|
|
2293
|
-
});
|
|
2294
|
-
}
|
|
2295
|
-
if (sub[0] === "subscribe") {
|
|
2296
|
-
return sendJson(res, { ok: false, error: "subscribe requires a WebSocket upgrade" }, 426);
|
|
2297
|
-
}
|
|
2298
|
-
if ((sub.length === 1 || sub.length === 2) && (req.method === "POST" || req.method === "GET")) {
|
|
2299
|
-
const opName = sub.join("/");
|
|
2300
|
-
const method = req.method.toLowerCase();
|
|
2301
|
-
return serialize(async () => {
|
|
2302
|
-
const ledger = ledgerFor(ws);
|
|
2303
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2304
|
-
const spec = openapiFor(ws);
|
|
2305
|
-
const resolved = resolveGatewayOperation(spec, opName, method);
|
|
2306
|
-
if (resolved.ambiguous) return sendJson(res, { ok: false, error: "ambiguous operation", candidates: resolved.ambiguous }, 409);
|
|
2307
|
-
const x = resolved.op && resolved.op["x-nomos"];
|
|
2308
|
-
if (!x) {
|
|
2309
|
-
if (RECOGNIZED_UNSUPPORTED.has(opName)) {
|
|
2310
|
-
return sendJson(res, {
|
|
2311
|
-
ok: false,
|
|
2312
|
-
unsupported: true,
|
|
2313
|
-
route: opName,
|
|
2314
|
-
error: `'${opName}' is a recognized Nomos Cloud route that 'githolon cloud dev' (single-node local) does not serve \u2014 ${UNSUPPORTED_WHY[opName] || "a multi-node / Cloudflare-infra concern"}. This is NOT an app bug; if you need it locally, ask the Nomos devs.`
|
|
2315
|
-
}, 501);
|
|
2316
|
-
}
|
|
2317
|
-
return sendJson(res, { ok: false, error: "unknown operation", see: `GET /v2/workspaces/${ws}` }, 404);
|
|
2318
|
-
}
|
|
2319
|
-
const principal = principalOf(req);
|
|
2320
|
-
const authToken = authTokenOf(req);
|
|
2321
|
-
if (x.kind === "offer" && method === "post") {
|
|
2322
|
-
const body = JSON.parse((await readBody(req)).toString("utf8") || "{}");
|
|
2323
|
-
if (body.intentRef && typeof body.intentRef === "object") {
|
|
2324
|
-
const resolved2 = await resolveStagedIntent(stageBucket, body.intentRef);
|
|
2325
|
-
if (!resolved2.ok) return sendJson(res, { ok: false, refused: true, error: resolved2.error }, resolved2.status);
|
|
2326
|
-
body.intentBytes = resolved2.intentB64;
|
|
2327
|
-
}
|
|
2328
|
-
try {
|
|
2329
|
-
let out;
|
|
2330
|
-
if (typeof body.intentBytes === "string" && body.intentBytes) {
|
|
2331
|
-
out = offerAdmit(eng, ws, unb64(body.intentBytes), {});
|
|
2332
|
-
if (out.ok) await commitAndPush(eng, ws, ledger);
|
|
2333
|
-
} else {
|
|
2334
|
-
const { actor, authorSecret, attestations, ...payload } = body;
|
|
2335
|
-
const effectiveActor = actor || principal || "";
|
|
2336
|
-
const lawHash = (currentLaw(eng, ws) || {})[x.domain] || "";
|
|
2337
|
-
out = await authorOn(eng, ws, ledger, x.domain, x.directiveId, payload, lawHash, {
|
|
2338
|
-
actor: effectiveActor,
|
|
2339
|
-
...effectiveActor ? { principalVerified: true } : {},
|
|
2340
|
-
...authToken ? { authToken } : {},
|
|
2341
|
-
...authorSecret ? { authorSecret } : {},
|
|
2342
|
-
...Array.isArray(attestations) ? { attestations } : {}
|
|
2343
|
-
});
|
|
2344
|
-
}
|
|
2345
|
-
if (!out.ok) return sendJson(res, { ok: false, refused: true, error: String(out.error).slice(0, 600) }, 422);
|
|
2346
|
-
const born = out.born ? await persistBorn(out.born) : void 0;
|
|
2347
|
-
sendJson(res, { ok: true, ws, head: out.head, ...born ? { born } : {}, ...out.birthOutcomes ? { birthOutcomes: out.birthOutcomes } : {} });
|
|
2348
|
-
} catch (e) {
|
|
2349
|
-
sendJson(res, { ok: false, refused: true, error: String(e && e.message || e).slice(0, 600) }, 422);
|
|
2350
|
-
}
|
|
2351
|
-
return;
|
|
2352
|
-
}
|
|
2353
|
-
if (method === "get") {
|
|
2354
|
-
const group = url.searchParams.get("group") || "";
|
|
2355
|
-
try {
|
|
2356
|
-
if (x.kind === "query") {
|
|
2357
|
-
const params = {};
|
|
2358
|
-
for (const k of x.key || []) {
|
|
2359
|
-
const v = url.searchParams.get(k);
|
|
2360
|
-
if (v != null) params[k] = v;
|
|
2361
|
-
}
|
|
2362
|
-
const rows = query(eng, ws, x.queryId, JSON.stringify(params), principal);
|
|
2363
|
-
return sendJson(res, { ok: true, ws, rows });
|
|
2364
|
-
}
|
|
2365
|
-
if (x.kind === "count") return sendJson(res, { ok: true, ws, ...count(eng, ws, x.countId, group, principal) });
|
|
2366
|
-
if (x.kind === "sum") return sendJson(res, { ok: true, ws, ...sum(eng, ws, x.sumId, group, principal) });
|
|
2367
|
-
if (x.kind === "extremum") return sendJson(res, { ok: true, ws, ...extremum(eng, ws, x.extremumId, x.extremumKind || url.searchParams.get("kind") || "", group, principal) });
|
|
2368
|
-
if (x.kind === "spatial") return sendJson(res, { ok: true, ws, ...spatialWithin(eng, ws, x.spatialId, url.searchParams.get("bbox") || {}, principal) });
|
|
2369
|
-
if (x.kind === "pendingConflicts") return sendJson(res, { ok: true, ws, rows: pendingConflicts(eng, ws, principal) });
|
|
2370
|
-
} catch (e) {
|
|
2371
|
-
return sendJson(res, { ok: false, error: String(e && e.message || e).slice(0, 600) }, 403);
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
sendJson(res, { ok: false, error: "unroutable operation" }, 404);
|
|
2375
|
-
});
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
sendJson(res, { ok: false, error: "not found", path: url.pathname }, 404);
|
|
2379
|
-
} catch (e) {
|
|
2380
|
-
sendJson(res, { ok: false, error: String(e && e.message || e).slice(0, 600) }, 500);
|
|
2381
|
-
}
|
|
2382
|
-
});
|
|
2383
|
-
async function gitBackend(req, res, repoPathInfo, ws) {
|
|
2384
|
-
const body = await readBody(req);
|
|
2385
|
-
let sessionPush = false;
|
|
2386
|
-
if (ws && req.method === "POST" && /git-receive-pack/.test(repoPathInfo)) {
|
|
2387
|
-
try {
|
|
2388
|
-
sessionPush = receivePackCommands(body).some((c) => c.ref.startsWith("refs/heads/session/"));
|
|
2389
|
-
} catch {
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
const out = await runGitHttpBackend(custody.root, {
|
|
2393
|
-
method: req.method,
|
|
2394
|
-
pathInfo: "/" + repoPathInfo,
|
|
2395
|
-
query: new URL(req.url, origin).search.replace(/^\?/, ""),
|
|
2396
|
-
contentType: req.headers["content-type"],
|
|
2397
|
-
body
|
|
2398
|
-
});
|
|
2399
|
-
res.writeHead(out.status, { ...out.headers, ...CORS });
|
|
2400
|
-
res.end(out.body);
|
|
2401
|
-
if (sessionPush) scheduleAdmit(ws);
|
|
2402
|
-
}
|
|
2403
|
-
const admitTimers = /* @__PURE__ */ new Map();
|
|
2404
|
-
function scheduleAdmit(ws) {
|
|
2405
|
-
if (admitTimers.has(ws)) clearTimeout(admitTimers.get(ws));
|
|
2406
|
-
admitTimers.set(ws, setTimeout(() => {
|
|
2407
|
-
admitTimers.delete(ws);
|
|
2408
|
-
serialize(async () => {
|
|
2409
|
-
try {
|
|
2410
|
-
const ledger = ledgerFor(ws);
|
|
2411
|
-
await mountWorkspace(eng, ws, ledger);
|
|
2412
|
-
const out = await admitAll(eng, ws, ledger, {});
|
|
2413
|
-
if (Array.isArray(out.born) && out.born.length) await persistBorn(packBornChildren(eng, out.born));
|
|
2414
|
-
} catch (e) {
|
|
2415
|
-
console.error(`[dev-cloud] admit ${ws} failed:`, String(e && e.message || e));
|
|
2416
|
-
}
|
|
2417
|
-
});
|
|
2418
|
-
}, 350));
|
|
2419
|
-
}
|
|
2420
|
-
server.on("upgrade", (req, socket) => {
|
|
2421
|
-
const key = req.headers["sec-websocket-key"];
|
|
2422
|
-
if (!key) return socket.destroy();
|
|
2423
|
-
const accept = createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
|
|
2424
|
-
socket.write(
|
|
2425
|
-
`HTTP/1.1 101 Switching Protocols\r
|
|
2426
|
-
Upgrade: websocket\r
|
|
2427
|
-
Connection: Upgrade\r
|
|
2428
|
-
Sec-WebSocket-Accept: ${accept}\r
|
|
2429
|
-
\r
|
|
2430
|
-
`
|
|
2431
|
-
);
|
|
2432
|
-
socket.on("data", () => {
|
|
2433
|
-
});
|
|
2434
|
-
socket.on("error", () => {
|
|
2435
|
-
});
|
|
2436
|
-
});
|
|
2437
|
-
if (!globalThis.__nomosDevGuards) {
|
|
2438
|
-
globalThis.__nomosDevGuards = true;
|
|
2439
|
-
process.on("unhandledRejection", (e) => {
|
|
2440
|
-
console.error("[dev-cloud] unhandledRejection (kept alive):", String(e && e.stack || e && e.message || e).slice(0, 500));
|
|
2441
|
-
});
|
|
2442
|
-
process.on("uncaughtException", (e) => {
|
|
2443
|
-
console.error("[dev-cloud] uncaughtException (kept alive):", String(e && e.stack || e && e.message || e).slice(0, 500));
|
|
2444
|
-
});
|
|
2445
|
-
}
|
|
2446
|
-
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
2447
|
-
return { server, origin, dataDir, kernelSha, eng, custody, ledgerFor, serialize, persistBorn, close: () => new Promise((r) => server.close(r)) };
|
|
2448
|
-
}
|
|
2449
|
-
function parseArgs(argv) {
|
|
2450
|
-
const o = {};
|
|
2451
|
-
for (let i = 0; i < argv.length; i++) {
|
|
2452
|
-
const a = argv[i];
|
|
2453
|
-
if (a === "--ephemeral") o.ephemeral = true;
|
|
2454
|
-
else if (a === "--port") o.port = parseInt(argv[++i], 10);
|
|
2455
|
-
else if (a === "--data") o.data = argv[++i];
|
|
2456
|
-
else if (a === "--firebase-project") o.firebaseProject = argv[++i];
|
|
2457
|
-
else if (a === "--platform") o.platform = argv[++i];
|
|
2458
|
-
else if (a === "--deploy") o.deploy = argv[++i];
|
|
2459
|
-
else if (a === "--auth-provider") o.authProvider = argv[++i];
|
|
2460
|
-
else if (a === "--bootstrap") o.bootstrap = true;
|
|
2461
|
-
else if (a === "--attested") o.attested = true;
|
|
2462
|
-
else if (a === "--kernel-source") o.kernelSource = argv[++i];
|
|
2463
|
-
}
|
|
2464
|
-
return o;
|
|
2465
|
-
}
|
|
2466
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
2467
|
-
const opts = parseArgs(process.argv.slice(2));
|
|
2468
|
-
const dev = await startDevCloud(opts);
|
|
2469
|
-
console.log(`[dev-cloud] Nomos dev cloud listening on ${dev.origin}`);
|
|
2470
|
-
console.log(`[dev-cloud] data dir : ${dev.dataDir}`);
|
|
2471
|
-
console.log(`[dev-cloud] kernel : ${dev.kernelSha.slice(0, 16)}\u2026`);
|
|
2472
|
-
console.log(`[dev-cloud] point your Flutter app's cloud: base URL at ${dev.origin}`);
|
|
2473
|
-
if (opts.bootstrap || opts.platform) {
|
|
2474
|
-
const { bootstrapPlatform: bootstrapPlatform2 } = await Promise.resolve().then(() => (init_bootstrap(), bootstrap_exports));
|
|
2475
|
-
await bootstrapPlatform2(dev, opts).catch((e) => console.error("[dev-cloud] bootstrap failed:", e));
|
|
2476
|
-
}
|
|
2477
|
-
process.on("SIGINT", async () => {
|
|
2478
|
-
await dev.close();
|
|
2479
|
-
process.exit(0);
|
|
2480
|
-
});
|
|
2481
|
-
}
|
|
2482
|
-
export {
|
|
2483
|
-
startDevCloud
|
|
2484
|
-
};
|