prae-cli 0.9.1 → 0.10.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/lib/bin.js +2 -6
- package/lib/{checkpoint-BWSuhZ5h.js → checkpoint-BqJ8xwky.js} +1 -1
- package/lib/cli-BcAJlB9B.js +8 -0
- package/lib/{cli-7yLG89gV.js → cli-DPldZ2ND.js} +1122 -511
- package/lib/docs/pilot-claude-code.md +191 -0
- package/lib/{evaluate-wMPkjcIC.js → evaluate-DhoGQw0Z.js} +32 -1
- package/lib/frameworks-BgZHGKiH.js +118 -0
- package/lib/hook-Gll2spTP.js +695 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +6 -4
- package/lib/init-DRd-IRR6.js +266 -0
- package/lib/{instructions-Bg5puOen.js → instructions-DihPiiC2.js} +2 -1
- package/lib/{mcp-doNYU-1C.js → mcp-D521EoP6.js} +2 -2
- package/lib/pack-h5n43S0F.js +448 -0
- package/lib/playground.js +27 -3
- package/lib/{receipt-check-CD-W1wXs.js → receipt-check-CUuEjGHL.js} +1 -1
- package/lib/{src-D7g4lvaY.js → src-Cy-Acrgj.js} +111 -25
- package/lib/{src-CwBEFhww.js → src-DMoYsdfo.js} +1 -1
- package/lib/src-wG36kT6K.js +3 -0
- package/lib/{test-run-XNIRHKOu.js → test-run-C8dHjIE2.js} +1 -1
- package/lib/views-CUcixte0.js +1396 -0
- package/package.json +5 -4
- package/lib/src-d82lSKVQ.js +0 -3
- package/lib/views-CIg2ZNG0.js +0 -1401
- /package/lib/{ocsf-DhVGbBUw.js → ocsf-DF9cMiQJ.js} +0 -0
- /package/lib/{src-CBGZVvt7.js → src-BlmsTGdY.js} +0 -0
- /package/lib/{src-B25SHWcz.js → src-BuaOiL2v.js} +0 -0
- /package/lib/{src-d4c_n6wd.js → src-CDYyZyeU.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
|
|
@@ -116,6 +116,13 @@ const ANSWER_TTL_MS = 5 * 6e4;
|
|
|
116
116
|
function signBytes(bytes, privateKey) {
|
|
117
117
|
return sign(null, Buffer.from(bytes), priv(privateKey)).toString("base64");
|
|
118
118
|
}
|
|
119
|
+
function verifyBytes(bytes, signature, publicKeyPem) {
|
|
120
|
+
try {
|
|
121
|
+
return verify(null, Buffer.from(bytes), createPublicKey(publicKeyPem), Buffer.from(signature.trim(), "base64"));
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
119
126
|
/**
|
|
120
127
|
* How a private key is sealed at rest: scrypt (N = 2^17, r = 8, p = 1) over
|
|
121
128
|
* the passphrase, AES-256-GCM over the key. The sealed file can sit where an
|
|
@@ -171,13 +178,16 @@ function unsealPrivateKey(sealed, passphrase) {
|
|
|
171
178
|
if (match === null) throw new Error("not a PRAE sealed key (an older format?): make a new one with prae keygen --role");
|
|
172
179
|
const box = JSON.parse(Buffer.from((match[1] ?? "").replace(/\s/g, ""), "base64").toString("utf8"));
|
|
173
180
|
if (box.v !== 1 || box.kdf !== "scrypt") throw new Error("unknown sealing");
|
|
174
|
-
const
|
|
181
|
+
const wrap = scryptSync(passphrase, Buffer.from(String(box.salt), "base64"), 32, {
|
|
175
182
|
N: Number(box.N),
|
|
176
183
|
r: Number(box.r),
|
|
177
184
|
p: Number(box.p),
|
|
178
185
|
maxmem: SCRYPT.maxmem
|
|
179
|
-
})
|
|
180
|
-
|
|
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);
|
|
181
191
|
return createPrivateKey({
|
|
182
192
|
key: Buffer.concat([decipher.update(Buffer.from(String(box.data), "base64")), decipher.final()]),
|
|
183
193
|
format: "der",
|
|
@@ -200,14 +210,15 @@ function readEntries(text) {
|
|
|
200
210
|
return text.split("\n").filter((line) => line.trim() !== "").map((line) => JSON.parse(line));
|
|
201
211
|
}
|
|
202
212
|
/** Re-derive every hash and confirm each entry names its predecessor. */
|
|
203
|
-
function verifyEntries(text) {
|
|
213
|
+
function verifyEntries(text, from) {
|
|
204
214
|
const problems = [];
|
|
205
|
-
let expectedPrev = GENESIS;
|
|
206
|
-
let expectedSeq = 0;
|
|
215
|
+
let expectedPrev = from?.prev ?? GENESIS;
|
|
216
|
+
let expectedSeq = from?.seq ?? 0;
|
|
207
217
|
let count = 0;
|
|
218
|
+
const offset = from?.line ?? 0;
|
|
208
219
|
const lines = text.split("\n").filter((line) => line.trim() !== "");
|
|
209
220
|
for (const [index, line] of lines.entries()) {
|
|
210
|
-
const at = index + 1;
|
|
221
|
+
const at = offset + index + 1;
|
|
211
222
|
count += 1;
|
|
212
223
|
let entry;
|
|
213
224
|
try {
|
|
@@ -294,6 +305,16 @@ const STALE_LOCK_MS = 1e4;
|
|
|
294
305
|
/** Run `fn` holding an exclusive lock file; synchronous, like the append. */
|
|
295
306
|
function withLock(lock, fn) {
|
|
296
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) {
|
|
297
318
|
for (;;) {
|
|
298
319
|
let fd;
|
|
299
320
|
try {
|
|
@@ -306,9 +327,7 @@ function withLock(lock, fn) {
|
|
|
306
327
|
continue;
|
|
307
328
|
}
|
|
308
329
|
} catch {}
|
|
309
|
-
|
|
310
|
-
Atomics.wait(SLEEP, 0, 0, 2);
|
|
311
|
-
continue;
|
|
330
|
+
return BUSY;
|
|
312
331
|
}
|
|
313
332
|
try {
|
|
314
333
|
return fn();
|
|
@@ -354,19 +373,30 @@ var Ledger = class Ledger {
|
|
|
354
373
|
}
|
|
355
374
|
/** Append one decision and return the entry as written. */
|
|
356
375
|
append(record) {
|
|
357
|
-
return withLock(`${this.path}.lock`, () =>
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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;
|
|
370
400
|
}
|
|
371
401
|
/** Every entry, in order. */
|
|
372
402
|
read() {
|
|
@@ -389,4 +419,60 @@ var Ledger = class Ledger {
|
|
|
389
419
|
const nap = new Int32Array(new SharedArrayBuffer(4));
|
|
390
420
|
|
|
391
421
|
//#endregion
|
|
392
|
-
|
|
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 };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as scan, i as looksBinary, n as MAX_SCAN_BYTES, r as entropy, t as DETECTORS } from "./src-
|
|
1
|
+
import { a as scan, i as looksBinary, n as MAX_SCAN_BYTES, r as entropy, t as DETECTORS } from "./src-BuaOiL2v.js";
|
|
2
2
|
|
|
3
3
|
export { DETECTORS, MAX_SCAN_BYTES, entropy, looksBinary, scan };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { _ as canonical, a as verifyEntries, b as seal, c as unsealPrivateKey, d as generateKeypair, f as makeReceipt, g as GENESIS, h as verifySignature, i as tailOf, l as verifyBytes, m as verifyReceipt, n as Ledger, o as sealedKeypair, p as signEntry, r as readEntries, s as signBytes, t as verifyGrowing, u as fingerprint, v as chain, y as hashEntry } from "./src-Cy-Acrgj.js";
|
|
2
|
+
|
|
3
|
+
export { GENESIS, Ledger, canonical, chain, fingerprint, generateKeypair, hashEntry, makeReceipt, readEntries, seal, sealedKeypair, signBytes, signEntry, tailOf, unsealPrivateKey, verifyBytes, verifyEntries, verifyGrowing, verifyReceipt, verifySignature };
|