prae-cli 0.9.2 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bin.js +2 -6
- package/lib/{checkpoint-C_q_Rpa5.js → checkpoint-BLOGdWFu.js} +1 -1
- package/lib/cli-BlWTUnGJ.js +6 -0
- package/lib/{cli-B_B6-bUZ.js → cli-C_6dosaw.js} +629 -534
- package/lib/docs/pilot-claude-code.md +192 -0
- package/lib/hook-DxLgNtc4.js +693 -0
- package/lib/index.js +4 -4
- package/lib/init-BPlBJvA-.js +272 -0
- package/lib/{instructions-ya0LVnZy.js → instructions-fCJ4j7ce.js} +2 -1
- package/lib/{mcp-BCXbpqu4.js → mcp-DdEDvTvR.js} +1 -1
- package/lib/playground.js +717 -109
- package/lib/prae-baseline.prae.json +144 -1
- package/lib/{receipt-check-DQK_gVPc.js → receipt-check-2F4zLEW5.js} +1 -1
- package/lib/src-2iJy5MZc.js +3 -0
- package/lib/src-CZYvvEn5.js +1691 -0
- package/lib/{src-LA7NdLKb.js → src-CuiWC80o.js} +104 -25
- package/lib/{test-run-CCDgJ0Dp.js → test-run-BGsK-inZ.js} +1 -1
- package/lib/views-C7DD-Oif.js +1396 -0
- package/package.json +6 -6
- package/lib/evaluate-BqLxhYiP.js +0 -499
- package/lib/src-7X6dO7gC.js +0 -3
- package/lib/views-D4UEfbzt.js +0 -1505
- /package/lib/{ocsf-DhVGbBUw.js → ocsf-BwWyM7hc.js} +0 -0
- /package/lib/{src-CBGZVvt7.js → src-BoBCmiwj.js} +0 -0
- /package/lib/{src-d4c_n6wd.js → src-XukJfuml.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { appendFileSync, closeSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { createCipheriv, createDecipheriv, createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomBytes, scryptSync, sign, verify } from "node:crypto";
|
|
4
4
|
|
|
5
5
|
//#region ../ledger/src/entry.ts
|
|
@@ -178,13 +178,16 @@ function unsealPrivateKey(sealed, passphrase) {
|
|
|
178
178
|
if (match === null) throw new Error("not a PRAE sealed key (an older format?): make a new one with prae keygen --role");
|
|
179
179
|
const box = JSON.parse(Buffer.from((match[1] ?? "").replace(/\s/g, ""), "base64").toString("utf8"));
|
|
180
180
|
if (box.v !== 1 || box.kdf !== "scrypt") throw new Error("unknown sealing");
|
|
181
|
-
const
|
|
181
|
+
const wrap = scryptSync(passphrase, Buffer.from(String(box.salt), "base64"), 32, {
|
|
182
182
|
N: Number(box.N),
|
|
183
183
|
r: Number(box.r),
|
|
184
184
|
p: Number(box.p),
|
|
185
185
|
maxmem: SCRYPT.maxmem
|
|
186
|
-
})
|
|
187
|
-
|
|
186
|
+
});
|
|
187
|
+
const tag = Buffer.from(String(box.tag), "base64");
|
|
188
|
+
if (tag.length !== 16) throw new Error("sealed key: authentication tag is not 16 bytes");
|
|
189
|
+
const decipher = createDecipheriv("aes-256-gcm", wrap, Buffer.from(String(box.iv), "base64"), { authTagLength: 16 });
|
|
190
|
+
decipher.setAuthTag(tag);
|
|
188
191
|
return createPrivateKey({
|
|
189
192
|
key: Buffer.concat([decipher.update(Buffer.from(String(box.data), "base64")), decipher.final()]),
|
|
190
193
|
format: "der",
|
|
@@ -207,14 +210,15 @@ function readEntries(text) {
|
|
|
207
210
|
return text.split("\n").filter((line) => line.trim() !== "").map((line) => JSON.parse(line));
|
|
208
211
|
}
|
|
209
212
|
/** Re-derive every hash and confirm each entry names its predecessor. */
|
|
210
|
-
function verifyEntries(text) {
|
|
213
|
+
function verifyEntries(text, from) {
|
|
211
214
|
const problems = [];
|
|
212
|
-
let expectedPrev = GENESIS;
|
|
213
|
-
let expectedSeq = 0;
|
|
215
|
+
let expectedPrev = from?.prev ?? GENESIS;
|
|
216
|
+
let expectedSeq = from?.seq ?? 0;
|
|
214
217
|
let count = 0;
|
|
218
|
+
const offset = from?.line ?? 0;
|
|
215
219
|
const lines = text.split("\n").filter((line) => line.trim() !== "");
|
|
216
220
|
for (const [index, line] of lines.entries()) {
|
|
217
|
-
const at = index + 1;
|
|
221
|
+
const at = offset + index + 1;
|
|
218
222
|
count += 1;
|
|
219
223
|
let entry;
|
|
220
224
|
try {
|
|
@@ -301,6 +305,16 @@ const STALE_LOCK_MS = 1e4;
|
|
|
301
305
|
/** Run `fn` holding an exclusive lock file; synchronous, like the append. */
|
|
302
306
|
function withLock(lock, fn) {
|
|
303
307
|
const deadline = Date.now() + 3e4;
|
|
308
|
+
for (;;) {
|
|
309
|
+
const done = tryLock(lock, fn);
|
|
310
|
+
if (done !== BUSY) return done;
|
|
311
|
+
if (Date.now() > deadline) throw new Error(`prae ledger: ${lock} has been held for 30 s; refusing to append without it`);
|
|
312
|
+
Atomics.wait(SLEEP, 0, 0, 2);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const BUSY = Symbol("busy");
|
|
316
|
+
/** Run `fn` holding the lock if it is free now (or stale); `BUSY` if not. */
|
|
317
|
+
function tryLock(lock, fn) {
|
|
304
318
|
for (;;) {
|
|
305
319
|
let fd;
|
|
306
320
|
try {
|
|
@@ -313,9 +327,7 @@ function withLock(lock, fn) {
|
|
|
313
327
|
continue;
|
|
314
328
|
}
|
|
315
329
|
} catch {}
|
|
316
|
-
|
|
317
|
-
Atomics.wait(SLEEP, 0, 0, 2);
|
|
318
|
-
continue;
|
|
330
|
+
return BUSY;
|
|
319
331
|
}
|
|
320
332
|
try {
|
|
321
333
|
return fn();
|
|
@@ -361,19 +373,30 @@ var Ledger = class Ledger {
|
|
|
361
373
|
}
|
|
362
374
|
/** Append one decision and return the entry as written. */
|
|
363
375
|
append(record) {
|
|
364
|
-
return withLock(`${this.path}.lock`, () =>
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
376
|
+
return withLock(`${this.path}.lock`, () => this.write(record));
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Append only if no other writer holds the lock right now; `undefined`
|
|
380
|
+
* when one does. For a process that must not block while it waits —
|
|
381
|
+
* one that answers other writers' signers (PRA-143), whose writers hold
|
|
382
|
+
* the lock until it answers.
|
|
383
|
+
*/
|
|
384
|
+
tryAppend(record) {
|
|
385
|
+
const done = tryLock(`${this.path}.lock`, () => this.write(record));
|
|
386
|
+
return done === BUSY ? void 0 : done;
|
|
387
|
+
}
|
|
388
|
+
write(record) {
|
|
389
|
+
const { seq, last } = tailOf(this.path);
|
|
390
|
+
const sealed = chain(record, seq, last);
|
|
391
|
+
const entry = this.signer !== void 0 ? {
|
|
392
|
+
...sealed,
|
|
393
|
+
sig: this.signer(sealed)
|
|
394
|
+
} : this.signWith === void 0 ? sealed : {
|
|
395
|
+
...sealed,
|
|
396
|
+
sig: signEntry(sealed, this.signWith)
|
|
397
|
+
};
|
|
398
|
+
appendFileSync(this.path, `${JSON.stringify(entry)}\n`);
|
|
399
|
+
return entry;
|
|
377
400
|
}
|
|
378
401
|
/** Every entry, in order. */
|
|
379
402
|
read() {
|
|
@@ -396,4 +419,60 @@ var Ledger = class Ledger {
|
|
|
396
419
|
const nap = new Int32Array(new SharedArrayBuffer(4));
|
|
397
420
|
|
|
398
421
|
//#endregion
|
|
399
|
-
|
|
422
|
+
//#region ../ledger/src/verify-cache.ts
|
|
423
|
+
const prefixes = /* @__PURE__ */ new Map();
|
|
424
|
+
const sha = (s) => createHash("sha256").update(s).digest("hex");
|
|
425
|
+
function verifyGrowing(path, publicKey) {
|
|
426
|
+
const raw = readFileSync(path, "utf8");
|
|
427
|
+
const text = raw.slice(0, raw.lastIndexOf("\n") + 1);
|
|
428
|
+
const slot = `${path}\u0000${publicKey ?? ""}`;
|
|
429
|
+
const known = prefixes.get(slot);
|
|
430
|
+
const from = known !== void 0 && known.key === publicKey && text.length >= known.length && sha(text.slice(0, known.length)) === known.sha ? known : void 0;
|
|
431
|
+
const fresh = from === void 0 ? text : text.slice(from.length);
|
|
432
|
+
const verified = verifyEntries(fresh, from === void 0 ? void 0 : {
|
|
433
|
+
prev: from.head,
|
|
434
|
+
seq: from.seq,
|
|
435
|
+
line: from.entries
|
|
436
|
+
});
|
|
437
|
+
let checked = from?.checked ?? 0;
|
|
438
|
+
const failed = [...from?.failed ?? []];
|
|
439
|
+
let rows = [];
|
|
440
|
+
try {
|
|
441
|
+
rows = readEntries(fresh);
|
|
442
|
+
} catch {
|
|
443
|
+
rows = [];
|
|
444
|
+
}
|
|
445
|
+
if (publicKey !== void 0) for (const e of rows) {
|
|
446
|
+
if (e.sig === void 0) continue;
|
|
447
|
+
checked += 1;
|
|
448
|
+
if (!verifySignature(e, e.sig, publicKey)) failed.push(e.seq);
|
|
449
|
+
}
|
|
450
|
+
const entries = (from?.entries ?? 0) + verified.entries;
|
|
451
|
+
const problems = [...from?.problems ?? [], ...verified.problems];
|
|
452
|
+
const last = rows.at(-1);
|
|
453
|
+
if (rows.length === verified.entries) prefixes.set(slot, {
|
|
454
|
+
length: text.length,
|
|
455
|
+
sha: sha(text),
|
|
456
|
+
key: publicKey,
|
|
457
|
+
head: last?.hash ?? from?.head ?? GENESIS,
|
|
458
|
+
seq: last === void 0 ? from?.seq ?? 0 : last.seq + 1,
|
|
459
|
+
entries,
|
|
460
|
+
checked,
|
|
461
|
+
failed,
|
|
462
|
+
problems
|
|
463
|
+
});
|
|
464
|
+
else prefixes.delete(slot);
|
|
465
|
+
return {
|
|
466
|
+
ok: problems.length === 0,
|
|
467
|
+
entries,
|
|
468
|
+
problems,
|
|
469
|
+
signatures: publicKey === void 0 ? null : {
|
|
470
|
+
checked,
|
|
471
|
+
failed
|
|
472
|
+
},
|
|
473
|
+
text
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
//#endregion
|
|
478
|
+
export { canonical as _, verifyEntries as a, seal as b, unsealPrivateKey as c, generateKeypair as d, makeReceipt as f, GENESIS as g, verifySignature as h, tailOf as i, verifyBytes as l, verifyReceipt as m, Ledger as n, sealedKeypair as o, signEntry as p, readEntries as r, signBytes as s, verifyGrowing as t, fingerprint as u, chain as v, hashEntry as y };
|