loki-mode 11.7.1 → 11.9.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/backlog.py +40 -4
- package/autonomy/lib/chat.sh +3 -2
- package/autonomy/lib/workspace.py +95 -4
- package/autonomy/loki +185 -106
- package/autonomy/trigger-server.py +550 -5
- package/autonomy/verify.sh +5 -0
- package/bin/loki +1 -1
- package/completions/_loki +34 -1
- package/completions/loki.bash +10 -3
- package/dashboard/__init__.py +1 -1
- package/docs/ARCHITECTURE.md +0 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/COMPONENTS.md +1 -1
- package/docs/DEPLOY-PLAN.md +13 -3
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +1 -1
- package/docs/dev/architecture-reference.md +1 -1
- package/docs/sentry-intake.md +36 -0
- package/docs/v10/D61-SPEED.md +14 -0
- package/docs/v10/PROGRESS.md +22 -0
- package/loki-ts/dist/loki.js +962 -879
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/ask-tools-server.js +13 -4
- package/packages/control-plane/dist/server.js +614 -456
- package/packages/control-plane/src/server/diffstat.ts +10 -2
- package/packages/control-plane/src/server/legacy/routes-control.ts +8 -4
- package/packages/control-plane/src/server/routes/checkpoints.ts +11 -2
- package/packages/control-plane/ui/dist/assets/index-u8cd6TRQ.js +190 -0
- package/packages/control-plane/ui/dist/index.html +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-S3BYeapm.js → AdminPage-BdH9iL2I.js} +1 -1
- package/web-app/dist/assets/{Avatar-C1rHwLgn.js → Avatar-C-WixxR6.js} +1 -1
- package/web-app/dist/assets/{Badge-CzlnN5aQ.js → Badge-BFWpRd_L.js} +1 -1
- package/web-app/dist/assets/{Button-B1w5AeGC.js → Button-CE5vG66Y.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-Dyeqjfkc.js → CockpitPage-BAVn10bY.js} +1 -1
- package/web-app/dist/assets/{ComparePage-C6y2GkGR.js → ComparePage-DOecIGfM.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-Dg2fcGkR.js → ErrorBoundary-1rNA5v7f.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CxbyjvTj.js → EvidenceReceiptPanel-DjOaWfdH.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Dpys1xDb.js → GitHubIssuesPanel-NNPBuRPD.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CRVjWoFl.js → GitHubPRsPanel-CL1b--tV.js} +1 -1
- package/web-app/dist/assets/{HomePage-BKnv-oYw.js → HomePage-BsLDN6MW.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BGksmFRd.js → LoginPage-CVIv5bCS.js} +1 -1
- package/web-app/dist/assets/{MagicPage-OCvhp16M.js → MagicPage-BgMBB9lK.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-CJOJpXWC.js → MetricsPage-BHeJlqPC.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-C04p3QvN.js → NotFoundPage-DDzwfyDK.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BRB8txsY.js → ProjectPage-CuQWEYBF.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-CoUYeQEh.js → ProjectsPage-OoD0RFDZ.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-BkjmJGfM.js → SettingsPage-tMFMKkK5.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-B4vU9TjQ.js → ShowcasePage-DVfLoWdz.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-Z2jiVsni.js → SystemSettingsPage-CThV9N4l.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-Ulm8SISq.js → TeamsPage-CV0nvXI8.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-C_7MM6Ey.js → TemplatesPage-D81EI1L8.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cumd7Tgs.js → TerminalOutput-Bj7uKRIe.js} +1 -1
- package/web-app/dist/assets/{activity-CZAUkbiY.js → activity-DoNfNkOL.js} +1 -1
- package/web-app/dist/assets/{bell-BOvSXpEr.js → bell-BWO3OU50.js} +1 -1
- package/web-app/dist/assets/{bot-DOTZxBYj.js → bot-DcvWv_zD.js} +1 -1
- package/web-app/dist/assets/{check-BxpE0B5u.js → check-CfG09Z18.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D6KPvBOA.js → chevron-left-z0fh4x2h.js} +1 -1
- package/web-app/dist/assets/{circle-alert-Bajq86oe.js → circle-alert-C_7GlYul.js} +1 -1
- package/web-app/dist/assets/{clock-CnlHAVJc.js → clock-BsYUp070.js} +1 -1
- package/web-app/dist/assets/{cloud-DjaymjwM.js → cloud-BVWAJ7Rv.js} +1 -1
- package/web-app/dist/assets/{code-xml-BADEw2Ld.js → code-xml-CdZT-6zu.js} +1 -1
- package/web-app/dist/assets/{database-B4aKb5Cq.js → database-Dbkm9t2D.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DrldXpja.js → dollar-sign-CfIUM5Pd.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-DVebDVR-.js → file-code-corner-CrUKhnj-.js} +1 -1
- package/web-app/dist/assets/{file-plus-BD-N_-Tl.js → file-plus-Cf6u0xHY.js} +1 -1
- package/web-app/dist/assets/{globe-C7ieInC-.js → globe-DsIqaI7w.js} +1 -1
- package/web-app/dist/assets/{hammer-BSugKIji.js → hammer-CA14BeMp.js} +1 -1
- package/web-app/dist/assets/{index-nLQgfvOn.js → index-DpJ_GJuZ.js} +3 -3
- package/web-app/dist/assets/{layers-CHXxU2on.js → layers-Dh5Oqhrj.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BEEz0LbD.js → loader-circle-B0WHIGqt.js} +1 -1
- package/web-app/dist/assets/{lock-CzQGfcnE.js → lock-DkgfEGYF.js} +1 -1
- package/web-app/dist/assets/{package-CcHYQLGd.js → package-LzjmwD1S.js} +1 -1
- package/web-app/dist/assets/{plus-DdzgFEXn.js → plus-BNjA0KSV.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-7x0HbH9Y.js → refresh-cw-k3IacuDX.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-DBahX1Sp.js → rotate-ccw-BFON_xv-.js} +1 -1
- package/web-app/dist/assets/{scroll-text-BB8y8a9Z.js → scroll-text-CaIlSPZB.js} +1 -1
- package/web-app/dist/assets/{server-CfkbW2MH.js → server-CKFUccCG.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DUgD8rBF.js → shield-alert-Q4-rPo5r.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DGoyY6QC.js → thumbs-up-BYMeh8oV.js} +1 -1
- package/web-app/dist/assets/{trash-2-B6xrSUMr.js → trash-2-BHSUEIHw.js} +1 -1
- package/web-app/dist/assets/{trending-down-B6RGKTOx.js → trending-down-BC_FR2VT.js} +1 -1
- package/web-app/dist/assets/{trending-up-B44DCrt1.js → trending-up-CmA-WmLW.js} +1 -1
- package/web-app/dist/assets/{upload-4kWm33ZY.js → upload-BxLgBa1B.js} +1 -1
- package/web-app/dist/assets/{usePolling-zkUtRQG6.js → usePolling-DKNgwt5n.js} +1 -1
- package/web-app/dist/assets/{user-04cEVIe2.js → user-CA2DlmLc.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/memory/rag_injector.py +0 -161
- package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +0 -190
|
@@ -248,7 +248,7 @@ async function backfill(o) {
|
|
|
248
248
|
}
|
|
249
249
|
const source = sourceId(o.repoDir);
|
|
250
250
|
let runs2 = 0, sent = 0;
|
|
251
|
-
const
|
|
251
|
+
const failed2 = [];
|
|
252
252
|
for (const id of ids) {
|
|
253
253
|
const runDir = join7(root, id);
|
|
254
254
|
if (!hasSpool(runDir))
|
|
@@ -264,9 +264,9 @@ async function backfill(o) {
|
|
|
264
264
|
await sleep(backoffMs(a));
|
|
265
265
|
}
|
|
266
266
|
if (!res.ok)
|
|
267
|
-
|
|
267
|
+
failed2.push(id);
|
|
268
268
|
}
|
|
269
|
-
return { runs: runs2, sent, failed };
|
|
269
|
+
return { runs: runs2, sent, failed: failed2 };
|
|
270
270
|
}
|
|
271
271
|
var init_backfill = __esm(() => {
|
|
272
272
|
init_ship();
|
|
@@ -7325,6 +7325,72 @@ var init_paths = __esm(() => {
|
|
|
7325
7325
|
REPO_ROOT = findRepoRoot();
|
|
7326
7326
|
});
|
|
7327
7327
|
|
|
7328
|
+
// ../../loki-ts/src/util/receipt_signer.ts
|
|
7329
|
+
import { createHash as createHash6, randomBytes as randomBytes2, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign, verify as verify2 } from "crypto";
|
|
7330
|
+
import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
7331
|
+
import { homedir as homedir8 } from "os";
|
|
7332
|
+
import { dirname as dirname7, join as join26 } from "path";
|
|
7333
|
+
function canonicalJson(x) {
|
|
7334
|
+
const walk = (v) => {
|
|
7335
|
+
if (Array.isArray(v))
|
|
7336
|
+
return `[${v.map(walk).join(",")}]`;
|
|
7337
|
+
if (v !== null && typeof v === "object") {
|
|
7338
|
+
const o = v;
|
|
7339
|
+
return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
|
|
7340
|
+
}
|
|
7341
|
+
return JSON.stringify(v ?? null);
|
|
7342
|
+
};
|
|
7343
|
+
return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
7344
|
+
}
|
|
7345
|
+
function loadSigningKey(generate = true) {
|
|
7346
|
+
try {
|
|
7347
|
+
const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
|
|
7348
|
+
let pem = inline ?? "";
|
|
7349
|
+
if (!inline) {
|
|
7350
|
+
const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
7351
|
+
const file = given || join26(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
|
|
7352
|
+
try {
|
|
7353
|
+
pem = readFileSync18(file);
|
|
7354
|
+
if (!given) {
|
|
7355
|
+
for (const [f, m] of [[file, 384], [dirname7(file), 448]])
|
|
7356
|
+
if (statSync11(f).mode & 63)
|
|
7357
|
+
chmodSync4(f, m);
|
|
7358
|
+
}
|
|
7359
|
+
} catch (e) {
|
|
7360
|
+
if (e.code !== "ENOENT" || !generate)
|
|
7361
|
+
return null;
|
|
7362
|
+
mkdirSync5(dirname7(dirname7(file)), { recursive: true, mode: 448 });
|
|
7363
|
+
mkdirSync5(dirname7(file), { recursive: true, mode: 448 });
|
|
7364
|
+
const tmp = `${file}.${randomBytes2(6).toString("hex")}.tmp`;
|
|
7365
|
+
writeFileSync8(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
|
|
7366
|
+
try {
|
|
7367
|
+
linkSync(tmp, file);
|
|
7368
|
+
} catch (l) {
|
|
7369
|
+
if (l.code !== "EEXIST")
|
|
7370
|
+
throw l;
|
|
7371
|
+
} finally {
|
|
7372
|
+
unlinkSync3(tmp);
|
|
7373
|
+
}
|
|
7374
|
+
pem = readFileSync18(file);
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
const k = createPrivateKey(pem);
|
|
7378
|
+
return k.asymmetricKeyType === "ed25519" ? k : null;
|
|
7379
|
+
} catch {
|
|
7380
|
+
return null;
|
|
7381
|
+
}
|
|
7382
|
+
}
|
|
7383
|
+
function signReceipt(runId, hash) {
|
|
7384
|
+
const key = loadSigningKey();
|
|
7385
|
+
if (!key)
|
|
7386
|
+
return { jwt: null, kid: null };
|
|
7387
|
+
const kid = kidOf2(createPublicKey2(key));
|
|
7388
|
+
const input = `${b64u(canonicalJson({ alg: "EdDSA", typ: "JWT", kid }))}.${b64u(canonicalJson({ job_id: runId, run_id: runId, receipt_sha256: hash, iat: Math.floor(Date.now() / 1000) }))}`;
|
|
7389
|
+
return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
|
|
7390
|
+
}
|
|
7391
|
+
var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash6("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest());
|
|
7392
|
+
var init_receipt_signer = () => {};
|
|
7393
|
+
|
|
7328
7394
|
// ../../loki-ts/src/engine10/registry.ts
|
|
7329
7395
|
var init_registry = () => {};
|
|
7330
7396
|
|
|
@@ -7377,7 +7443,7 @@ var init_types = __esm(() => {
|
|
|
7377
7443
|
});
|
|
7378
7444
|
|
|
7379
7445
|
// ../../loki-ts/src/util/spec_file.ts
|
|
7380
|
-
import { isAbsolute, join as
|
|
7446
|
+
import { isAbsolute, join as join27, relative } from "path";
|
|
7381
7447
|
function specReceiptBlock(env) {
|
|
7382
7448
|
const { LOKI_E10_SPEC_PATH: p, LOKI_E10_SPEC_SHA256: sha2 } = env;
|
|
7383
7449
|
if (!p || !sha2)
|
|
@@ -7388,7 +7454,7 @@ function specReceiptBlock(env) {
|
|
|
7388
7454
|
var SPEC_DIR;
|
|
7389
7455
|
var init_spec_file = __esm(() => {
|
|
7390
7456
|
init_types();
|
|
7391
|
-
SPEC_DIR =
|
|
7457
|
+
SPEC_DIR = join27(".loki", "specs");
|
|
7392
7458
|
});
|
|
7393
7459
|
|
|
7394
7460
|
// ../../loki-ts/src/engine10/pr_body.ts
|
|
@@ -7400,14 +7466,14 @@ function aiTrailers(i, env = process.env) {
|
|
|
7400
7466
|
var markingOn = (env) => env["LOKI_AI_MARKING"] === "1", clean = (v) => v.replace(/[\u0000-\u001f\u007f\u2028\u2029]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 100) || "unknown";
|
|
7401
7467
|
|
|
7402
7468
|
// ../../loki-ts/src/util/credential_env.ts
|
|
7403
|
-
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, statSync as
|
|
7469
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, statSync as statSync12, lstatSync as lstatSync4 } from "fs";
|
|
7404
7470
|
import { tmpdir as tmpdir3 } from "os";
|
|
7405
|
-
import { join as
|
|
7406
|
-
import { randomBytes as
|
|
7471
|
+
import { join as join28 } from "path";
|
|
7472
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
7407
7473
|
function withheldSentinel(fresh = false) {
|
|
7408
7474
|
if (fresh)
|
|
7409
7475
|
sentinel = null;
|
|
7410
|
-
return sentinel ??= `${SENTINEL_PREFIX}${process.pid}${
|
|
7476
|
+
return sentinel ??= `${SENTINEL_PREFIX}${process.pid}${randomBytes3(8).toString("hex")}INVALID`;
|
|
7411
7477
|
}
|
|
7412
7478
|
function removeConfigDir() {
|
|
7413
7479
|
if (configDir)
|
|
@@ -7421,7 +7487,7 @@ function emptyGhConfigDir(env = process.env) {
|
|
|
7421
7487
|
const runTmp = env["LOKI_RUN_TMP"] ?? "";
|
|
7422
7488
|
const root = runTmp !== "" && isDir(runTmp) ? runTmp : tmpdir3();
|
|
7423
7489
|
try {
|
|
7424
|
-
configDir = mkdtempSync2(
|
|
7490
|
+
configDir = mkdtempSync2(join28(root, "loki-gh-config-"));
|
|
7425
7491
|
} catch {
|
|
7426
7492
|
return null;
|
|
7427
7493
|
}
|
|
@@ -7462,7 +7528,7 @@ function hardenCredentialEnv(env, fresh = false) {
|
|
|
7462
7528
|
}
|
|
7463
7529
|
var GITHUB_TOKEN_VARS, SENTINEL_PREFIX = "ghp_LOKIWITHHELDsentinel", sentinel = null, configDir = null, exitHooked = false, sigtermHooked = false, isDir = (p) => {
|
|
7464
7530
|
try {
|
|
7465
|
-
return !lstatSync4(p).isSymbolicLink() &&
|
|
7531
|
+
return !lstatSync4(p).isSymbolicLink() && statSync12(p).isDirectory();
|
|
7466
7532
|
} catch {
|
|
7467
7533
|
return false;
|
|
7468
7534
|
}
|
|
@@ -7588,9 +7654,9 @@ var init_run_cap = __esm(() => {
|
|
|
7588
7654
|
});
|
|
7589
7655
|
|
|
7590
7656
|
// ../../loki-ts/src/util/mutation_proof.ts
|
|
7591
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
7657
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync3, readFileSync as readFileSync19, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync9 } from "fs";
|
|
7592
7658
|
import { tmpdir as tmpdir4 } from "os";
|
|
7593
|
-
import { basename as basename8, dirname as
|
|
7659
|
+
import { basename as basename8, dirname as dirname8, isAbsolute as isAbsolute2, join as join29, relative as relative2 } from "path";
|
|
7594
7660
|
function mutationProof(i) {
|
|
7595
7661
|
const rels = i.wallFiles.map((f) => isAbsolute2(f.path) ? relative2(i.repoDir, f.path) : f.path);
|
|
7596
7662
|
if (rels.length === 0)
|
|
@@ -7608,25 +7674,25 @@ function mutationProof(i) {
|
|
|
7608
7674
|
const deadline = Date.now() + budgetS * 1000, remaining = () => deadline - Date.now();
|
|
7609
7675
|
let parent = null, wt = null, added = false;
|
|
7610
7676
|
try {
|
|
7611
|
-
parent = mkdtempSync3(
|
|
7612
|
-
wt =
|
|
7677
|
+
parent = mkdtempSync3(join29(tmpdir4(), "loki-mutproof-"));
|
|
7678
|
+
wt = join29(parent, "wt");
|
|
7613
7679
|
const add2 = git(i.repoDir, ["worktree", "add", "--detach", wt, i.baseSha], Math.max(1000, Math.min(30000, remaining())));
|
|
7614
7680
|
added = existsSync15(wt) || add2.status === 0;
|
|
7615
7681
|
if (add2.status !== 0)
|
|
7616
7682
|
return np("could not create the base worktree");
|
|
7617
7683
|
for (const rel of rels) {
|
|
7618
|
-
const sealed =
|
|
7684
|
+
const sealed = join29(i.runDir, "wall", basename8(rel)), src = existsSync15(sealed) ? sealed : join29(i.repoDir, rel);
|
|
7619
7685
|
if (!existsSync15(src))
|
|
7620
7686
|
return np(`Wall file missing: ${rel}`);
|
|
7621
|
-
|
|
7622
|
-
|
|
7687
|
+
mkdirSync6(dirname8(join29(wt, rel)), { recursive: true });
|
|
7688
|
+
writeFileSync9(join29(wt, rel), readFileSync19(src));
|
|
7623
7689
|
}
|
|
7624
7690
|
for (const rel of rels) {
|
|
7625
7691
|
const parts = rel.split("/").slice(0, -1);
|
|
7626
7692
|
for (let n2 = 0;n2 <= parts.length; n2++) {
|
|
7627
7693
|
const anc = parts.slice(0, n2).join("/");
|
|
7628
7694
|
for (const d of DEP_DIRS) {
|
|
7629
|
-
const src =
|
|
7695
|
+
const src = join29(i.repoDir, anc, d), dst = join29(wt, anc, d);
|
|
7630
7696
|
if (existsSync15(src) && !existsSync15(dst)) {
|
|
7631
7697
|
try {
|
|
7632
7698
|
symlinkSync(src, dst);
|
|
@@ -7823,7 +7889,7 @@ var init_shell = __esm(() => {
|
|
|
7823
7889
|
});
|
|
7824
7890
|
|
|
7825
7891
|
// ../../loki-ts/src/util/python.ts
|
|
7826
|
-
import { accessSync, constants as constants2, existsSync as existsSync16, statSync as
|
|
7892
|
+
import { accessSync, constants as constants2, existsSync as existsSync16, statSync as statSync13 } from "fs";
|
|
7827
7893
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7828
7894
|
import { tmpdir as tmpdir5 } from "os";
|
|
7829
7895
|
function resolvePython3() {
|
|
@@ -7833,7 +7899,7 @@ function resolvePython3() {
|
|
|
7833
7899
|
const cand = `${dir}/python3`;
|
|
7834
7900
|
try {
|
|
7835
7901
|
accessSync(cand, constants2.X_OK);
|
|
7836
|
-
if (
|
|
7902
|
+
if (statSync13(cand).isFile())
|
|
7837
7903
|
return cand;
|
|
7838
7904
|
} catch {}
|
|
7839
7905
|
}
|
|
@@ -7960,7 +8026,7 @@ var init_python = __esm(() => {
|
|
|
7960
8026
|
init_shell();
|
|
7961
8027
|
});
|
|
7962
8028
|
|
|
7963
|
-
// ../../loki-ts/src/
|
|
8029
|
+
// ../../loki-ts/src/util/ts_signatures.ts
|
|
7964
8030
|
function templateEnd(s, i) {
|
|
7965
8031
|
for (let j = i + 1;j < s.length; ) {
|
|
7966
8032
|
const c = s[j];
|
|
@@ -8686,6 +8752,14 @@ function tsSignatures(src, jsx) {
|
|
|
8686
8752
|
}
|
|
8687
8753
|
return out2;
|
|
8688
8754
|
}
|
|
8755
|
+
var REGEX_PREV = "(,=:[!&|?{;+-*%~^", isComment = (s, i) => s[i] === "/" && (s[i + 1] === "/" || s[i + 1] === "*"), MODS = "(?:(?:public|private|protected|static|readonly|abstract|async|get|set|declare|override)\\s+)*", MEMBER_GRAMMAR, NOT_MEMBER, RESERVED_AFTER_DECORATOR;
|
|
8756
|
+
var init_ts_signatures = __esm(() => {
|
|
8757
|
+
MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
|
|
8758
|
+
NOT_MEMBER = /^(return|const|let|var|if|else|for|while|do|switch|case|default|throw|try|catch|finally|new|await|yield|break|continue|import|export|function|delete|typeof|void)\b/;
|
|
8759
|
+
RESERVED_AFTER_DECORATOR = /^(?:if|else|for|while|do|switch|case|try|catch|finally|return|throw|with|var|let|const|function|new|delete|typeof|void|yield|await|break|continue|import|debugger|instanceof|this|super|null|true|false)(?![\w$])/;
|
|
8760
|
+
});
|
|
8761
|
+
|
|
8762
|
+
// ../../loki-ts/src/features/wall_manifest.ts
|
|
8689
8763
|
function packageRunner(content) {
|
|
8690
8764
|
try {
|
|
8691
8765
|
const pkg = JSON.parse(content);
|
|
@@ -8876,21 +8950,19 @@ var MANIFEST_MAX_LINES = 400, MAX_EXAMPLES = 2, EXAMPLE_LINES = 40, MAX_LAYOUT =
|
|
|
8876
8950
|
const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
|
|
8877
8951
|
return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
|
|
8878
8952
|
}, norm3 = (s) => s.replace(/\r\n?/g, `
|
|
8879
|
-
`), stemOf = (p) => (p.split("/").pop() ?? p).replace(/\.[^.]*$/, ""), byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.content < b.content ? -1 : a.content > b.content ? 1 : 0
|
|
8953
|
+
`), stemOf = (p) => (p.split("/").pop() ?? p).replace(/\.[^.]*$/, ""), byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.content < b.content ? -1 : a.content > b.content ? 1 : 0;
|
|
8880
8954
|
var init_wall_manifest = __esm(() => {
|
|
8881
8955
|
init_python();
|
|
8956
|
+
init_ts_signatures();
|
|
8882
8957
|
TEST_PATH = /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.(py|go)$/;
|
|
8883
8958
|
TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
8884
8959
|
EXAMPLE_EXT = /\.(py|[cm]?[jt]sx?)$/;
|
|
8885
8960
|
GENERIC_STEMS = ["index", "init", "mod", "main"];
|
|
8886
|
-
MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
|
|
8887
|
-
NOT_MEMBER = /^(return|const|let|var|if|else|for|while|do|switch|case|default|throw|try|catch|finally|new|await|yield|break|continue|import|export|function|delete|typeof|void)\b/;
|
|
8888
|
-
RESERVED_AFTER_DECORATOR = /^(?:if|else|for|while|do|switch|case|try|catch|finally|return|throw|with|var|let|const|function|new|delete|typeof|void|yield|await|break|continue|import|debugger|instanceof|this|super|null|true|false)(?![\w$])/;
|
|
8889
8961
|
});
|
|
8890
8962
|
|
|
8891
8963
|
// ../../loki-ts/src/features/wall_manifest_wire.ts
|
|
8892
|
-
import { createHash as
|
|
8893
|
-
import { basename as basename9, dirname as
|
|
8964
|
+
import { createHash as createHash7 } from "crypto";
|
|
8965
|
+
import { basename as basename9, dirname as dirname9 } from "path";
|
|
8894
8966
|
function blobEntries(ls) {
|
|
8895
8967
|
const out2 = [];
|
|
8896
8968
|
for (const raw2 of ls.toString("latin1").split("\x00")) {
|
|
@@ -8920,7 +8992,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
|
|
|
8920
8992
|
return null;
|
|
8921
8993
|
const low = task.toLowerCase().replace(/\\/g, "/");
|
|
8922
8994
|
const token = (n2) => new RegExp(`(?<![A-Za-z0-9_])${n2.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`).test(low);
|
|
8923
|
-
const dirModule = (e) => /^(index|__init__|mod|main)\.[^.]+$/.test(basename9(e.path)) && e.path.includes("/") && token(
|
|
8995
|
+
const dirModule = (e) => /^(index|__init__|mod|main)\.[^.]+$/.test(basename9(e.path)) && e.path.includes("/") && token(dirname9(e.path));
|
|
8924
8996
|
const matched = all.filter((e) => SOURCE.test(e.path) && (token(basename9(e.path)) || token(e.path) || token(basename9(e.path).replace(/\.[^.]*$/, "")) || dirModule(e)));
|
|
8925
8997
|
const spans = (p) => [...low.matchAll(new RegExp(`(?<![A-Za-z0-9_])${p.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`, "g"))].map((m) => [m.index, m.index + m[0].length]);
|
|
8926
8998
|
const occ = new Map(matched.map((e) => [e, spans(e.path)]));
|
|
@@ -8949,7 +9021,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
|
|
|
8949
9021
|
if (at !== cat.length)
|
|
8950
9022
|
return null;
|
|
8951
9023
|
const text2 = buildWallManifest([...files, ...listed.map((e) => ({ path: e.path, content: "" }))], named.map((e) => e.path), matched.map((e) => e.path));
|
|
8952
|
-
return { text: text2, sha256:
|
|
9024
|
+
return { text: text2, sha256: createHash7("sha256").update(text2).digest("hex") };
|
|
8953
9025
|
} catch {
|
|
8954
9026
|
return null;
|
|
8955
9027
|
}
|
|
@@ -8981,12 +9053,12 @@ Example: test('name', () => { assert.strictEqual(fn(1), 2); }); Run: node --tes
|
|
|
8981
9053
|
});
|
|
8982
9054
|
|
|
8983
9055
|
// ../../loki-ts/src/engine10/cache.ts
|
|
8984
|
-
import { createHash as
|
|
8985
|
-
import { existsSync as existsSync17, mkdirSync as
|
|
9056
|
+
import { createHash as createHash8 } from "crypto";
|
|
9057
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
|
|
8986
9058
|
import { resolve as resolve8 } from "path";
|
|
8987
9059
|
function repoKey(originUrl, repoDir) {
|
|
8988
9060
|
const basis = originUrl && originUrl.trim() !== "" ? originUrl : resolve8(repoDir);
|
|
8989
|
-
return
|
|
9061
|
+
return createHash8("sha256").update(basis).digest("hex");
|
|
8990
9062
|
}
|
|
8991
9063
|
function defaultCacheRoot() {
|
|
8992
9064
|
return resolve8(homeLokiDir(), "cache", "v10");
|
|
@@ -8998,7 +9070,7 @@ function readJson3(path, isValid = (_v) => true) {
|
|
|
8998
9070
|
if (!existsSync17(path))
|
|
8999
9071
|
return null;
|
|
9000
9072
|
try {
|
|
9001
|
-
const parsed = JSON.parse(
|
|
9073
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
9002
9074
|
return isValid(parsed) ? parsed : null;
|
|
9003
9075
|
} catch {
|
|
9004
9076
|
return null;
|
|
@@ -9048,12 +9120,12 @@ function speedLikelyFiles(task, map, env = process.env) {
|
|
|
9048
9120
|
var init_lean_select = () => {};
|
|
9049
9121
|
|
|
9050
9122
|
// ../../loki-ts/src/util/claude_model.ts
|
|
9051
|
-
import { readFileSync as
|
|
9123
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
9052
9124
|
import { resolve as resolve9 } from "path";
|
|
9053
9125
|
function resolveClaudeModel(want) {
|
|
9054
9126
|
try {
|
|
9055
9127
|
const path = process.env["LOKI_MODEL_CATALOG"] || resolve9(REPO_ROOT, "providers/model_catalog.json");
|
|
9056
|
-
return JSON.parse(
|
|
9128
|
+
return JSON.parse(readFileSync21(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
|
|
9057
9129
|
} catch {
|
|
9058
9130
|
return want;
|
|
9059
9131
|
}
|
|
@@ -9113,8 +9185,8 @@ function routerMode(env = process.env) {
|
|
|
9113
9185
|
var routerEnabled = (env = process.env) => routerMode(env) === "on";
|
|
9114
9186
|
|
|
9115
9187
|
// ../../loki-ts/src/engine10/cost.ts
|
|
9116
|
-
import { readdirSync as
|
|
9117
|
-
import { join as
|
|
9188
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync22 } from "fs";
|
|
9189
|
+
import { join as join30 } from "path";
|
|
9118
9190
|
function routerTelemetryOn(env = process.env) {
|
|
9119
9191
|
return routerEnabled(env);
|
|
9120
9192
|
}
|
|
@@ -9122,7 +9194,7 @@ function num3(v) {
|
|
|
9122
9194
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
9123
9195
|
}
|
|
9124
9196
|
function resultCostPath(lokiRoot, iteration) {
|
|
9125
|
-
return
|
|
9197
|
+
return join30(lokiRoot, "metrics", `result-cost-${iteration}.json`);
|
|
9126
9198
|
}
|
|
9127
9199
|
function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
9128
9200
|
const out2 = {
|
|
@@ -9153,7 +9225,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9153
9225
|
const parsed = new Map;
|
|
9154
9226
|
for (const iter of iterations) {
|
|
9155
9227
|
try {
|
|
9156
|
-
parsed.set(iter, JSON.parse(
|
|
9228
|
+
parsed.set(iter, JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")));
|
|
9157
9229
|
} catch {}
|
|
9158
9230
|
}
|
|
9159
9231
|
const own = new Set(iterations);
|
|
@@ -9162,7 +9234,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9162
9234
|
if (own.has(iter))
|
|
9163
9235
|
continue;
|
|
9164
9236
|
try {
|
|
9165
|
-
lookup.push({ iter, rec: JSON.parse(
|
|
9237
|
+
lookup.push({ iter, rec: JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")) });
|
|
9166
9238
|
} catch {}
|
|
9167
9239
|
}
|
|
9168
9240
|
const verdicts = hooks.costRecords?.resumeVerdicts(lookup) ?? { ambiguous: [], separate: [] };
|
|
@@ -9267,19 +9339,19 @@ import {
|
|
|
9267
9339
|
fstatSync as fstatSync5,
|
|
9268
9340
|
fsyncSync as fsyncSync2,
|
|
9269
9341
|
lstatSync as lstatSync6,
|
|
9270
|
-
mkdirSync as
|
|
9342
|
+
mkdirSync as mkdirSync9,
|
|
9271
9343
|
openSync as openSync6,
|
|
9272
9344
|
readSync as readSync5,
|
|
9273
|
-
renameSync as
|
|
9345
|
+
renameSync as renameSync5,
|
|
9274
9346
|
rmSync as rmSync6,
|
|
9275
|
-
statSync as
|
|
9276
|
-
unlinkSync as
|
|
9277
|
-
writeFileSync as
|
|
9347
|
+
statSync as statSync14,
|
|
9348
|
+
unlinkSync as unlinkSync4,
|
|
9349
|
+
writeFileSync as writeFileSync12,
|
|
9278
9350
|
writeSync as writeSync3
|
|
9279
9351
|
} from "fs";
|
|
9280
|
-
import { dirname as
|
|
9352
|
+
import { dirname as dirname10 } from "path";
|
|
9281
9353
|
function durableWriteThenRename(target, body) {
|
|
9282
|
-
|
|
9354
|
+
mkdirSync9(dirname10(target), { recursive: true });
|
|
9283
9355
|
const tmp = tmpPathFor(target);
|
|
9284
9356
|
const fd = openSync6(tmp, "w");
|
|
9285
9357
|
try {
|
|
@@ -9288,13 +9360,13 @@ function durableWriteThenRename(target, body) {
|
|
|
9288
9360
|
} finally {
|
|
9289
9361
|
closeSync6(fd);
|
|
9290
9362
|
}
|
|
9291
|
-
|
|
9363
|
+
renameSync5(tmp, target);
|
|
9292
9364
|
fsyncParentDir(target);
|
|
9293
9365
|
}
|
|
9294
9366
|
function fsyncParentDir(target) {
|
|
9295
9367
|
let dfd = null;
|
|
9296
9368
|
try {
|
|
9297
|
-
dfd = openSync6(
|
|
9369
|
+
dfd = openSync6(dirname10(target), "r");
|
|
9298
9370
|
fsyncSync2(dfd);
|
|
9299
9371
|
} catch {} finally {
|
|
9300
9372
|
if (dfd !== null) {
|
|
@@ -9317,14 +9389,14 @@ var init_atomic = __esm(() => {
|
|
|
9317
9389
|
});
|
|
9318
9390
|
|
|
9319
9391
|
// ../../loki-ts/src/runner/budget.ts
|
|
9320
|
-
import { existsSync as existsSync18, mkdirSync as
|
|
9321
|
-
import { dirname as
|
|
9392
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync11, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
|
|
9393
|
+
import { dirname as dirname11, join as join31, resolve as resolve10 } from "path";
|
|
9322
9394
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9323
9395
|
function loadDefaultMaxCostUsd() {
|
|
9324
|
-
const here =
|
|
9396
|
+
const here = dirname11(fileURLToPath2(import.meta.url));
|
|
9325
9397
|
for (const rel of [["..", "..", "data"], ["..", "data"]]) {
|
|
9326
9398
|
try {
|
|
9327
|
-
const j = JSON.parse(
|
|
9399
|
+
const j = JSON.parse(readFileSync23(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
|
|
9328
9400
|
if (typeof j.default_max_cost_usd === "number" && Number.isFinite(j.default_max_cost_usd) && j.default_max_cost_usd > 0)
|
|
9329
9401
|
return j.default_max_cost_usd;
|
|
9330
9402
|
} catch {}
|
|
@@ -9333,11 +9405,11 @@ function loadDefaultMaxCostUsd() {
|
|
|
9333
9405
|
}
|
|
9334
9406
|
function _loadPricing() {
|
|
9335
9407
|
try {
|
|
9336
|
-
const moduleDir =
|
|
9408
|
+
const moduleDir = dirname11(fileURLToPath2(import.meta.url));
|
|
9337
9409
|
const jsonPath = resolve10(moduleDir, "..", "..", "data", "model-pricing.json");
|
|
9338
9410
|
if (!existsSync18(jsonPath))
|
|
9339
9411
|
return _FALLBACK_PRICING;
|
|
9340
|
-
const raw2 = JSON.parse(
|
|
9412
|
+
const raw2 = JSON.parse(readFileSync23(jsonPath, "utf8"));
|
|
9341
9413
|
const pricing2 = raw2["pricing"];
|
|
9342
9414
|
if (!pricing2 || typeof pricing2 !== "object")
|
|
9343
9415
|
return _FALLBACK_PRICING;
|
|
@@ -9467,7 +9539,7 @@ var init_model_rank = __esm(() => {
|
|
|
9467
9539
|
});
|
|
9468
9540
|
|
|
9469
9541
|
// ../../loki-ts/src/engine10/sizing.ts
|
|
9470
|
-
import { existsSync as existsSync19, readFileSync as
|
|
9542
|
+
import { existsSync as existsSync19, readFileSync as readFileSync24 } from "fs";
|
|
9471
9543
|
import { basename as basename10 } from "path";
|
|
9472
9544
|
function namedFiles(task, map) {
|
|
9473
9545
|
return (map?.files ?? []).filter((f) => task.toLowerCase().includes(basename10(f).toLowerCase()));
|
|
@@ -9489,7 +9561,7 @@ function loadRepoMap(repomapRef) {
|
|
|
9489
9561
|
if (!repomapRef || !existsSync19(repomapRef))
|
|
9490
9562
|
return null;
|
|
9491
9563
|
try {
|
|
9492
|
-
return JSON.parse(
|
|
9564
|
+
return JSON.parse(readFileSync24(repomapRef, "utf8"));
|
|
9493
9565
|
} catch {
|
|
9494
9566
|
return null;
|
|
9495
9567
|
}
|
|
@@ -9520,8 +9592,8 @@ var init_sizing = __esm(() => {
|
|
|
9520
9592
|
});
|
|
9521
9593
|
|
|
9522
9594
|
// ../../loki-ts/src/project_model/schema.ts
|
|
9523
|
-
import { existsSync as existsSync20, lstatSync as lstatSync7, realpathSync as realpathSync15, statSync as
|
|
9524
|
-
import { isAbsolute as isAbsolute3, join as
|
|
9595
|
+
import { existsSync as existsSync20, lstatSync as lstatSync7, realpathSync as realpathSync15, statSync as statSync15 } from "fs";
|
|
9596
|
+
import { isAbsolute as isAbsolute3, join as join33, normalize, sep as sep9 } from "path";
|
|
9525
9597
|
function safeRel(p) {
|
|
9526
9598
|
const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
|
|
9527
9599
|
if (n2 === "" || isAbsolute3(n2) || n2 === ".." || n2.startsWith("../"))
|
|
@@ -9530,7 +9602,7 @@ function safeRel(p) {
|
|
|
9530
9602
|
}
|
|
9531
9603
|
function insideRepo(repoDir, rel) {
|
|
9532
9604
|
try {
|
|
9533
|
-
const real2 = realpathSync15(
|
|
9605
|
+
const real2 = realpathSync15(join33(repoDir, rel));
|
|
9534
9606
|
const base = realpathSync15(repoDir);
|
|
9535
9607
|
return real2 === base || real2.startsWith(base + sep9);
|
|
9536
9608
|
} catch {
|
|
@@ -9543,8 +9615,8 @@ function checkCitation(repoDir, raw2) {
|
|
|
9543
9615
|
const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
|
|
9544
9616
|
if (rel === null)
|
|
9545
9617
|
return `citation "${raw2}" is not a repo-relative path`;
|
|
9546
|
-
const abs =
|
|
9547
|
-
if (!existsSync20(abs) || !
|
|
9618
|
+
const abs = join33(repoDir, rel);
|
|
9619
|
+
if (!existsSync20(abs) || !statSync15(abs).isFile() || !insideRepo(repoDir, rel))
|
|
9548
9620
|
return `citation "${raw2}" names a file that does not exist`;
|
|
9549
9621
|
return null;
|
|
9550
9622
|
}
|
|
@@ -9555,7 +9627,7 @@ function checkFingerprint(repoDir, raw2) {
|
|
|
9555
9627
|
if (rel === null)
|
|
9556
9628
|
return `fingerprint "${raw2}" is not a repo-relative path`;
|
|
9557
9629
|
try {
|
|
9558
|
-
if (!lstatSync7(
|
|
9630
|
+
if (!lstatSync7(join33(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
|
|
9559
9631
|
return `fingerprint "${raw2}" is not a regular file inside the repo`;
|
|
9560
9632
|
} catch {
|
|
9561
9633
|
return `fingerprint "${raw2}" names a file that does not exist`;
|
|
@@ -9583,8 +9655,8 @@ function checkDir(repoDir, v, where, errs) {
|
|
|
9583
9655
|
errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
|
|
9584
9656
|
return ".";
|
|
9585
9657
|
}
|
|
9586
|
-
const abs =
|
|
9587
|
-
if (!existsSync20(abs) || !
|
|
9658
|
+
const abs = join33(repoDir, rel);
|
|
9659
|
+
if (!existsSync20(abs) || !statSync15(abs).isDirectory() || !insideRepo(repoDir, rel))
|
|
9588
9660
|
errs.push(`${where}: directory "${rel}" does not exist`);
|
|
9589
9661
|
return rel;
|
|
9590
9662
|
}
|
|
@@ -9686,9 +9758,9 @@ var init_schema = __esm(() => {
|
|
|
9686
9758
|
});
|
|
9687
9759
|
|
|
9688
9760
|
// ../../loki-ts/src/project_model/gather.ts
|
|
9689
|
-
import { createHash as
|
|
9690
|
-
import { closeSync as closeSync7, lstatSync as lstatSync8, openSync as openSync7, readFileSync as
|
|
9691
|
-
import { dirname as
|
|
9761
|
+
import { createHash as createHash9 } from "crypto";
|
|
9762
|
+
import { closeSync as closeSync7, lstatSync as lstatSync8, openSync as openSync7, readFileSync as readFileSync25, readSync as readSync6 } from "fs";
|
|
9763
|
+
import { dirname as dirname12, join as join34 } from "path";
|
|
9692
9764
|
function listShallow(repoDir) {
|
|
9693
9765
|
try {
|
|
9694
9766
|
const out2 = safeGit(repoDir, ["ls-files", "-z"], { maxBuffer: 256 * 1024 * 1024 });
|
|
@@ -9698,10 +9770,10 @@ function listShallow(repoDir) {
|
|
|
9698
9770
|
}
|
|
9699
9771
|
}
|
|
9700
9772
|
function shallowDirs(repoDir) {
|
|
9701
|
-
return [...new Set(listShallow(repoDir).map((p) =>
|
|
9773
|
+
return [...new Set(listShallow(repoDir).map((p) => dirname12(p)))].sort();
|
|
9702
9774
|
}
|
|
9703
9775
|
function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
9704
|
-
const h =
|
|
9776
|
+
const h = createHash9("sha256");
|
|
9705
9777
|
h.update(`${MODEL_REV}
|
|
9706
9778
|
`);
|
|
9707
9779
|
if (committedHash !== null)
|
|
@@ -9714,9 +9786,9 @@ function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
|
9714
9786
|
if (checkFingerprint(repoDir, f) !== null)
|
|
9715
9787
|
h.update("invalid");
|
|
9716
9788
|
else {
|
|
9717
|
-
const abs =
|
|
9789
|
+
const abs = join34(repoDir, f);
|
|
9718
9790
|
const size = lstatSync8(abs).size;
|
|
9719
|
-
h.update(size <= GATHER_CAPS.maxHashBytes ?
|
|
9791
|
+
h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync25(abs) : `too-large:${size}`);
|
|
9720
9792
|
}
|
|
9721
9793
|
} catch {
|
|
9722
9794
|
h.update("missing");
|
|
@@ -9741,7 +9813,7 @@ function committedModelHash(raw2) {
|
|
|
9741
9813
|
if (typeof raw2 !== "object" || raw2 === null || Array.isArray(raw2))
|
|
9742
9814
|
return null;
|
|
9743
9815
|
const { key: _key, expiresAt: _exp, ...rest } = raw2;
|
|
9744
|
-
return
|
|
9816
|
+
return createHash9("sha256").update(JSON.stringify(rest)).digest("hex");
|
|
9745
9817
|
}
|
|
9746
9818
|
var GATHER_CAPS, depthOf = (p) => p.split("/").length, MODEL_REV = "model-rev:2";
|
|
9747
9819
|
var init_gather = __esm(() => {
|
|
@@ -9759,14 +9831,14 @@ var init_gather = __esm(() => {
|
|
|
9759
9831
|
});
|
|
9760
9832
|
|
|
9761
9833
|
// ../../loki-ts/src/project_model/discover.ts
|
|
9762
|
-
import { existsSync as existsSync21, mkdirSync as
|
|
9763
|
-
import { join as
|
|
9834
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync26, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "fs";
|
|
9835
|
+
import { join as join35 } from "path";
|
|
9764
9836
|
function projectModelEnabled(env = process.env) {
|
|
9765
9837
|
return env["LOKI_E10_PROJECT_MODEL"] !== "0";
|
|
9766
9838
|
}
|
|
9767
9839
|
function loadCached(repoDir, now2 = Date.now()) {
|
|
9768
9840
|
try {
|
|
9769
|
-
return parseCached(repoDir, JSON.parse(
|
|
9841
|
+
return parseCached(repoDir, JSON.parse(readFileSync26(join35(repoDir, PROJECT_FILE), "utf8")), now2);
|
|
9770
9842
|
} catch {
|
|
9771
9843
|
return null;
|
|
9772
9844
|
}
|
|
@@ -9781,7 +9853,7 @@ function loadCommitted(repoDir) {
|
|
|
9781
9853
|
};
|
|
9782
9854
|
let raw2;
|
|
9783
9855
|
try {
|
|
9784
|
-
raw2 = JSON.parse(
|
|
9856
|
+
raw2 = JSON.parse(readFileSync26(join35(repoDir, PROJECT_FILE), "utf8"));
|
|
9785
9857
|
} catch (e) {
|
|
9786
9858
|
return ignore(`unreadable JSON (${e instanceof Error ? e.message : String(e)})`);
|
|
9787
9859
|
}
|
|
@@ -9854,7 +9926,7 @@ var clean2 = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/"
|
|
|
9854
9926
|
var init_api = () => {};
|
|
9855
9927
|
|
|
9856
9928
|
// ../../loki-ts/src/project_model/resolve.ts
|
|
9857
|
-
import { join as
|
|
9929
|
+
import { join as join36 } from "path";
|
|
9858
9930
|
function loadProjectApi(repoDir, env = process.env) {
|
|
9859
9931
|
if (!projectModelEnabled(env))
|
|
9860
9932
|
return null;
|
|
@@ -9876,7 +9948,7 @@ function siteFor(api, repoDir, file) {
|
|
|
9876
9948
|
const rel = api.relativeToRoot(file);
|
|
9877
9949
|
if (!pkg || pkg.root === "." || rel === null)
|
|
9878
9950
|
return null;
|
|
9879
|
-
return { pkg, root: pkg.root, cwd:
|
|
9951
|
+
return { pkg, root: pkg.root, cwd: join36(repoDir, pkg.root), file: rel };
|
|
9880
9952
|
}
|
|
9881
9953
|
function groupByPackage(api, repoDir, files) {
|
|
9882
9954
|
const out2 = new Map;
|
|
@@ -9907,11 +9979,99 @@ var init_scope = __esm(() => {
|
|
|
9907
9979
|
dropped = new WeakMap;
|
|
9908
9980
|
});
|
|
9909
9981
|
|
|
9982
|
+
// ../../loki-ts/src/features/runner_config.ts
|
|
9983
|
+
import { readFileSync as readFileSync27 } from "fs";
|
|
9984
|
+
import { join as join37 } from "path";
|
|
9985
|
+
function relevant(shared, text2) {
|
|
9986
|
+
if (shared === "go") {
|
|
9987
|
+
let blk = false;
|
|
9988
|
+
return lines(text2).filter((l) => {
|
|
9989
|
+
if (blk) {
|
|
9990
|
+
if (l === ")")
|
|
9991
|
+
blk = false;
|
|
9992
|
+
return true;
|
|
9993
|
+
}
|
|
9994
|
+
if (/^replace\s*\($/.test(l)) {
|
|
9995
|
+
blk = true;
|
|
9996
|
+
return true;
|
|
9997
|
+
}
|
|
9998
|
+
return /^replace\s/.test(l);
|
|
9999
|
+
}).join(`
|
|
10000
|
+
`);
|
|
10001
|
+
}
|
|
10002
|
+
if (shared === "tsconfig") {
|
|
10003
|
+
const j = jsonc(text2);
|
|
10004
|
+
const o = j.compilerOptions ?? {};
|
|
10005
|
+
return JSON.stringify(["extends", "baseUrl", "paths", "rootDirs", "moduleSuffixes", "customConditions"].map((k) => k === "extends" ? j["extends"] : o[k]));
|
|
10006
|
+
}
|
|
10007
|
+
if (Array.isArray(shared)) {
|
|
10008
|
+
const j = JSON.parse(text2);
|
|
10009
|
+
return JSON.stringify([shared.map((k) => j[k]), depGuard(j)]);
|
|
10010
|
+
}
|
|
10011
|
+
let on = false;
|
|
10012
|
+
return lines(text2).filter((l) => {
|
|
10013
|
+
if (/^\[[^\]]+\]$/.test(l))
|
|
10014
|
+
on = shared.test(l);
|
|
10015
|
+
return on || KW.test(l);
|
|
10016
|
+
}).join(`
|
|
10017
|
+
`);
|
|
10018
|
+
}
|
|
10019
|
+
function testConfigChanged(repoDir, baseSha, changed) {
|
|
10020
|
+
return changed.filter((f) => {
|
|
10021
|
+
const spec = RUNNER_CONFIGS.find((s) => s.re.test(f));
|
|
10022
|
+
if (!spec)
|
|
10023
|
+
return false;
|
|
10024
|
+
if (!spec.shared)
|
|
10025
|
+
return true;
|
|
10026
|
+
const b = baseText(repoDir, baseSha, f), h = headText(repoDir, f);
|
|
10027
|
+
if (b === h)
|
|
10028
|
+
return false;
|
|
10029
|
+
try {
|
|
10030
|
+
return relevant(spec.shared, b || "{}") !== relevant(spec.shared, h || "{}");
|
|
10031
|
+
} catch {
|
|
10032
|
+
return true;
|
|
10033
|
+
}
|
|
10034
|
+
});
|
|
10035
|
+
}
|
|
10036
|
+
var PKG_KEYS, RUNNER_CONFIGS, DEP_MAPS, RUNNER_DEP, LOCAL_SPEC, depGuard = (j) => DEP_MAPS.map((m) => Object.entries(j[m] ?? {}).filter(([k, v]) => RUNNER_DEP.test(k) || LOCAL_SPEC.test(String(v)))), KW, lines = (t) => t.split(`
|
|
10037
|
+
`).map((l) => l.trim()).filter((l) => l && !l.startsWith("#")), jsonc = (t) => JSON.parse(t.replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, "$1").replace(/,(\s*[}\]])/g, "$1")), baseText = (dir, base, f) => {
|
|
10038
|
+
try {
|
|
10039
|
+
return safeGit(dir, ["show", `${base}:./${f}`], { timeout: 20000, maxBuffer: 64 << 20 });
|
|
10040
|
+
} catch {
|
|
10041
|
+
return "";
|
|
10042
|
+
}
|
|
10043
|
+
}, headText = (dir, f) => {
|
|
10044
|
+
try {
|
|
10045
|
+
return readFileSync27(join37(dir, f), "utf8");
|
|
10046
|
+
} catch {
|
|
10047
|
+
return "";
|
|
10048
|
+
}
|
|
10049
|
+
};
|
|
10050
|
+
var init_runner_config = __esm(() => {
|
|
10051
|
+
init_safe_git();
|
|
10052
|
+
PKG_KEYS = ["scripts", "jest", "overrides", "resolutions", "imports", "pnpm", "mocha", "vitest", "ava", "c8", "nyc", "babel", "bun"];
|
|
10053
|
+
RUNNER_CONFIGS = [
|
|
10054
|
+
{ re: /(^|\/)(conftest\.py|tox\.ini|bunfig\.toml|\.?pytest\.(ini|toml)|\.mocharc[\w.]*)$/ },
|
|
10055
|
+
{ re: /(^|\/)(vitest|vite|jest)\.(config|workspace)\.[\w.]+$/ },
|
|
10056
|
+
{ re: /(^|\/)\.cargo\/config(\.toml)?$/ },
|
|
10057
|
+
{ re: /(^|\/)package\.json$/, shared: PKG_KEYS },
|
|
10058
|
+
{ re: /(^|\/)pyproject\.toml$/, shared: /^\[tool\.pytest/ },
|
|
10059
|
+
{ re: /(^|\/)setup\.cfg$/, shared: /^\[tool:pytest\]/ },
|
|
10060
|
+
{ re: /(^|\/)go\.mod$/, shared: "go" },
|
|
10061
|
+
{ re: /(^|\/)Cargo\.toml$/, shared: /^\[(patch|replace)\b/ },
|
|
10062
|
+
{ re: /(^|\/)[jt]sconfig(\.[\w-]+)?\.json$/, shared: "tsconfig" }
|
|
10063
|
+
];
|
|
10064
|
+
DEP_MAPS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
|
10065
|
+
RUNNER_DEP = /^(jest|vitest|mocha|ava|c8|nyc|pytest[\w-]*|@jest\/.*|ts-jest|babel-jest)$/;
|
|
10066
|
+
LOCAL_SPEC = /^(file|link|npm|workspace):/;
|
|
10067
|
+
KW = /pytest|jest|mocha|vitest|addopts|testpaths/i;
|
|
10068
|
+
});
|
|
10069
|
+
|
|
9910
10070
|
// ../../loki-ts/src/e10ext/assert_delta.ts
|
|
9911
10071
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
9912
|
-
import { mkdtempSync as mkdtempSync4, readFileSync as
|
|
10072
|
+
import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync28, rmSync as rmSync8 } from "fs";
|
|
9913
10073
|
import { tmpdir as tmpdir6 } from "os";
|
|
9914
|
-
import { join as
|
|
10074
|
+
import { join as join38 } from "path";
|
|
9915
10075
|
import { basename as basename11 } from "path";
|
|
9916
10076
|
function classifyAssertDelta(inp) {
|
|
9917
10077
|
const f = basename11(inp.path);
|
|
@@ -9921,7 +10081,7 @@ function classifyAssertDelta(inp) {
|
|
|
9921
10081
|
return WEAKENED;
|
|
9922
10082
|
let cwd = "";
|
|
9923
10083
|
try {
|
|
9924
|
-
cwd = mkdtempSync4(
|
|
10084
|
+
cwd = mkdtempSync4(join38(tmpdir6(), "loki-ad-"));
|
|
9925
10085
|
const raw2 = execFileSync5("python3", ["-I", "-c", PY], {
|
|
9926
10086
|
input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
|
|
9927
10087
|
cwd,
|
|
@@ -9952,7 +10112,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
|
|
|
9952
10112
|
return null;
|
|
9953
10113
|
try {
|
|
9954
10114
|
const show = (ref) => safeGit(repoDir, ["show", `${ref}:${path}`]);
|
|
9955
|
-
const head = headRef ? show(headRef) :
|
|
10115
|
+
const head = headRef ? show(headRef) : readFileSync28(join38(repoDir, path), "utf8");
|
|
9956
10116
|
const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
|
|
9957
10117
|
return r[0]?.kind === "literal-only" ? r.map((e) => `assertion value changed (not shown to be required by the task): ${e.file}:${e.line} ${e.old} -> ${e.new}`) : null;
|
|
9958
10118
|
} catch {
|
|
@@ -10142,9 +10302,9 @@ function hasRealTestFailure(out2) {
|
|
|
10142
10302
|
return /^\s*Tests:?\s+.*\b[1-9]\d* failed\b/m.test(out2) || /^(?:=+ )?.*\b[1-9]\d* failed\b.* in [\d.]+s/m.test(out2) || /^FAILED \S+::/m.test(out2) || /^(?:#|\u2139) fail [1-9]/m.test(out2) || /^\s*[1-9]\d*\s+fail\s*$/m.test(out2) || /^\s*(?:FAIL|\u00D7)\s+\S.*\s>\s/m.test(out2) || /\bAssertionError\b/.test(out2);
|
|
10143
10303
|
}
|
|
10144
10304
|
function firstLoadLine(out2) {
|
|
10145
|
-
const
|
|
10305
|
+
const lines2 = out2.split(`
|
|
10146
10306
|
`).map((l2) => l2.trim()).filter(Boolean);
|
|
10147
|
-
const l =
|
|
10307
|
+
const l = lines2.find((x) => /^(?:E\s+)?(?:Error|\w*Error|ERROR|Failed|FAIL)\b|Cannot find|Interrupted:|No test files found/.test(x)) ?? lines2[0] ?? "";
|
|
10148
10308
|
return l.replace(/\s+/g, " ").slice(0, 160);
|
|
10149
10309
|
}
|
|
10150
10310
|
function classifyRunnerOutput(out2) {
|
|
@@ -10176,7 +10336,7 @@ var init_runner_errors = __esm(() => {
|
|
|
10176
10336
|
});
|
|
10177
10337
|
|
|
10178
10338
|
// ../../loki-ts/src/engine10/testmap.ts
|
|
10179
|
-
import { basename as basename12, extname, join as
|
|
10339
|
+
import { basename as basename12, extname, join as join39, relative as relative3, sep as sep10 } from "path";
|
|
10180
10340
|
function isTestFile(rel) {
|
|
10181
10341
|
const name = basename12(rel);
|
|
10182
10342
|
return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
|
|
@@ -10206,8 +10366,8 @@ var init_testmap = __esm(() => {
|
|
|
10206
10366
|
});
|
|
10207
10367
|
|
|
10208
10368
|
// ../../loki-ts/src/util/check_result.ts
|
|
10209
|
-
import { mkdirSync as
|
|
10210
|
-
import { basename as basename13, join as
|
|
10369
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync29, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "fs";
|
|
10370
|
+
import { basename as basename13, join as join40 } from "path";
|
|
10211
10371
|
function plainTestEnv(base = process.env) {
|
|
10212
10372
|
const e = {};
|
|
10213
10373
|
for (const [k, v] of Object.entries(tokenFreeEnv(base)))
|
|
@@ -10283,12 +10443,12 @@ function unittestCount(out2, ok) {
|
|
|
10283
10443
|
return Math.max(0, +r[1] - +(v.match(/skipped=(\d+)/)?.[1] ?? 0) - +(v.match(/expected failures=(\d+)/)?.[1] ?? 0));
|
|
10284
10444
|
}
|
|
10285
10445
|
function goCount(out2, ok) {
|
|
10286
|
-
const
|
|
10446
|
+
const lines2 = out2.split(`
|
|
10287
10447
|
`);
|
|
10288
|
-
if (!
|
|
10448
|
+
if (!lines2.some((x) => GO_PKG_RE.test(x)))
|
|
10289
10449
|
return;
|
|
10290
|
-
const last = new Map, seen = new Set, skip = new Set, failPkg = ok !== true &&
|
|
10291
|
-
for (const x of
|
|
10450
|
+
const last = new Map, seen = new Set, skip = new Set, failPkg = ok !== true && lines2.some((x) => /^FAIL\s+\S+\s+[\d.]+s\b/.test(x));
|
|
10451
|
+
for (const x of lines2) {
|
|
10292
10452
|
const r = x.match(/^=== RUN\s+(\S+)/), m = x.match(/^--- (PASS|FAIL|SKIP): (\S+)/);
|
|
10293
10453
|
if (r)
|
|
10294
10454
|
seen.add(r[1]);
|
|
@@ -10305,9 +10465,9 @@ function goCount(out2, ok) {
|
|
|
10305
10465
|
const t = [...last.values()].filter((v) => v !== "SKIP").length;
|
|
10306
10466
|
if (t > 0)
|
|
10307
10467
|
return t;
|
|
10308
|
-
if (
|
|
10468
|
+
if (lines2.some((x) => /^(?:=== RUN|PASS$|FAIL$|testing: warning: no tests to run)/.test(x)))
|
|
10309
10469
|
return 0;
|
|
10310
|
-
return
|
|
10470
|
+
return lines2.filter((x) => GO_PKG_RE.test(x)).every((x) => /\[no tests? (?:files|to run)\]/.test(x)) ? 0 : null;
|
|
10311
10471
|
}
|
|
10312
10472
|
function goRunner(cmd, args) {
|
|
10313
10473
|
const segs = args ? [[cmd, ...args]] : cmd.split(/&&|\|\||[;|\n]/).map((x) => x.trim().split(/\s+/).filter(Boolean));
|
|
@@ -10331,7 +10491,7 @@ function goRunner(cmd, args) {
|
|
|
10331
10491
|
return {};
|
|
10332
10492
|
}
|
|
10333
10493
|
function goJson(out2) {
|
|
10334
|
-
let any = false,
|
|
10494
|
+
let any = false, failed2 = false;
|
|
10335
10495
|
for (const x of out2.split(`
|
|
10336
10496
|
`)) {
|
|
10337
10497
|
if (!x.trim() || /^go: /.test(x))
|
|
@@ -10346,9 +10506,9 @@ function goJson(out2) {
|
|
|
10346
10506
|
return null;
|
|
10347
10507
|
any = true;
|
|
10348
10508
|
if (e.Action === "fail")
|
|
10349
|
-
|
|
10509
|
+
failed2 = true;
|
|
10350
10510
|
}
|
|
10351
|
-
return any ? { failed } : null;
|
|
10511
|
+
return any ? { failed: failed2 } : null;
|
|
10352
10512
|
}
|
|
10353
10513
|
function skipped(raw2) {
|
|
10354
10514
|
return stripAnsi(raw2).split(`
|
|
@@ -10416,7 +10576,7 @@ function markExecuted(a, executed) {
|
|
|
10416
10576
|
return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) };
|
|
10417
10577
|
}
|
|
10418
10578
|
function classifyRisk(files, env = process.env) {
|
|
10419
|
-
const
|
|
10579
|
+
const lines2 = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
|
|
10420
10580
|
const sensitive = files.flatMap((f) => {
|
|
10421
10581
|
const hit = SENSITIVE.find(([, re]) => re.test(f.path));
|
|
10422
10582
|
return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
|
|
@@ -10434,14 +10594,14 @@ function classifyRisk(files, env = process.env) {
|
|
|
10434
10594
|
high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
|
|
10435
10595
|
if (files.length >= FILES_HIGH)
|
|
10436
10596
|
high.push(`${files.length} files >= ${FILES_HIGH}`);
|
|
10437
|
-
if (
|
|
10438
|
-
high.push(`${
|
|
10597
|
+
if (lines2 >= LINES_HIGH)
|
|
10598
|
+
high.push(`${lines2} changed lines >= ${LINES_HIGH}`);
|
|
10439
10599
|
if (testsModified.length)
|
|
10440
10600
|
med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
|
|
10441
10601
|
if (files.length >= FILES_MEDIUM)
|
|
10442
10602
|
med.push(`${files.length} files >= ${FILES_MEDIUM}`);
|
|
10443
|
-
if (
|
|
10444
|
-
med.push(`${
|
|
10603
|
+
if (lines2 >= LINES_MEDIUM)
|
|
10604
|
+
med.push(`${lines2} changed lines >= ${LINES_MEDIUM}`);
|
|
10445
10605
|
const computed = high.length ? "high" : med.length ? "medium" : "low";
|
|
10446
10606
|
const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
|
|
10447
10607
|
let tier = computed, override, ignored;
|
|
@@ -10464,7 +10624,7 @@ function classifyRisk(files, env = process.env) {
|
|
|
10464
10624
|
tier,
|
|
10465
10625
|
computed,
|
|
10466
10626
|
reasons,
|
|
10467
|
-
stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10627
|
+
stats: { files: files.length, lines: lines2, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10468
10628
|
checks_selected: plan.selected,
|
|
10469
10629
|
checks_deferred,
|
|
10470
10630
|
...override ? { override } : {},
|
|
@@ -10499,7 +10659,7 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10499
10659
|
return f;
|
|
10500
10660
|
let n2 = 0;
|
|
10501
10661
|
try {
|
|
10502
|
-
n2 =
|
|
10662
|
+
n2 = readFileSync29(join40(repoDir, path), "utf8").split(`
|
|
10503
10663
|
`).length;
|
|
10504
10664
|
} catch {}
|
|
10505
10665
|
return { path, added: n2, deleted: 0, status: "added" };
|
|
@@ -10507,11 +10667,11 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10507
10667
|
}
|
|
10508
10668
|
function recordRisk(repoDir, a) {
|
|
10509
10669
|
try {
|
|
10510
|
-
const dir =
|
|
10511
|
-
|
|
10512
|
-
|
|
10670
|
+
const dir = join40(repoDir, ".loki", "state"), p = join40(dir, "risk-tier.json");
|
|
10671
|
+
mkdirSync12(dir, { recursive: true });
|
|
10672
|
+
writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
|
|
10513
10673
|
`);
|
|
10514
|
-
|
|
10674
|
+
renameSync6(`${p}.tmp`, p);
|
|
10515
10675
|
return p;
|
|
10516
10676
|
} catch {
|
|
10517
10677
|
return null;
|
|
@@ -10552,10 +10712,10 @@ var init_check_result = __esm(() => {
|
|
|
10552
10712
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10553
10713
|
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
|
|
10554
10714
|
import { tmpdir as tmpdir7 } from "os";
|
|
10555
|
-
import { basename as basename14, extname as extname2, join as
|
|
10715
|
+
import { basename as basename14, extname as extname2, join as join41 } from "path";
|
|
10556
10716
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
10557
10717
|
return changed.filter((f) => {
|
|
10558
|
-
if (out2.includes(f) || out2.includes(
|
|
10718
|
+
if (out2.includes(f) || out2.includes(join41(repoDir, f)))
|
|
10559
10719
|
return true;
|
|
10560
10720
|
const segs = f.split("/");
|
|
10561
10721
|
if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
|
|
@@ -10666,7 +10826,7 @@ async function loadErrorIsHarnessOwned(i) {
|
|
|
10666
10826
|
return false;
|
|
10667
10827
|
if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
|
|
10668
10828
|
return false;
|
|
10669
|
-
const dir = mkdtempSync5(
|
|
10829
|
+
const dir = mkdtempSync5(join41(tmpdir7(), "e10-loadown-"));
|
|
10670
10830
|
const git3 = (args) => {
|
|
10671
10831
|
safeGit(i.repoDir, args, { stdio: "ignore" });
|
|
10672
10832
|
};
|
|
@@ -10698,7 +10858,7 @@ var init_load_owner = __esm(() => {
|
|
|
10698
10858
|
});
|
|
10699
10859
|
|
|
10700
10860
|
// ../../loki-ts/src/util/wall_owned.ts
|
|
10701
|
-
import { isAbsolute as isAbsolute4, join as
|
|
10861
|
+
import { isAbsolute as isAbsolute4, join as join42, relative as relative4 } from "path";
|
|
10702
10862
|
function failingLocations(raw2, cwd, repoDir) {
|
|
10703
10863
|
const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
|
|
10704
10864
|
`), files = new Set;
|
|
@@ -10730,7 +10890,7 @@ function failingLocations(raw2, cwd, repoDir) {
|
|
|
10730
10890
|
}
|
|
10731
10891
|
if (errors2 === 0 || located !== errors2)
|
|
10732
10892
|
return null;
|
|
10733
|
-
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f :
|
|
10893
|
+
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join42(cwd, f)));
|
|
10734
10894
|
}
|
|
10735
10895
|
function wallOwnedFailure(raw2, cwd, repoDir, wall) {
|
|
10736
10896
|
const locs = failingLocations(raw2, cwd, repoDir);
|
|
@@ -10759,11 +10919,11 @@ var init_state = __esm(() => {
|
|
|
10759
10919
|
});
|
|
10760
10920
|
|
|
10761
10921
|
// ../../loki-ts/src/runner/repo_profile.ts
|
|
10762
|
-
import { existsSync as existsSync22, readFileSync as
|
|
10763
|
-
import { basename as basename15, join as
|
|
10922
|
+
import { existsSync as existsSync22, readFileSync as readFileSync30, mkdirSync as mkdirSync13, readdirSync as readdirSync12, renameSync as renameSync7, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
|
|
10923
|
+
import { basename as basename15, join as join43, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
|
|
10764
10924
|
function loadProfile(repoDir) {
|
|
10765
10925
|
try {
|
|
10766
|
-
const p = JSON.parse(
|
|
10926
|
+
const p = JSON.parse(readFileSync30(commandProfilePath(repoDir), "utf8"));
|
|
10767
10927
|
return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
|
|
10768
10928
|
} catch {
|
|
10769
10929
|
return null;
|
|
@@ -10794,7 +10954,7 @@ function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
|
|
|
10794
10954
|
const block = new Set(blocked);
|
|
10795
10955
|
return attribute(repoDir, baseSha, bad.map((c) => ({ name: c.name, ...c.ids ? { ids: c.ids } : {}, eligible: (c.ids ?? []).every((i) => !block.has(i) && !changed.some((f) => i.includes(f))) })));
|
|
10796
10956
|
}
|
|
10797
|
-
var commandProfilePath = (repoDir) =>
|
|
10957
|
+
var commandProfilePath = (repoDir) => join43(repoDir, ".loki", "state", "repo-profile.json");
|
|
10798
10958
|
var init_repo_profile = __esm(() => {
|
|
10799
10959
|
init_failures();
|
|
10800
10960
|
init_check_result();
|
|
@@ -10804,23 +10964,23 @@ var init_repo_profile = __esm(() => {
|
|
|
10804
10964
|
});
|
|
10805
10965
|
|
|
10806
10966
|
// ../../loki-ts/src/project_graph.ts
|
|
10807
|
-
import { createHash as
|
|
10967
|
+
import { createHash as createHash10 } from "crypto";
|
|
10808
10968
|
import {
|
|
10809
10969
|
existsSync as existsSync23,
|
|
10810
|
-
readdirSync as
|
|
10811
|
-
readFileSync as
|
|
10812
|
-
statSync as
|
|
10813
|
-
writeFileSync as
|
|
10814
|
-
mkdirSync as
|
|
10970
|
+
readdirSync as readdirSync13,
|
|
10971
|
+
readFileSync as readFileSync31,
|
|
10972
|
+
statSync as statSync16,
|
|
10973
|
+
writeFileSync as writeFileSync17,
|
|
10974
|
+
mkdirSync as mkdirSync14
|
|
10815
10975
|
} from "fs";
|
|
10816
|
-
import { basename as basename16, dirname as
|
|
10976
|
+
import { basename as basename16, dirname as dirname13, join as join44, resolve as resolve12 } from "path";
|
|
10817
10977
|
function readManifest(path) {
|
|
10818
10978
|
if (!existsSync23(path)) {
|
|
10819
10979
|
return null;
|
|
10820
10980
|
}
|
|
10821
10981
|
let raw2;
|
|
10822
10982
|
try {
|
|
10823
|
-
raw2 =
|
|
10983
|
+
raw2 = readFileSync31(path, "utf8");
|
|
10824
10984
|
} catch {
|
|
10825
10985
|
return null;
|
|
10826
10986
|
}
|
|
@@ -10857,7 +11017,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10857
11017
|
const seen = new Set;
|
|
10858
11018
|
let children;
|
|
10859
11019
|
try {
|
|
10860
|
-
children =
|
|
11020
|
+
children = readdirSync13(rootDir);
|
|
10861
11021
|
} catch {
|
|
10862
11022
|
children = [];
|
|
10863
11023
|
}
|
|
@@ -10872,12 +11032,12 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10872
11032
|
for (const child of children) {
|
|
10873
11033
|
if (!matchPattern(name, child))
|
|
10874
11034
|
continue;
|
|
10875
|
-
const full =
|
|
11035
|
+
const full = join44(rootDir, child);
|
|
10876
11036
|
if (seen.has(full))
|
|
10877
11037
|
continue;
|
|
10878
11038
|
let st;
|
|
10879
11039
|
try {
|
|
10880
|
-
st =
|
|
11040
|
+
st = statSync16(full);
|
|
10881
11041
|
} catch {
|
|
10882
11042
|
continue;
|
|
10883
11043
|
}
|
|
@@ -10889,18 +11049,18 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10889
11049
|
}
|
|
10890
11050
|
if (declared.length === 0) {
|
|
10891
11051
|
for (const child of children) {
|
|
10892
|
-
const full =
|
|
11052
|
+
const full = join44(rootDir, child);
|
|
10893
11053
|
if (seen.has(full))
|
|
10894
11054
|
continue;
|
|
10895
11055
|
let st;
|
|
10896
11056
|
try {
|
|
10897
|
-
st =
|
|
11057
|
+
st = statSync16(full);
|
|
10898
11058
|
} catch {
|
|
10899
11059
|
continue;
|
|
10900
11060
|
}
|
|
10901
11061
|
if (!st.isDirectory())
|
|
10902
11062
|
continue;
|
|
10903
|
-
if (!existsSync23(
|
|
11063
|
+
if (!existsSync23(join44(full, ".loki", "app.json")))
|
|
10904
11064
|
continue;
|
|
10905
11065
|
seen.add(full);
|
|
10906
11066
|
out2.push(full);
|
|
@@ -10913,25 +11073,25 @@ function cacheKey(manifestPaths) {
|
|
|
10913
11073
|
for (const p of manifestPaths.sort()) {
|
|
10914
11074
|
let mtime = 0;
|
|
10915
11075
|
try {
|
|
10916
|
-
mtime =
|
|
11076
|
+
mtime = statSync16(p).mtimeMs;
|
|
10917
11077
|
} catch {}
|
|
10918
11078
|
parts.push(`${p}:${mtime}`);
|
|
10919
11079
|
}
|
|
10920
|
-
return
|
|
11080
|
+
return createHash10("sha256").update(parts.join("|")).digest("hex");
|
|
10921
11081
|
}
|
|
10922
11082
|
function writeCache(targetDir, key, result) {
|
|
10923
11083
|
try {
|
|
10924
|
-
const cacheDir =
|
|
10925
|
-
|
|
10926
|
-
const cachePath =
|
|
10927
|
-
|
|
11084
|
+
const cacheDir = join44(targetDir, ".loki", "state");
|
|
11085
|
+
mkdirSync14(cacheDir, { recursive: true });
|
|
11086
|
+
const cachePath = join44(cacheDir, "project-graph.json");
|
|
11087
|
+
writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
|
|
10928
11088
|
} catch {}
|
|
10929
11089
|
}
|
|
10930
11090
|
function discoverProjectGraph(targetDir) {
|
|
10931
11091
|
const absTarget = resolve12(targetDir);
|
|
10932
|
-
const targetManifest =
|
|
10933
|
-
const parentDir =
|
|
10934
|
-
const parentManifest =
|
|
11092
|
+
const targetManifest = join44(absTarget, ".loki", "app.json");
|
|
11093
|
+
const parentDir = dirname13(absTarget);
|
|
11094
|
+
const parentManifest = join44(parentDir, ".loki", "app.json");
|
|
10935
11095
|
const examined = [];
|
|
10936
11096
|
let chosenManifest = null;
|
|
10937
11097
|
let chosenRoot = "";
|
|
@@ -10951,7 +11111,7 @@ function discoverProjectGraph(targetDir) {
|
|
|
10951
11111
|
const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
|
|
10952
11112
|
const filtered = [];
|
|
10953
11113
|
for (const m of members) {
|
|
10954
|
-
const sibManifestPath =
|
|
11114
|
+
const sibManifestPath = join44(m, ".loki", "app.json");
|
|
10955
11115
|
if (existsSync23(sibManifestPath)) {
|
|
10956
11116
|
examined.push(sibManifestPath);
|
|
10957
11117
|
const sibManifest = readManifest(sibManifestPath);
|
|
@@ -11024,15 +11184,15 @@ var init_steer = __esm(() => {
|
|
|
11024
11184
|
});
|
|
11025
11185
|
|
|
11026
11186
|
// ../../loki-ts/src/util/pr_lessons.ts
|
|
11027
|
-
import { existsSync as existsSync24, mkdirSync as
|
|
11028
|
-
import { dirname as
|
|
11029
|
-
import { createHash as
|
|
11187
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
|
|
11188
|
+
import { dirname as dirname14, join as join45 } from "path";
|
|
11189
|
+
import { createHash as createHash11 } from "crypto";
|
|
11030
11190
|
function loadLessons(repoDir) {
|
|
11031
11191
|
const p = lessonsPath(repoDir);
|
|
11032
11192
|
if (!existsSync24(p))
|
|
11033
11193
|
return [];
|
|
11034
11194
|
try {
|
|
11035
|
-
const j = JSON.parse(
|
|
11195
|
+
const j = JSON.parse(readFileSync32(p, "utf-8"));
|
|
11036
11196
|
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
11037
11197
|
} catch {
|
|
11038
11198
|
return [];
|
|
@@ -11040,11 +11200,11 @@ function loadLessons(repoDir) {
|
|
|
11040
11200
|
}
|
|
11041
11201
|
function save(repoDir, lessons) {
|
|
11042
11202
|
const p = lessonsPath(repoDir);
|
|
11043
|
-
|
|
11203
|
+
mkdirSync15(dirname14(p), { recursive: true });
|
|
11044
11204
|
atomicWriteJson(p, { version: 2, lessons });
|
|
11045
11205
|
}
|
|
11046
11206
|
function appendReceiptFact(repoDir, f, now2 = () => new Date) {
|
|
11047
|
-
const id = `rcp-${
|
|
11207
|
+
const id = `rcp-${createHash11("sha256").update(f.runId).digest("hex").slice(0, 12)}`;
|
|
11048
11208
|
const lessons = loadLessons(repoDir);
|
|
11049
11209
|
if (lessons.some((l) => l.id === id))
|
|
11050
11210
|
return false;
|
|
@@ -11074,7 +11234,7 @@ function recordRunVerdict(repoDir, runId, verdict) {
|
|
|
11074
11234
|
if (dirty)
|
|
11075
11235
|
save(repoDir, lessons);
|
|
11076
11236
|
}
|
|
11077
|
-
var lessonsPath = (repoDir) =>
|
|
11237
|
+
var lessonsPath = (repoDir) => join45(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
|
|
11078
11238
|
var init_pr_lessons = __esm(() => {
|
|
11079
11239
|
init_atomic();
|
|
11080
11240
|
KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
|
|
@@ -11090,19 +11250,19 @@ var init_model_downgrades = __esm(() => {
|
|
|
11090
11250
|
var init_mcp_config = () => {};
|
|
11091
11251
|
|
|
11092
11252
|
// ../../loki-ts/src/providers/claude_flags.ts
|
|
11093
|
-
import { existsSync as existsSync25, readFileSync as
|
|
11094
|
-
import { resolve as resolvePath, dirname as
|
|
11253
|
+
import { existsSync as existsSync25, readFileSync as readFileSync33 } from "fs";
|
|
11254
|
+
import { resolve as resolvePath, dirname as dirname15 } from "path";
|
|
11095
11255
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11096
11256
|
function designArchetypesText() {
|
|
11097
11257
|
try {
|
|
11098
|
-
const moduleDir =
|
|
11258
|
+
const moduleDir = dirname15(fileURLToPath3(import.meta.url));
|
|
11099
11259
|
for (const rel of [
|
|
11100
11260
|
["..", "..", "..", "references", "design-archetypes.md"],
|
|
11101
11261
|
["..", "..", "references", "design-archetypes.md"]
|
|
11102
11262
|
]) {
|
|
11103
11263
|
const p = resolvePath(moduleDir, ...rel);
|
|
11104
11264
|
if (existsSync25(p))
|
|
11105
|
-
return
|
|
11265
|
+
return readFileSync33(p, "utf8");
|
|
11106
11266
|
}
|
|
11107
11267
|
} catch {}
|
|
11108
11268
|
return "";
|
|
@@ -11180,14 +11340,14 @@ var init_providers = __esm(() => {
|
|
|
11180
11340
|
});
|
|
11181
11341
|
|
|
11182
11342
|
// ../../loki-ts/src/runner/router/route_record.ts
|
|
11183
|
-
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as
|
|
11184
|
-
import { join as
|
|
11343
|
+
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
|
|
11344
|
+
import { join as join46 } from "path";
|
|
11185
11345
|
function loadRouteRecord(runDir) {
|
|
11186
11346
|
try {
|
|
11187
|
-
const p =
|
|
11347
|
+
const p = join46(runDir, ROUTE_RECORD_FILE);
|
|
11188
11348
|
if (!existsSync26(p) || !lstatSync9(p).isFile())
|
|
11189
11349
|
return;
|
|
11190
|
-
const v = JSON.parse(
|
|
11350
|
+
const v = JSON.parse(readFileSync34(p, "utf8"));
|
|
11191
11351
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
11192
11352
|
} catch {
|
|
11193
11353
|
return;
|
|
@@ -11197,7 +11357,7 @@ var ROUTE_RECORD_FILE = "route.json";
|
|
|
11197
11357
|
var init_route_record = () => {};
|
|
11198
11358
|
|
|
11199
11359
|
// ../../loki-ts/src/runner/router/history_store.ts
|
|
11200
|
-
import { existsSync as existsSync27, mkdirSync as
|
|
11360
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync35, renameSync as renameSync8, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
|
|
11201
11361
|
import { resolve as resolve13 } from "path";
|
|
11202
11362
|
function validRun(v) {
|
|
11203
11363
|
if (typeof v !== "object" || v === null)
|
|
@@ -11213,7 +11373,7 @@ function readRunHistory(key, cacheRoot) {
|
|
|
11213
11373
|
if (!existsSync27(path))
|
|
11214
11374
|
return [];
|
|
11215
11375
|
try {
|
|
11216
|
-
const parsed = JSON.parse(
|
|
11376
|
+
const parsed = JSON.parse(readFileSync35(path, "utf8"));
|
|
11217
11377
|
const runs2 = parsed?.runs;
|
|
11218
11378
|
return Array.isArray(runs2) ? runs2.filter(validRun) : [];
|
|
11219
11379
|
} catch {
|
|
@@ -11223,12 +11383,12 @@ function readRunHistory(key, cacheRoot) {
|
|
|
11223
11383
|
function appendRunOutcome(key, outcome, cacheRoot) {
|
|
11224
11384
|
try {
|
|
11225
11385
|
const runs2 = [...readRunHistory(key, cacheRoot), outcome].slice(-HISTORY_CAP);
|
|
11226
|
-
|
|
11386
|
+
mkdirSync17(repoCacheDir(key, cacheRoot), { recursive: true });
|
|
11227
11387
|
const target = historyPath(key, cacheRoot);
|
|
11228
11388
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
11229
11389
|
try {
|
|
11230
|
-
|
|
11231
|
-
|
|
11390
|
+
writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
|
|
11391
|
+
renameSync8(tmp, target);
|
|
11232
11392
|
} catch (e) {
|
|
11233
11393
|
rmSync11(tmp, { force: true, recursive: false });
|
|
11234
11394
|
throw e;
|
|
@@ -11247,7 +11407,7 @@ var init_history_store = __esm(() => {
|
|
|
11247
11407
|
});
|
|
11248
11408
|
|
|
11249
11409
|
// ../../loki-ts/src/runner/router/history.ts
|
|
11250
|
-
import { dirname as
|
|
11410
|
+
import { dirname as dirname16, join as join47, resolve as resolve14 } from "path";
|
|
11251
11411
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11252
11412
|
function shapeKey(model) {
|
|
11253
11413
|
if (!model || model.status !== "ok")
|
|
@@ -11276,7 +11436,7 @@ var init_history = __esm(() => {
|
|
|
11276
11436
|
init_discover2();
|
|
11277
11437
|
init_gather();
|
|
11278
11438
|
init_history_store();
|
|
11279
|
-
SHAPE_DEFAULTS_FILE = resolve14(
|
|
11439
|
+
SHAPE_DEFAULTS_FILE = resolve14(dirname16(fileURLToPath4(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
|
|
11280
11440
|
});
|
|
11281
11441
|
|
|
11282
11442
|
// ../../loki-ts/src/runner/router/plan_route.ts
|
|
@@ -11407,7 +11567,7 @@ var init_worker = __esm(() => {
|
|
|
11407
11567
|
});
|
|
11408
11568
|
|
|
11409
11569
|
// ../../loki-ts/src/project_model/package_suite.ts
|
|
11410
|
-
import { join as
|
|
11570
|
+
import { join as join48 } from "path";
|
|
11411
11571
|
async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
11412
11572
|
const api = loadProjectApi(repoDir);
|
|
11413
11573
|
if (!isMultiRoot(api))
|
|
@@ -11418,7 +11578,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11418
11578
|
for (const pkg of withTests) {
|
|
11419
11579
|
const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
|
|
11420
11580
|
const started = Date.now();
|
|
11421
|
-
const r = await run(["bash", "-c", tc.cmd], { cwd:
|
|
11581
|
+
const r = await run(["bash", "-c", tc.cmd], { cwd: join48(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11422
11582
|
const duration_s = (Date.now() - started) / 1000;
|
|
11423
11583
|
if (signal.aborted) {
|
|
11424
11584
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
@@ -11427,7 +11587,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11427
11587
|
}
|
|
11428
11588
|
const out2 = `${r.stdout}
|
|
11429
11589
|
${r.stderr}`;
|
|
11430
|
-
const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd:
|
|
11590
|
+
const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join48(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11431
11591
|
if (lr) {
|
|
11432
11592
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
11433
11593
|
notProven.add(`${lr} (${name}; harness-owned)`);
|
|
@@ -11460,8 +11620,8 @@ __export(exports_deep, {
|
|
|
11460
11620
|
DEFAULT_SECRET_SCAN_SH: () => DEFAULT_SECRET_SCAN_SH,
|
|
11461
11621
|
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
|
|
11462
11622
|
});
|
|
11463
|
-
import { existsSync as existsSync28, mkdirSync as
|
|
11464
|
-
import { join as
|
|
11623
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
|
|
11624
|
+
import { join as join49 } from "path";
|
|
11465
11625
|
async function runFullSuite(ctx, signal, opts, checks, notProven) {
|
|
11466
11626
|
if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
|
|
11467
11627
|
return;
|
|
@@ -11558,7 +11718,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
|
11558
11718
|
}
|
|
11559
11719
|
const inline = 'script="$1"; shift; . "$script" || exit 3; hit=0; for f in "$@"; do if _commit_scan_secret_file "$f" || _commit_path_looks_secret "$f"; then printf "%s\\n" "$f"; hit=1; fi; done; exit "$hit"';
|
|
11560
11720
|
const started = Date.now();
|
|
11561
|
-
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) =>
|
|
11721
|
+
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join49(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
|
|
11562
11722
|
const durationS = (Date.now() - started) / 1000;
|
|
11563
11723
|
if (r.exitCode === 3) {
|
|
11564
11724
|
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
|
|
@@ -11579,10 +11739,10 @@ function commentArgv(prNumber, bodyFile) {
|
|
|
11579
11739
|
return ["comment", prNumber, bodyFile];
|
|
11580
11740
|
}
|
|
11581
11741
|
function buildCommentBody(checks, notProven, statusState) {
|
|
11582
|
-
const
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
return `${
|
|
11742
|
+
const lines2 = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
|
|
11743
|
+
lines2.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
|
|
11744
|
+
lines2.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
|
|
11745
|
+
return `${lines2.join(`
|
|
11586
11746
|
`)}
|
|
11587
11747
|
`;
|
|
11588
11748
|
}
|
|
@@ -11598,9 +11758,9 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
|
|
|
11598
11758
|
const prUrl = ctx.outputs().pr?.pr_url;
|
|
11599
11759
|
const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
|
|
11600
11760
|
if (prNumber) {
|
|
11601
|
-
|
|
11602
|
-
const bodyFile =
|
|
11603
|
-
|
|
11761
|
+
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11762
|
+
const bodyFile = join49(ctx.runDir, "deep-comment.md");
|
|
11763
|
+
writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
|
|
11604
11764
|
const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
11605
11765
|
if (commentResult.exitCode !== 0)
|
|
11606
11766
|
out2.push(`deep comment not posted (engine10-push.sh exit ${commentResult.exitCode})`);
|
|
@@ -11652,8 +11812,8 @@ async function runDeep(ctx, signal, opts = {}) {
|
|
|
11652
11812
|
const addendumSha256 = sha2562(canonicalJson(addendumBody));
|
|
11653
11813
|
const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
|
|
11654
11814
|
const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
11655
|
-
|
|
11656
|
-
|
|
11815
|
+
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11816
|
+
writeFileSync20(join49(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
|
|
11657
11817
|
`);
|
|
11658
11818
|
const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
|
|
11659
11819
|
const allNotProven = [...notProvenList, ...pushNotProven];
|
|
@@ -11698,7 +11858,7 @@ var init_deep = __esm(() => {
|
|
|
11698
11858
|
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
11699
11859
|
import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
|
|
11700
11860
|
import { tmpdir as tmpdir8 } from "os";
|
|
11701
|
-
import { dirname as
|
|
11861
|
+
import { dirname as dirname17, isAbsolute as isAbsolute6, join as join50, relative as relative5 } from "path";
|
|
11702
11862
|
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
11703
11863
|
const inRepo = (p) => {
|
|
11704
11864
|
try {
|
|
@@ -11709,7 +11869,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
|
11709
11869
|
}
|
|
11710
11870
|
};
|
|
11711
11871
|
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
11712
|
-
const project = [
|
|
11872
|
+
const project = [join50(repoDir, ".venv", "bin", bin), join50(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join50(venvEnv, "bin", bin)] : []].find(existsSync29);
|
|
11713
11873
|
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
11714
11874
|
}
|
|
11715
11875
|
function runnerCmd(t, repoDir) {
|
|
@@ -11729,7 +11889,7 @@ function runnerCmd(t, repoDir) {
|
|
|
11729
11889
|
case "npm":
|
|
11730
11890
|
return ["npm", ["test", "--silent"]];
|
|
11731
11891
|
case "go":
|
|
11732
|
-
return ["go", ["test", "-json", `./${
|
|
11892
|
+
return ["go", ["test", "-json", `./${dirname17(t.path)}`]];
|
|
11733
11893
|
case "cargo":
|
|
11734
11894
|
return ["cargo", ["test"]];
|
|
11735
11895
|
}
|
|
@@ -11739,13 +11899,6 @@ function commandFor(t, repoDir, api) {
|
|
|
11739
11899
|
const [cmd, args, interpreter] = runnerCmd(site ? { runner: t.runner, path: site.file } : t, repoDir);
|
|
11740
11900
|
return { cwd: site?.cwd ?? repoDir, cmd, args, argv: [cmd, ...args], ...interpreter ? { interpreter } : {}, pkgRoot: site?.root ?? "." };
|
|
11741
11901
|
}
|
|
11742
|
-
function testConfigChanged(repoDir, baseSha, changed) {
|
|
11743
|
-
const diff = (f) => safeGit(repoDir, ["diff", "-U0", baseSha, "--", f]);
|
|
11744
|
-
return changed.filter((f) => CFG_ALWAYS.test(f) || CFG_SHARED.test(f) && (() => {
|
|
11745
|
-
const d = diff(f);
|
|
11746
|
-
return !d || CFG_LINE.test(d);
|
|
11747
|
-
})());
|
|
11748
|
-
}
|
|
11749
11902
|
async function runOnce(cmd, args, cwd, signal, opts) {
|
|
11750
11903
|
if (!Bun.which(cmd, { PATH: opts.path ?? process.env["PATH"] ?? "" }))
|
|
11751
11904
|
return { ok: false, missing: true, cut: false, out: "" };
|
|
@@ -11784,8 +11937,8 @@ function firstError(out2) {
|
|
|
11784
11937
|
return l2;
|
|
11785
11938
|
}
|
|
11786
11939
|
};
|
|
11787
|
-
const
|
|
11788
|
-
`).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l =
|
|
11940
|
+
const lines2 = out2.slice(-65536).split(`
|
|
11941
|
+
`).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l = lines2.find((x) => /^(FAILED\s|\u25cf\s.*\u203a|not ok\s|_{3,}\s.+\s_{3,}$)/.test(x)) ?? lines2.find((x) => /^[\w./-]+\.go:\d+:\d+: \S/.test(x)) ?? lines2.find((x) => /fail|error/i.test(x) && !/^(=|\u2713|ok\b|PASS)/.test(x)) ?? "";
|
|
11789
11942
|
return l.replace(/\d{4}-\d\d-\d\dT[\d:.]+Z?/g, "").replace(/(^|\s)\/(?:[\w.@-]+\/)*[\w.@-]+/g, "$1<path>").replace(/:\d+(?::\d+)?/g, "").replace(/\[?\d+(?:\.\d+)?m?s\]?/g, "").replace(/\s+/g, " ").slice(0, 160);
|
|
11790
11943
|
}
|
|
11791
11944
|
async function runCheck(ctx, name, cmd, args, signal, checks, opts = {}) {
|
|
@@ -11827,7 +11980,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11827
11980
|
const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
|
|
11828
11981
|
if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
|
|
11829
11982
|
return out2;
|
|
11830
|
-
const dir = mkdtempSync6(
|
|
11983
|
+
const dir = mkdtempSync6(join50(tmpdir8(), "e10-base-"));
|
|
11831
11984
|
const git3 = (args) => {
|
|
11832
11985
|
safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
|
|
11833
11986
|
};
|
|
@@ -11862,7 +12015,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11862
12015
|
return out2;
|
|
11863
12016
|
}
|
|
11864
12017
|
function isLokiModeRepo(repoDir) {
|
|
11865
|
-
return existsSync29(
|
|
12018
|
+
return existsSync29(join50(repoDir, "scripts", "select-tests.sh"));
|
|
11866
12019
|
}
|
|
11867
12020
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
11868
12021
|
const { api, ...runOpts } = opts;
|
|
@@ -11879,15 +12032,16 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
11879
12032
|
}
|
|
11880
12033
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
11881
12034
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
11882
|
-
if (existsSync29(
|
|
12035
|
+
if (existsSync29(join50(g.cwd, "tsconfig.json")))
|
|
11883
12036
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
11884
|
-
if (ESLINT_CONFIGS.some((f) => existsSync29(
|
|
12037
|
+
if (ESLINT_CONFIGS.some((f) => existsSync29(join50(g.cwd, f))))
|
|
11885
12038
|
await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
11886
12039
|
}
|
|
11887
12040
|
}
|
|
11888
|
-
var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT,
|
|
12041
|
+
var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT, ESLINT_CONFIGS, verifyStage;
|
|
11889
12042
|
var init_verify = __esm(() => {
|
|
11890
12043
|
init_scope();
|
|
12044
|
+
init_runner_config();
|
|
11891
12045
|
init_assert_delta();
|
|
11892
12046
|
init_failures();
|
|
11893
12047
|
init_load_owner();
|
|
@@ -11901,10 +12055,8 @@ var init_verify = __esm(() => {
|
|
|
11901
12055
|
init_repo_profile();
|
|
11902
12056
|
init_check_result();
|
|
11903
12057
|
init_check_result();
|
|
12058
|
+
init_runner_config();
|
|
11904
12059
|
CODE_EXT = /\.(?:[cm]?[jt]sx?|py|go)$/;
|
|
11905
|
-
CFG_ALWAYS = /(^|\/)(conftest\.py|\.?pytest\.(?:ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+|\.mocharc[\w.]*)$/;
|
|
11906
|
-
CFG_SHARED = /(^|\/)(setup\.cfg|pyproject\.toml|package\.json)$/;
|
|
11907
|
-
CFG_LINE = /^[+-].*(pytest|jest|mocha|vitest|"test"\s*:|addopts|testpaths)/im;
|
|
11908
12060
|
ESLINT_CONFIGS = [".eslintrc", ".eslintrc.json", ".eslintrc.js", ".eslintrc.cjs", "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"];
|
|
11909
12061
|
verifyStage = {
|
|
11910
12062
|
name: "verify",
|
|
@@ -11941,8 +12093,8 @@ var init_verify = __esm(() => {
|
|
|
11941
12093
|
const wall = ctx.outputs().wall ?? {};
|
|
11942
12094
|
const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute6(f.path) ? relative5(ctx.repoDir, f.path) : f.path));
|
|
11943
12095
|
const wallTests = map.tests.filter((t) => wallPaths.has(t.path));
|
|
11944
|
-
const intake = ctx.outputs().intake,
|
|
11945
|
-
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...
|
|
12096
|
+
const intake = ctx.outputs().intake, relevant2 = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
|
|
12097
|
+
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant2]);
|
|
11946
12098
|
const scopedOut = scopedOutOf(map);
|
|
11947
12099
|
if (scopedOut.length)
|
|
11948
12100
|
ctx.emit("test.scoped_out", "verify", { tests: scopedOut });
|
|
@@ -11950,10 +12102,10 @@ var init_verify = __esm(() => {
|
|
|
11950
12102
|
if (signal.aborted)
|
|
11951
12103
|
break;
|
|
11952
12104
|
const tc = commandFor(t, ctx.repoDir, api);
|
|
11953
|
-
await runCheck(ctx, `${t.runner}:${t.path}`, tc.cmd, tc.args, signal, checks, { ...tc.interpreter ? { interpreter: tc.interpreter } : {}, protect: [...wallTests, ...
|
|
12105
|
+
await runCheck(ctx, `${t.runner}:${t.path}`, tc.cmd, tc.args, signal, checks, { ...tc.interpreter ? { interpreter: tc.interpreter } : {}, protect: [...wallTests, ...relevant2].includes(t), ...tc.pkgRoot !== "." ? { cwd: tc.cwd } : {} });
|
|
11954
12106
|
}
|
|
11955
|
-
const targetNames = new Set([...wallTests, ...
|
|
11956
|
-
const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(
|
|
12107
|
+
const targetNames = new Set([...wallTests, ...relevant2].map((t) => `${t.runner}:${t.path}`));
|
|
12108
|
+
const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(relevant2.map((t) => `${t.runner}:${t.path}`)), signal);
|
|
11957
12109
|
const attr = attribute(ctx.repoDir, ctx.baseSha, checks.filter((c) => c.result === "fail" && !preRed.names.includes(c.name)).map((c) => ({ name: c.name, ids: c.ids, eligible: !targetNames.has(c.name) && !changed.includes(c.name.slice(c.name.indexOf(":") + 1)) })));
|
|
11958
12110
|
preRed.names.push(...attr.preExistingChecks);
|
|
11959
12111
|
preRed.ids.push(...attr.preExistingIds);
|
|
@@ -11999,7 +12151,7 @@ var init_verify = __esm(() => {
|
|
|
11999
12151
|
}
|
|
12000
12152
|
};
|
|
12001
12153
|
const testCounts = {};
|
|
12002
|
-
const modifiedRel =
|
|
12154
|
+
const modifiedRel = relevant2.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
|
|
12003
12155
|
const k = preRed.cnt[`${t.runner}:${t.path}`];
|
|
12004
12156
|
if (k)
|
|
12005
12157
|
testCounts[t.path] = k;
|
|
@@ -12013,8 +12165,8 @@ var init_verify = __esm(() => {
|
|
|
12013
12165
|
});
|
|
12014
12166
|
|
|
12015
12167
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
12016
|
-
import { existsSync as existsSync30, readdirSync as
|
|
12017
|
-
import { dirname as
|
|
12168
|
+
import { existsSync as existsSync30, readdirSync as readdirSync14, readFileSync as readFileSync36 } from "fs";
|
|
12169
|
+
import { dirname as dirname18, join as join51 } from "path";
|
|
12018
12170
|
function parseJsonc(text2) {
|
|
12019
12171
|
let out2 = "", i = 0;
|
|
12020
12172
|
while (i < text2.length) {
|
|
@@ -12049,25 +12201,25 @@ function moduleOf(file, depth = 0) {
|
|
|
12049
12201
|
const ext = typeof j.extends === "string" ? j.extends : null;
|
|
12050
12202
|
if (!ext || !ext.startsWith("."))
|
|
12051
12203
|
return null;
|
|
12052
|
-
const next =
|
|
12204
|
+
const next = join51(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
12053
12205
|
return existsSync30(next) ? moduleOf(next, depth + 1) : null;
|
|
12054
12206
|
}
|
|
12055
12207
|
function readPackageConventions(pkgDir, runner = null) {
|
|
12056
12208
|
let names = [];
|
|
12057
12209
|
try {
|
|
12058
|
-
names =
|
|
12210
|
+
names = readdirSync14(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
|
|
12059
12211
|
} catch {
|
|
12060
12212
|
return null;
|
|
12061
12213
|
}
|
|
12062
12214
|
let packageType = null, tsModule = null;
|
|
12063
12215
|
for (const n2 of names) {
|
|
12064
|
-
const j = readJson4(
|
|
12216
|
+
const j = readJson4(join51(pkgDir, n2));
|
|
12065
12217
|
if (!j)
|
|
12066
12218
|
continue;
|
|
12067
12219
|
if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
|
|
12068
12220
|
packageType = j.type;
|
|
12069
12221
|
if (tsModule === null && j.compilerOptions !== undefined)
|
|
12070
|
-
tsModule = moduleOf(
|
|
12222
|
+
tsModule = moduleOf(join51(pkgDir, n2));
|
|
12071
12223
|
}
|
|
12072
12224
|
if (packageType === null && tsModule === null)
|
|
12073
12225
|
return null;
|
|
@@ -12089,7 +12241,7 @@ function conventionViolation(c, fileName, content) {
|
|
|
12089
12241
|
}
|
|
12090
12242
|
var readJson4 = (p) => {
|
|
12091
12243
|
try {
|
|
12092
|
-
const v = parseJsonc(
|
|
12244
|
+
const v = parseJsonc(readFileSync36(p, "utf8"));
|
|
12093
12245
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12094
12246
|
} catch {
|
|
12095
12247
|
return null;
|
|
@@ -12099,15 +12251,15 @@ var init_conventions = () => {};
|
|
|
12099
12251
|
|
|
12100
12252
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
12101
12253
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12102
|
-
import { existsSync as existsSync31, mkdirSync as
|
|
12254
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync37, readdirSync as readdirSync15, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync21 } from "fs";
|
|
12103
12255
|
import { tmpdir as tmpdir9 } from "os";
|
|
12104
|
-
import { dirname as
|
|
12256
|
+
import { dirname as dirname19, isAbsolute as isAbsolute7, join as join52, relative as relative6 } from "path";
|
|
12105
12257
|
function pytestCollectionIsRed(output, repoDir) {
|
|
12106
12258
|
if (/ModuleNotFoundError/.test(output))
|
|
12107
12259
|
return false;
|
|
12108
12260
|
const real2 = (p) => {
|
|
12109
12261
|
try {
|
|
12110
|
-
return realpathSync18(isAbsolute7(p) ? p :
|
|
12262
|
+
return realpathSync18(isAbsolute7(p) ? p : join52(repoDir, p));
|
|
12111
12263
|
} catch {
|
|
12112
12264
|
return null;
|
|
12113
12265
|
}
|
|
@@ -12144,7 +12296,7 @@ function pytestCollectionIsRed(output, repoDir) {
|
|
|
12144
12296
|
}
|
|
12145
12297
|
function moduleUnderRepo(repoDir, name) {
|
|
12146
12298
|
const rel = name.replace(/\./g, "/");
|
|
12147
|
-
return existsSync31(
|
|
12299
|
+
return existsSync31(join52(repoDir, `${rel}.py`)) || existsSync31(join52(repoDir, rel, "__init__.py"));
|
|
12148
12300
|
}
|
|
12149
12301
|
function pytestExit1IsRed(output) {
|
|
12150
12302
|
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
@@ -12242,10 +12394,10 @@ ${acceptance.map((a) => `Acceptance: ${a}`).join(`
|
|
|
12242
12394
|
function loadTaskText(ctx, fromPrior) {
|
|
12243
12395
|
if (fromPrior)
|
|
12244
12396
|
return fromPrior;
|
|
12245
|
-
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ??
|
|
12397
|
+
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join52(ctx.runDir, "issue.json");
|
|
12246
12398
|
if (existsSync31(issueJsonPath)) {
|
|
12247
12399
|
try {
|
|
12248
|
-
const issue = JSON.parse(
|
|
12400
|
+
const issue = JSON.parse(readFileSync37(issueJsonPath, "utf8"));
|
|
12249
12401
|
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
12250
12402
|
|
|
12251
12403
|
`);
|
|
@@ -12257,7 +12409,7 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
12257
12409
|
}
|
|
12258
12410
|
function wallTargetDir(repoDir, existingTests) {
|
|
12259
12411
|
const first = existingTests[0];
|
|
12260
|
-
return first ?
|
|
12412
|
+
return first ? join52(repoDir, dirname19(first.path)) : join52(repoDir, "tests");
|
|
12261
12413
|
}
|
|
12262
12414
|
function guessRunner(fileName, runners) {
|
|
12263
12415
|
if (fileName.endsWith(".py"))
|
|
@@ -12288,11 +12440,11 @@ async function wallAuthor(ctx, signal) {
|
|
|
12288
12440
|
const tree = prior.intake?.tree;
|
|
12289
12441
|
const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
|
|
12290
12442
|
const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
|
|
12291
|
-
const conv2 = readPackageConventions(
|
|
12292
|
-
const cwd = mkdtempSync7(
|
|
12443
|
+
const conv2 = readPackageConventions(join52(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
|
|
12444
|
+
const cwd = mkdtempSync7(join52(tmpdir9(), "loki-e15-wall-"));
|
|
12293
12445
|
const wm = wallManifestFor(ctx.repoDir, tree, task);
|
|
12294
12446
|
for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
|
|
12295
|
-
|
|
12447
|
+
writeFileSync21(join52(cwd, n2), c, "utf8");
|
|
12296
12448
|
const session = await ctx.sessions.run({
|
|
12297
12449
|
stage: "wall",
|
|
12298
12450
|
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
|
|
@@ -12307,15 +12459,15 @@ async function wallAuthor(ctx, signal) {
|
|
|
12307
12459
|
rmSync13(cwd, { recursive: true, force: true });
|
|
12308
12460
|
return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
|
|
12309
12461
|
}
|
|
12310
|
-
const generated =
|
|
12311
|
-
const sealedDir =
|
|
12462
|
+
const generated = readdirSync15(cwd).filter((f) => f.startsWith(WALL_PREFIX));
|
|
12463
|
+
const sealedDir = join52(ctx.runDir, "wall");
|
|
12312
12464
|
if (generated.length > 0)
|
|
12313
|
-
|
|
12465
|
+
mkdirSync19(sealedDir, { recursive: true });
|
|
12314
12466
|
const contents = new Map;
|
|
12315
12467
|
const discarded = [];
|
|
12316
12468
|
for (const name of generated) {
|
|
12317
|
-
const content =
|
|
12318
|
-
|
|
12469
|
+
const content = readFileSync37(join52(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
|
|
12470
|
+
writeFileSync21(join52(sealedDir, name), content, "utf8");
|
|
12319
12471
|
if (bad)
|
|
12320
12472
|
discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
|
|
12321
12473
|
else
|
|
@@ -12327,7 +12479,7 @@ async function wallAuthor(ctx, signal) {
|
|
|
12327
12479
|
function installWall(ctx, a, baseDir, opts = {}) {
|
|
12328
12480
|
const { generated, contents, discarded, targetDir, runners } = a;
|
|
12329
12481
|
if (generated.length > 0)
|
|
12330
|
-
|
|
12482
|
+
mkdirSync19(targetDir, { recursive: true });
|
|
12331
12483
|
const sealedFiles = [];
|
|
12332
12484
|
const readOnlyFiles = [];
|
|
12333
12485
|
const wallTests = [];
|
|
@@ -12335,8 +12487,8 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12335
12487
|
const content = contents.get(name);
|
|
12336
12488
|
if (content === undefined)
|
|
12337
12489
|
continue;
|
|
12338
|
-
const dest =
|
|
12339
|
-
|
|
12490
|
+
const dest = join52(targetDir, name), runner = guessRunner(name, runners);
|
|
12491
|
+
writeFileSync21(dest, content, "utf8");
|
|
12340
12492
|
sealedFiles.push({ path: dest, sha256: sha2562(content) });
|
|
12341
12493
|
readOnlyFiles.push({ path: dest, content });
|
|
12342
12494
|
if (runner)
|
|
@@ -12345,7 +12497,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12345
12497
|
ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
|
|
12346
12498
|
const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
|
|
12347
12499
|
for (const t of wallTests) {
|
|
12348
|
-
const r = baseRunner.run(baseDir, [t]), abs =
|
|
12500
|
+
const r = baseRunner.run(baseDir, [t]), abs = join52(ctx.repoDir, t.path);
|
|
12349
12501
|
baseRun.pass += r.pass;
|
|
12350
12502
|
baseRun.fail += r.fail;
|
|
12351
12503
|
baseRun.not_run += r.not_run ?? 0;
|
|
@@ -12397,20 +12549,20 @@ var init_wall = __esm(() => {
|
|
|
12397
12549
|
});
|
|
12398
12550
|
|
|
12399
12551
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
12400
|
-
import { chmodSync as
|
|
12401
|
-
import { dirname as
|
|
12552
|
+
import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as readFileSync38, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
|
|
12553
|
+
import { dirname as dirname20, join as join53, relative as relative7, sep as sep11 } from "path";
|
|
12402
12554
|
function safeRestore(repoDir, f, want, mode) {
|
|
12403
|
-
const root = realpathSync19(repoDir), fp =
|
|
12555
|
+
const root = realpathSync19(repoDir), fp = join53(root, f), rel = relative7(root, dirname20(fp));
|
|
12404
12556
|
if (rel.startsWith(".."))
|
|
12405
12557
|
throw new Error("outside repo");
|
|
12406
12558
|
let cur = root;
|
|
12407
12559
|
for (const part of rel === "" ? [] : rel.split(sep11)) {
|
|
12408
|
-
cur =
|
|
12560
|
+
cur = join53(cur, part);
|
|
12409
12561
|
let st2;
|
|
12410
12562
|
try {
|
|
12411
12563
|
st2 = lstatSync10(cur);
|
|
12412
12564
|
} catch {
|
|
12413
|
-
|
|
12565
|
+
mkdirSync20(cur);
|
|
12414
12566
|
continue;
|
|
12415
12567
|
}
|
|
12416
12568
|
if (!st2.isDirectory())
|
|
@@ -12422,23 +12574,23 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
12422
12574
|
} catch {
|
|
12423
12575
|
st = null;
|
|
12424
12576
|
}
|
|
12425
|
-
if (st?.isFile() &&
|
|
12426
|
-
|
|
12577
|
+
if (st?.isFile() && readFileSync38(fp).equals(want)) {
|
|
12578
|
+
chmodSync5(fp, mode);
|
|
12427
12579
|
return;
|
|
12428
12580
|
}
|
|
12429
12581
|
if (st) {
|
|
12430
12582
|
if (st.isDirectory())
|
|
12431
12583
|
rmdirSync(fp);
|
|
12432
12584
|
else
|
|
12433
|
-
|
|
12585
|
+
unlinkSync5(fp);
|
|
12434
12586
|
}
|
|
12435
12587
|
const fd = openSync8(fp, "wx", mode);
|
|
12436
12588
|
try {
|
|
12437
|
-
|
|
12589
|
+
writeFileSync22(fd, want);
|
|
12438
12590
|
} finally {
|
|
12439
12591
|
closeSync8(fd);
|
|
12440
12592
|
}
|
|
12441
|
-
|
|
12593
|
+
chmodSync5(fp, mode);
|
|
12442
12594
|
}
|
|
12443
12595
|
function alreadySatisfied(o) {
|
|
12444
12596
|
const b = o.wall?.base_run ?? {};
|
|
@@ -12460,7 +12612,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12460
12612
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
12461
12613
|
const notProven = [];
|
|
12462
12614
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
12463
|
-
const fp =
|
|
12615
|
+
const fp = join53(repoDir, f);
|
|
12464
12616
|
try {
|
|
12465
12617
|
if (v.startsWith("!")) {
|
|
12466
12618
|
const st = lstatSync10(fp);
|
|
@@ -12479,7 +12631,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12479
12631
|
try {
|
|
12480
12632
|
let m = 420;
|
|
12481
12633
|
try {
|
|
12482
|
-
const l = lstatSync10(
|
|
12634
|
+
const l = lstatSync10(join53(repoDir, f));
|
|
12483
12635
|
if (l.isFile())
|
|
12484
12636
|
m = l.mode & 511;
|
|
12485
12637
|
} catch {}
|
|
@@ -12512,9 +12664,9 @@ var init_preexisting_dirty = __esm(() => {
|
|
|
12512
12664
|
});
|
|
12513
12665
|
|
|
12514
12666
|
// ../../loki-ts/src/e10ext/commit_filter.ts
|
|
12515
|
-
import { dirname as
|
|
12667
|
+
import { dirname as dirname21 } from "path";
|
|
12516
12668
|
function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
|
|
12517
|
-
return staged.concat(untouchedSinceIntake(repoDir, preexistingDirty, env).map((f) => ({ st: "L", f }))).filter(({ st, f }) => f.startsWith(".loki/") || st === "L" || /(^|\/)loki_wall_[^/]*$/.test(f) || st === "A" && !staged.some(({ f: m }) => /(^|\/)(package\.json|pyproject\.toml|requirements\.txt|Cargo\.toml|go\.mod)$/.test(m) &&
|
|
12669
|
+
return staged.concat(untouchedSinceIntake(repoDir, preexistingDirty, env).map((f) => ({ st: "L", f }))).filter(({ st, f }) => f.startsWith(".loki/") || st === "L" || /(^|\/)loki_wall_[^/]*$/.test(f) || st === "A" && !staged.some(({ f: m }) => /(^|\/)(package\.json|pyproject\.toml|requirements\.txt|Cargo\.toml|go\.mod)$/.test(m) && dirname21(m) === dirname21(f)) && /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|poetry\.lock|Cargo\.lock|go\.sum)$/.test(f));
|
|
12518
12670
|
}
|
|
12519
12671
|
var parseStaged = (out2) => out2.split("\x00").reduce((a, t, i, all) => i % 2 === 0 && t ? [...a, { st: t, f: all[i + 1] }] : a, []);
|
|
12520
12672
|
var init_commit_filter = __esm(() => {
|
|
@@ -12523,7 +12675,7 @@ var init_commit_filter = __esm(() => {
|
|
|
12523
12675
|
});
|
|
12524
12676
|
|
|
12525
12677
|
// ../../loki-ts/src/e10ext/scope.ts
|
|
12526
|
-
import { basename as basename18, dirname as
|
|
12678
|
+
import { basename as basename18, dirname as dirname22 } from "path";
|
|
12527
12679
|
function contractText(o) {
|
|
12528
12680
|
const c = o.intake?.contract_snapshot?.contract;
|
|
12529
12681
|
return Array.isArray(c?.criteria) ? c.criteria.map((x) => typeof x?.text === "string" ? x.text : "").join(`
|
|
@@ -12537,8 +12689,8 @@ function outsideScope(o, staged) {
|
|
|
12537
12689
|
const text2 = `${plan}
|
|
12538
12690
|
${typeof o.intake?.task === "string" ? o.intake.task : ""}
|
|
12539
12691
|
${contractText(o)}`, pre = o.intake?.preexisting_dirty ?? {};
|
|
12540
|
-
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) =>
|
|
12541
|
-
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename18(f)) && !plannedDirs.has(
|
|
12692
|
+
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) => dirname22(r)).filter((d) => d !== "."));
|
|
12693
|
+
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename18(f)) && !plannedDirs.has(dirname22(f)) && !dirSegs(f).some((d) => toks.has(d))).map(({ f }) => f);
|
|
12542
12694
|
}
|
|
12543
12695
|
function flagOutsideScope(o, staged) {
|
|
12544
12696
|
const unit = unitFlags(staged, process.env, o.intake?.preexisting_dirty ?? {});
|
|
@@ -12552,78 +12704,12 @@ var init_scope2 = __esm(() => {
|
|
|
12552
12704
|
STOP = new Set(["the", "and", "for", "with", "that", "this", "all", "any", "src", "from", "into", "each", "across", "apply", "add", "use", "make"]);
|
|
12553
12705
|
});
|
|
12554
12706
|
|
|
12555
|
-
// ../../loki-ts/src/util/receipt_signer.ts
|
|
12556
|
-
import { createHash as createHash11, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
|
|
12557
|
-
import { chmodSync as chmodSync5, linkSync, mkdirSync as mkdirSync20, readFileSync as readFileSync37, statSync as statSync16, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
|
|
12558
|
-
import { homedir as homedir8 } from "os";
|
|
12559
|
-
import { dirname as dirname22, join as join52 } from "path";
|
|
12560
|
-
function canonicalJson(x) {
|
|
12561
|
-
const walk = (v) => {
|
|
12562
|
-
if (Array.isArray(v))
|
|
12563
|
-
return `[${v.map(walk).join(",")}]`;
|
|
12564
|
-
if (v !== null && typeof v === "object") {
|
|
12565
|
-
const o = v;
|
|
12566
|
-
return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
|
|
12567
|
-
}
|
|
12568
|
-
return JSON.stringify(v ?? null);
|
|
12569
|
-
};
|
|
12570
|
-
return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
12571
|
-
}
|
|
12572
|
-
function loadSigningKey(generate = true) {
|
|
12573
|
-
try {
|
|
12574
|
-
const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
|
|
12575
|
-
let pem = inline ?? "";
|
|
12576
|
-
if (!inline) {
|
|
12577
|
-
const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
12578
|
-
const file = given || join52(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
|
|
12579
|
-
try {
|
|
12580
|
-
pem = readFileSync37(file);
|
|
12581
|
-
if (!given) {
|
|
12582
|
-
for (const [f, m] of [[file, 384], [dirname22(file), 448]])
|
|
12583
|
-
if (statSync16(f).mode & 63)
|
|
12584
|
-
chmodSync5(f, m);
|
|
12585
|
-
}
|
|
12586
|
-
} catch (e) {
|
|
12587
|
-
if (e.code !== "ENOENT" || !generate)
|
|
12588
|
-
return null;
|
|
12589
|
-
mkdirSync20(dirname22(dirname22(file)), { recursive: true, mode: 448 });
|
|
12590
|
-
mkdirSync20(dirname22(file), { recursive: true, mode: 448 });
|
|
12591
|
-
const tmp = `${file}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
12592
|
-
writeFileSync22(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
|
|
12593
|
-
try {
|
|
12594
|
-
linkSync(tmp, file);
|
|
12595
|
-
} catch (l) {
|
|
12596
|
-
if (l.code !== "EEXIST")
|
|
12597
|
-
throw l;
|
|
12598
|
-
} finally {
|
|
12599
|
-
unlinkSync5(tmp);
|
|
12600
|
-
}
|
|
12601
|
-
pem = readFileSync37(file);
|
|
12602
|
-
}
|
|
12603
|
-
}
|
|
12604
|
-
const k = createPrivateKey(pem);
|
|
12605
|
-
return k.asymmetricKeyType === "ed25519" ? k : null;
|
|
12606
|
-
} catch {
|
|
12607
|
-
return null;
|
|
12608
|
-
}
|
|
12609
|
-
}
|
|
12610
|
-
function signReceipt(runId, hash) {
|
|
12611
|
-
const key = loadSigningKey();
|
|
12612
|
-
if (!key)
|
|
12613
|
-
return { jwt: null, kid: null };
|
|
12614
|
-
const kid = kidOf2(createPublicKey2(key));
|
|
12615
|
-
const input = `${b64u(canonicalJson({ alg: "EdDSA", typ: "JWT", kid }))}.${b64u(canonicalJson({ job_id: runId, run_id: runId, receipt_sha256: hash, iat: Math.floor(Date.now() / 1000) }))}`;
|
|
12616
|
-
return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
|
|
12617
|
-
}
|
|
12618
|
-
var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash11("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest());
|
|
12619
|
-
var init_receipt_signer = () => {};
|
|
12620
|
-
|
|
12621
12707
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
12622
12708
|
import { spawn as spawn7 } from "child_process";
|
|
12623
12709
|
import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
|
|
12624
|
-
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as
|
|
12710
|
+
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync16, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
|
|
12625
12711
|
import { createServer } from "net";
|
|
12626
|
-
import { dirname as dirname23, isAbsolute as isAbsolute8, join as
|
|
12712
|
+
import { dirname as dirname23, isAbsolute as isAbsolute8, join as join54, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
|
|
12627
12713
|
function routeFor(file) {
|
|
12628
12714
|
let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
|
|
12629
12715
|
r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
|
|
@@ -12633,7 +12719,7 @@ function hashScreens(runDir, rels) {
|
|
|
12633
12719
|
const out2 = [];
|
|
12634
12720
|
for (const rel of rels) {
|
|
12635
12721
|
try {
|
|
12636
|
-
out2.push({ path: rel, sha256: sha256File(
|
|
12722
|
+
out2.push({ path: rel, sha256: sha256File(join54(runDir, rel)) });
|
|
12637
12723
|
} catch {}
|
|
12638
12724
|
}
|
|
12639
12725
|
return out2;
|
|
@@ -12646,10 +12732,10 @@ function checkScreens(runDir, screens) {
|
|
|
12646
12732
|
return "evidence_screens entry is malformed";
|
|
12647
12733
|
if (isAbsolute8(s.path) || normalize2(s.path).startsWith(".."))
|
|
12648
12734
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
12649
|
-
const file =
|
|
12735
|
+
const file = join54(runDir, s.path);
|
|
12650
12736
|
let cur = runDir;
|
|
12651
12737
|
for (const seg of normalize2(s.path).split(sep12)) {
|
|
12652
|
-
cur =
|
|
12738
|
+
cur = join54(cur, seg);
|
|
12653
12739
|
if (isSymlink(cur))
|
|
12654
12740
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
12655
12741
|
}
|
|
@@ -12696,11 +12782,11 @@ async function waitUp(url, ms, signal) {
|
|
|
12696
12782
|
}
|
|
12697
12783
|
function openapiPaths(repoDir) {
|
|
12698
12784
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
12699
|
-
const p =
|
|
12785
|
+
const p = join54(repoDir, f);
|
|
12700
12786
|
if (!existsSync32(p))
|
|
12701
12787
|
continue;
|
|
12702
12788
|
try {
|
|
12703
|
-
const txt =
|
|
12789
|
+
const txt = readFileSync39(p, "utf8");
|
|
12704
12790
|
const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
|
|
12705
12791
|
return paths.filter((x) => !x.includes("{")).slice(0, 10);
|
|
12706
12792
|
} catch {
|
|
@@ -12714,24 +12800,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
12714
12800
|
if (!pw || !pkg)
|
|
12715
12801
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
12716
12802
|
try {
|
|
12717
|
-
const dir =
|
|
12803
|
+
const dir = join54(runDir, relDir, "e2e");
|
|
12718
12804
|
mkdirSync21(dir, { recursive: true });
|
|
12719
|
-
writeFileSync23(
|
|
12720
|
-
writeFileSync23(
|
|
12805
|
+
writeFileSync23(join54(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
12806
|
+
writeFileSync23(join54(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
12721
12807
|
`);
|
|
12722
|
-
if (!await shoot(pw, ["test", "--config",
|
|
12808
|
+
if (!await shoot(pw, ["test", "--config", join54(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
12723
12809
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
12724
12810
|
const rels = [];
|
|
12725
12811
|
const walk = (d) => {
|
|
12726
|
-
for (const e of
|
|
12727
|
-
const full =
|
|
12812
|
+
for (const e of readdirSync16(d, { withFileTypes: true })) {
|
|
12813
|
+
const full = join54(d, e.name);
|
|
12728
12814
|
if (e.isDirectory())
|
|
12729
12815
|
walk(full);
|
|
12730
12816
|
else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
|
|
12731
|
-
rels.push(
|
|
12817
|
+
rels.push(join54(relDir, "e2e", relative8(dir, full)));
|
|
12732
12818
|
}
|
|
12733
12819
|
};
|
|
12734
|
-
walk(
|
|
12820
|
+
walk(join54(dir, "out"));
|
|
12735
12821
|
return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
|
|
12736
12822
|
} catch (e) {
|
|
12737
12823
|
return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
|
|
@@ -12791,6 +12877,16 @@ async function stopServer(c, graceMs, signal) {
|
|
|
12791
12877
|
for (let i = 0;i < 10 && groupAlive(pid); i++)
|
|
12792
12878
|
await new Promise((r) => setTimeout(r, 50));
|
|
12793
12879
|
}
|
|
12880
|
+
function noRootReason(subs, lacking) {
|
|
12881
|
+
const list = (xs) => xs.map((d) => `${d}/`).join(" or ");
|
|
12882
|
+
if (subs.length === 0)
|
|
12883
|
+
return "no package.json";
|
|
12884
|
+
if (lacking.length === subs.length)
|
|
12885
|
+
return `no dev, preview or start script in ${list(lacking)} (no root package.json)`;
|
|
12886
|
+
if (lacking.length === 0)
|
|
12887
|
+
return `no root package.json (${list(subs)} have a dev, preview or start script but no UI boot was resolved)`;
|
|
12888
|
+
return `no root package.json; no dev, preview or start script in ${list(lacking)} (${list(subs.filter((d) => !lacking.includes(d)))} has one)`;
|
|
12889
|
+
}
|
|
12794
12890
|
async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
12795
12891
|
const skip = (why) => ({ screens: [], http: false, skipped: why });
|
|
12796
12892
|
const signal = opts.signal;
|
|
@@ -12800,13 +12896,22 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12800
12896
|
if (!visualEvidenceEnabled(opts.env))
|
|
12801
12897
|
return skip("LOKI_VISUAL_EVIDENCE is 0");
|
|
12802
12898
|
const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
|
|
12803
|
-
const appDir = boot ?
|
|
12804
|
-
const pkgPath =
|
|
12899
|
+
const appDir = boot ? join54(repoDir, boot.boot.cwd) : repoDir;
|
|
12900
|
+
const pkgPath = join54(appDir, "package.json");
|
|
12805
12901
|
let script = null;
|
|
12806
12902
|
if (!boot) {
|
|
12807
|
-
if (!existsSync32(pkgPath))
|
|
12808
|
-
|
|
12809
|
-
|
|
12903
|
+
if (!existsSync32(pkgPath)) {
|
|
12904
|
+
const subs = ["backend", "frontend"].filter((d) => existsSync32(join54(repoDir, d, "package.json")));
|
|
12905
|
+
const has = (d) => {
|
|
12906
|
+
try {
|
|
12907
|
+
return pickScript(JSON.parse(readFileSync39(join54(repoDir, d, "package.json"), "utf8"))) !== null;
|
|
12908
|
+
} catch {
|
|
12909
|
+
return false;
|
|
12910
|
+
}
|
|
12911
|
+
};
|
|
12912
|
+
return skip(noRootReason(subs, subs.filter((d) => !has(d))));
|
|
12913
|
+
}
|
|
12914
|
+
script = pickScript(JSON.parse(readFileSync39(pkgPath, "utf8")));
|
|
12810
12915
|
if (!script)
|
|
12811
12916
|
return skip("no dev, preview or start script");
|
|
12812
12917
|
}
|
|
@@ -12833,20 +12938,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12833
12938
|
}
|
|
12834
12939
|
if (!await waitUp(base, left(), signal))
|
|
12835
12940
|
return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
|
|
12836
|
-
const evRoot =
|
|
12941
|
+
const evRoot = join54(runDir, "evidence");
|
|
12837
12942
|
if (isSymlink(evRoot))
|
|
12838
12943
|
return skip("evidence dir is a symlink");
|
|
12839
12944
|
mkdirSync21(evRoot, { recursive: true });
|
|
12840
12945
|
if (pages.length > 0) {
|
|
12841
|
-
const rel0 =
|
|
12842
|
-
mkdirSync21(
|
|
12946
|
+
const rel0 = join54("evidence", randomBytes4(6).toString("hex"));
|
|
12947
|
+
mkdirSync21(join54(runDir, rel0));
|
|
12843
12948
|
const rels = [];
|
|
12844
12949
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
12845
12950
|
if (aborted() || left() <= 0)
|
|
12846
12951
|
break;
|
|
12847
|
-
const rel =
|
|
12848
|
-
const ok = await shoot(pw, ["screenshot", `${base}${route}`,
|
|
12849
|
-
if (ok && !aborted() && !isSymlink(
|
|
12952
|
+
const rel = join54(rel0, `${screenName(route)}.png`);
|
|
12953
|
+
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join54(runDir, rel)], appDir, left(), signal);
|
|
12954
|
+
if (ok && !aborted() && !isSymlink(join54(runDir, rel)) && existsSync32(join54(runDir, rel)))
|
|
12850
12955
|
rels.push(rel);
|
|
12851
12956
|
}
|
|
12852
12957
|
if (aborted())
|
|
@@ -12871,9 +12976,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12871
12976
|
}
|
|
12872
12977
|
if (aborted())
|
|
12873
12978
|
return skip("aborted");
|
|
12874
|
-
const hdir =
|
|
12979
|
+
const hdir = join54(evRoot, randomBytes4(6).toString("hex"));
|
|
12875
12980
|
mkdirSync21(hdir);
|
|
12876
|
-
writeFileSync23(
|
|
12981
|
+
writeFileSync23(join54(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
12877
12982
|
`);
|
|
12878
12983
|
return { screens: [], http: true, skipped: null };
|
|
12879
12984
|
} catch (e) {
|
|
@@ -12898,12 +13003,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
12898
13003
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
12899
13004
|
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
|
|
12900
13005
|
}
|
|
12901
|
-
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(
|
|
12902
|
-
const bin =
|
|
13006
|
+
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(readFileSync39(p)).digest("hex"), playwrightBin = (repoDir) => {
|
|
13007
|
+
const bin = join54(repoDir, "node_modules", ".bin", "playwright");
|
|
12903
13008
|
return existsSync32(bin) ? bin : null;
|
|
12904
13009
|
}, playwrightTestPkg = (repoDir) => {
|
|
12905
|
-
const d =
|
|
12906
|
-
return existsSync32(
|
|
13010
|
+
const d = join54(repoDir, "node_modules", "@playwright", "test");
|
|
13011
|
+
return existsSync32(join54(d, "package.json")) ? d : null;
|
|
12907
13012
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
12908
13013
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
12909
13014
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
@@ -12934,14 +13039,14 @@ var init_visual_evidence = __esm(() => {
|
|
|
12934
13039
|
});
|
|
12935
13040
|
|
|
12936
13041
|
// ../../loki-ts/src/council/role_agents.ts
|
|
12937
|
-
import { existsSync as existsSync33, readdirSync as
|
|
13042
|
+
import { existsSync as existsSync33, readdirSync as readdirSync17, readFileSync as readFileSync40 } from "fs";
|
|
12938
13043
|
import { homedir as homedir9 } from "os";
|
|
12939
|
-
import { join as
|
|
13044
|
+
import { join as join55 } from "path";
|
|
12940
13045
|
function agentDirs(repoDir, env = process.env) {
|
|
12941
|
-
return [env["LOKI_AGENTS_PROJECT_DIR"] ||
|
|
13046
|
+
return [env["LOKI_AGENTS_PROJECT_DIR"] || join55(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join55(env["XDG_CONFIG_HOME"] || join55(homedir9(), ".config"), "loki", "agents")];
|
|
12942
13047
|
}
|
|
12943
13048
|
function parseAgent(file, stem2) {
|
|
12944
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(
|
|
13049
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync40(file, "utf8"));
|
|
12945
13050
|
if (!m)
|
|
12946
13051
|
return null;
|
|
12947
13052
|
const body = m[2].trim();
|
|
@@ -12959,14 +13064,14 @@ function loadRoleAgent(repoDir, role, env = process.env) {
|
|
|
12959
13064
|
continue;
|
|
12960
13065
|
let names;
|
|
12961
13066
|
try {
|
|
12962
|
-
names =
|
|
13067
|
+
names = readdirSync17(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
|
|
12963
13068
|
} catch {
|
|
12964
13069
|
continue;
|
|
12965
13070
|
}
|
|
12966
13071
|
const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
|
|
12967
13072
|
for (const n2 of ordered) {
|
|
12968
13073
|
try {
|
|
12969
|
-
const a = parseAgent(
|
|
13074
|
+
const a = parseAgent(join55(d, n2), n2.slice(0, -3));
|
|
12970
13075
|
if (a && a.role === role)
|
|
12971
13076
|
return a;
|
|
12972
13077
|
} catch {}
|
|
@@ -12983,8 +13088,8 @@ var init_role_agents = __esm(() => {
|
|
|
12983
13088
|
});
|
|
12984
13089
|
|
|
12985
13090
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
12986
|
-
import { existsSync as existsSync34, readFileSync as
|
|
12987
|
-
import { join as
|
|
13091
|
+
import { existsSync as existsSync34, readFileSync as readFileSync41 } from "fs";
|
|
13092
|
+
import { join as join56 } from "path";
|
|
12988
13093
|
function minVerdict(verdict, review) {
|
|
12989
13094
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
12990
13095
|
return RANK3[ceiling] < RANK3[verdict] ? ceiling : verdict;
|
|
@@ -12997,11 +13102,11 @@ function explicitReview(repoDir, env) {
|
|
|
12997
13102
|
if (flag && isOff(ev))
|
|
12998
13103
|
return "off";
|
|
12999
13104
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
13000
|
-
const p =
|
|
13105
|
+
const p = join56(repoDir, f);
|
|
13001
13106
|
if (!existsSync34(p))
|
|
13002
13107
|
continue;
|
|
13003
13108
|
try {
|
|
13004
|
-
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(
|
|
13109
|
+
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync41(p, "utf8"));
|
|
13005
13110
|
const v = asProvider(m?.[1]);
|
|
13006
13111
|
if (v)
|
|
13007
13112
|
return v;
|
|
@@ -13211,16 +13316,41 @@ function recordRun(env, repoDir, model, verdict, usd, unmetered, wallS, cacheRoo
|
|
|
13211
13316
|
return false;
|
|
13212
13317
|
return appendRunOutcome(repoKey(readOriginUrl(repoDir), repoDir), { shape, executor: /haiku/i.test(model) ? "haiku" : "sonnet", verdict: "pass", owner: null, escalated: false, usd, wallS, wallKind: WALL_KIND }, cacheRoot);
|
|
13213
13318
|
}
|
|
13214
|
-
var NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0";
|
|
13319
|
+
var MIN_RUNS = 3, NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0", ESTIMATE_HELP;
|
|
13215
13320
|
var init_cost_preview = __esm(() => {
|
|
13216
13321
|
init_cache();
|
|
13322
|
+
init_session();
|
|
13217
13323
|
init_history();
|
|
13218
13324
|
init_engine_origin();
|
|
13325
|
+
ESTIMATE_HELP = `loki estimate - Show what a run is likely to cost
|
|
13326
|
+
|
|
13327
|
+
Usage: loki estimate [--model M] [--provider P] [--json]
|
|
13328
|
+
|
|
13329
|
+
Prints the same estimate line "loki start" prints before a run: the dollar and time
|
|
13330
|
+
range of this repo's prior verified runs of the same project shape. With fewer than
|
|
13331
|
+
${MIN_RUNS} such runs it prints a rough per-model prior, or NOT AVAILABLE (reason).
|
|
13332
|
+
It never estimates from task text and never prints a number it did not measure.
|
|
13333
|
+
|
|
13334
|
+
Options:
|
|
13335
|
+
--model M Model whose rough prior to use when there is no history (default: the start model)
|
|
13336
|
+
--provider P Provider whose default model is used (default: LOKI_PROVIDER or claude)
|
|
13337
|
+
--json Machine-readable output (the estimate result object)
|
|
13338
|
+
--help, -h Show this help
|
|
13339
|
+
|
|
13340
|
+
Changed: --iterations is removed. The old iteration-based projection (tools/estimate-run.py)
|
|
13341
|
+
is replaced by this single estimator. LOKI_COST_PREVIEW=0 turns the estimate off.
|
|
13342
|
+
|
|
13343
|
+
Examples:
|
|
13344
|
+
loki estimate
|
|
13345
|
+
loki estimate --json
|
|
13346
|
+
|
|
13347
|
+
See also: loki cost --detail (what a run ALREADY spent)
|
|
13348
|
+
`;
|
|
13219
13349
|
});
|
|
13220
13350
|
|
|
13221
13351
|
// ../../loki-ts/src/features/contract.ts
|
|
13222
|
-
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as
|
|
13223
|
-
import { dirname as dirname24, join as
|
|
13352
|
+
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync42, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
13353
|
+
import { dirname as dirname24, join as join57 } from "path";
|
|
13224
13354
|
import { createHash as createHash13 } from "crypto";
|
|
13225
13355
|
function contractEnabled(env = process.env) {
|
|
13226
13356
|
return env["LOKI_CONTRACT"] !== "0";
|
|
@@ -13252,7 +13382,7 @@ function untracedLines(trace) {
|
|
|
13252
13382
|
return trace.criteria.filter((c) => c.status === "no_match").map((c) => `contract ${sanitizeCriterion(c.id)} untraced (no keyword match in changed files): ${sanitizeCriterion(c.text)}`);
|
|
13253
13383
|
}
|
|
13254
13384
|
function contractPath(repoDir) {
|
|
13255
|
-
return
|
|
13385
|
+
return join57(repoDir, ".loki", "contract.json");
|
|
13256
13386
|
}
|
|
13257
13387
|
function readContract(repoDir) {
|
|
13258
13388
|
const p = contractPath(repoDir);
|
|
@@ -13268,7 +13398,7 @@ function readContract(repoDir) {
|
|
|
13268
13398
|
return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
|
|
13269
13399
|
let raw2;
|
|
13270
13400
|
try {
|
|
13271
|
-
raw2 =
|
|
13401
|
+
raw2 = readFileSync42(p);
|
|
13272
13402
|
} catch {
|
|
13273
13403
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
13274
13404
|
}
|
|
@@ -13337,16 +13467,16 @@ var init_contract = __esm(() => {
|
|
|
13337
13467
|
init_safe_git();
|
|
13338
13468
|
STOP2 = new Set(["the", "and", "for", "with", "that", "this", "must", "should", "shall", "when", "then", "are", "was", "all", "any", "can", "not", "has", "have", "from", "into", "each", "user", "users"]);
|
|
13339
13469
|
MAX_CONTRACT_BYTES = 1024 * 1024;
|
|
13340
|
-
REQ_MAP_REL =
|
|
13470
|
+
REQ_MAP_REL = join57(".loki", "state", "requirements-map.json");
|
|
13341
13471
|
REQ_STOP = new Set(["with", "that", "this", "from", "have", "will", "should", "must", "when", "then", "into", "each", "every", "user", "users", "page", "able", "allow", "support", "ensure", "using", "their", "there", "which", "shall"]);
|
|
13342
13472
|
});
|
|
13343
13473
|
|
|
13344
13474
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
13345
13475
|
import { createHash as createHash14 } from "crypto";
|
|
13346
|
-
import { existsSync as existsSync36, readdirSync as
|
|
13347
|
-
import { basename as basename19, dirname as dirname25, join as
|
|
13476
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync18, readFileSync as readFileSync43, renameSync as renameSync9, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
|
|
13477
|
+
import { basename as basename19, dirname as dirname25, join as join58 } from "path";
|
|
13348
13478
|
function sealGroup(runDir, rh) {
|
|
13349
|
-
const manifestPath =
|
|
13479
|
+
const manifestPath = join58(runDir, "group", "manifest.json");
|
|
13350
13480
|
if (!existsSync36(manifestPath))
|
|
13351
13481
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
13352
13482
|
const notProven = [];
|
|
@@ -13371,10 +13501,10 @@ function sealGroup(runDir, rh) {
|
|
|
13371
13501
|
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
13372
13502
|
seen.add(id);
|
|
13373
13503
|
seenLower.add(id.toLowerCase());
|
|
13374
|
-
const dir =
|
|
13504
|
+
const dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
|
|
13375
13505
|
let bytes;
|
|
13376
13506
|
try {
|
|
13377
|
-
bytes =
|
|
13507
|
+
bytes = readFileSync43(rp);
|
|
13378
13508
|
} catch {
|
|
13379
13509
|
return fail(`unit ${id} sub-receipt missing`);
|
|
13380
13510
|
}
|
|
@@ -13391,7 +13521,7 @@ function sealGroup(runDir, rh) {
|
|
|
13391
13521
|
unit_id: id,
|
|
13392
13522
|
sub_receipt_sha256: sha2(bytes),
|
|
13393
13523
|
receipt_sha256: sub["receipt_sha256"],
|
|
13394
|
-
events_file_sha256: existsSync36(ep) ? sha2(
|
|
13524
|
+
events_file_sha256: existsSync36(ep) ? sha2(readFileSync43(ep)) : null,
|
|
13395
13525
|
verdict,
|
|
13396
13526
|
goal: text2(u["goal"], 200),
|
|
13397
13527
|
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
@@ -13416,7 +13546,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13416
13546
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
13417
13547
|
const g = receipt["group"];
|
|
13418
13548
|
if (g === undefined)
|
|
13419
|
-
return existsSync36(
|
|
13549
|
+
return existsSync36(join58(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
13420
13550
|
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
13421
13551
|
return bad("group section malformed");
|
|
13422
13552
|
const entries = g["units"];
|
|
@@ -13428,20 +13558,20 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13428
13558
|
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
13429
13559
|
let unchecked = null;
|
|
13430
13560
|
for (let i = 0;i < entries.length; i++) {
|
|
13431
|
-
const e = entries[i], id = ids[i], dir =
|
|
13561
|
+
const e = entries[i], id = ids[i], dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
|
|
13432
13562
|
if (e["index"] !== i)
|
|
13433
13563
|
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
13434
13564
|
let bytes;
|
|
13435
13565
|
try {
|
|
13436
13566
|
if (!statSync17(dir).isDirectory())
|
|
13437
13567
|
return bad(`unit ${id} is not a directory`);
|
|
13438
|
-
bytes =
|
|
13568
|
+
bytes = readFileSync43(rp);
|
|
13439
13569
|
} catch {
|
|
13440
13570
|
return bad(`unit ${id} sub-receipt missing`);
|
|
13441
13571
|
}
|
|
13442
13572
|
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
13443
13573
|
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
13444
|
-
const evSha = existsSync36(ep) ? sha2(
|
|
13574
|
+
const evSha = existsSync36(ep) ? sha2(readFileSync43(ep)) : null;
|
|
13445
13575
|
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
13446
13576
|
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
13447
13577
|
const sub = readJson5(rp);
|
|
@@ -13461,8 +13591,8 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13461
13591
|
}
|
|
13462
13592
|
function compositeVerdict(childVerdicts) {
|
|
13463
13593
|
const base = childVerdicts.includes("SPEC_CONFLICT") ? "SPEC_CONFLICT" : childVerdicts.length > 0 && childVerdicts.every((v) => v === "ALREADY_SATISFIED") ? "ALREADY_SATISFIED" : "VERIFIED";
|
|
13464
|
-
const
|
|
13465
|
-
return capGroupVerdict(base, { section: undefined, notProven: [], problems:
|
|
13594
|
+
const failed2 = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
|
|
13595
|
+
return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed2, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
|
|
13466
13596
|
}
|
|
13467
13597
|
async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
13468
13598
|
const runDir = dirname25(receiptPath);
|
|
@@ -13473,13 +13603,13 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13473
13603
|
return bad("body malformed");
|
|
13474
13604
|
let ibytes;
|
|
13475
13605
|
try {
|
|
13476
|
-
ibytes =
|
|
13606
|
+
ibytes = readFileSync43(join58(runDir, "integration.json"));
|
|
13477
13607
|
} catch {
|
|
13478
13608
|
return bad("integration.json missing");
|
|
13479
13609
|
}
|
|
13480
13610
|
if (sha2(ibytes) !== receipt["integration_sha256"])
|
|
13481
13611
|
return bad("integration.json does not match its recorded hash");
|
|
13482
|
-
const integ = readJson5(
|
|
13612
|
+
const integ = readJson5(join58(runDir, "integration.json"));
|
|
13483
13613
|
if (!isObj10(integ) || !isObj10(integ["outcomes"]))
|
|
13484
13614
|
return bad("integration.json has no outcomes");
|
|
13485
13615
|
const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
|
|
@@ -13497,10 +13627,10 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13497
13627
|
return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
|
|
13498
13628
|
if (outcomes[repo] !== "ok")
|
|
13499
13629
|
return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
|
|
13500
|
-
const rp =
|
|
13630
|
+
const rp = join58(runDir, rel);
|
|
13501
13631
|
let bytes;
|
|
13502
13632
|
try {
|
|
13503
|
-
bytes =
|
|
13633
|
+
bytes = readFileSync43(rp);
|
|
13504
13634
|
} catch {
|
|
13505
13635
|
return bad(`child ${repo} receipt missing`);
|
|
13506
13636
|
}
|
|
@@ -13535,15 +13665,15 @@ function compositeHash(r) {
|
|
|
13535
13665
|
const { composite_sha256: _c, verification: _v, ...body } = r;
|
|
13536
13666
|
return sha2(canonicalJson(body));
|
|
13537
13667
|
}
|
|
13538
|
-
var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) =>
|
|
13668
|
+
var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join58(runDir, "group", "units"), readJson5 = (p) => {
|
|
13539
13669
|
try {
|
|
13540
|
-
return JSON.parse(
|
|
13670
|
+
return JSON.parse(readFileSync43(p, "utf8"));
|
|
13541
13671
|
} catch {
|
|
13542
13672
|
return;
|
|
13543
13673
|
}
|
|
13544
13674
|
}, dirNames = (d) => {
|
|
13545
13675
|
try {
|
|
13546
|
-
return
|
|
13676
|
+
return readdirSync18(d).filter((n2) => !n2.startsWith(".")).sort();
|
|
13547
13677
|
} catch {
|
|
13548
13678
|
return null;
|
|
13549
13679
|
}
|
|
@@ -13555,8 +13685,8 @@ var init_seal_group = __esm(() => {
|
|
|
13555
13685
|
});
|
|
13556
13686
|
|
|
13557
13687
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
13558
|
-
import { existsSync as existsSync37, readFileSync as
|
|
13559
|
-
import { basename as basename20, join as
|
|
13688
|
+
import { existsSync as existsSync37, readFileSync as readFileSync44, statSync as statSync18 } from "fs";
|
|
13689
|
+
import { basename as basename20, join as join59 } from "path";
|
|
13560
13690
|
function supplyEnabled(env) {
|
|
13561
13691
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
13562
13692
|
}
|
|
@@ -13571,13 +13701,13 @@ function isManifestPath(path) {
|
|
|
13571
13701
|
return MANIFESTS.test(basename20(path));
|
|
13572
13702
|
}
|
|
13573
13703
|
function readDeclared(repoDir) {
|
|
13574
|
-
const p =
|
|
13704
|
+
const p = join59(repoDir, ".loki", DECLARED_FILE);
|
|
13575
13705
|
if (!existsSync37(p))
|
|
13576
13706
|
return { deps: [], problem: null };
|
|
13577
13707
|
try {
|
|
13578
13708
|
if (statSync18(p).size > MAX_FILE_BYTES)
|
|
13579
13709
|
return { deps: [], problem: "declaration file too large" };
|
|
13580
|
-
const j = JSON.parse(
|
|
13710
|
+
const j = JSON.parse(readFileSync44(p, "utf8"));
|
|
13581
13711
|
if (!Array.isArray(j))
|
|
13582
13712
|
return { deps: [], problem: "declaration is not a JSON array" };
|
|
13583
13713
|
const deps = [];
|
|
@@ -13597,11 +13727,11 @@ function readDeclared(repoDir) {
|
|
|
13597
13727
|
}
|
|
13598
13728
|
}
|
|
13599
13729
|
function readAllowlist(repoDir) {
|
|
13600
|
-
const p =
|
|
13730
|
+
const p = join59(repoDir, ".loki", "supply-allowlist");
|
|
13601
13731
|
if (!existsSync37(p))
|
|
13602
13732
|
return new Set;
|
|
13603
13733
|
try {
|
|
13604
|
-
return new Set(
|
|
13734
|
+
return new Set(readFileSync44(p, "utf8").split(`
|
|
13605
13735
|
`).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
|
|
13606
13736
|
} catch {
|
|
13607
13737
|
return new Set;
|
|
@@ -13642,7 +13772,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13642
13772
|
if (eco === "npm") {
|
|
13643
13773
|
if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
|
|
13644
13774
|
return true;
|
|
13645
|
-
return files(
|
|
13775
|
+
return files(join59(repoDir, ".npmrc"), home ? join59(home, ".npmrc") : "").some((t) => t.split(`
|
|
13646
13776
|
`).some((l) => {
|
|
13647
13777
|
const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
|
|
13648
13778
|
return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
|
|
@@ -13651,7 +13781,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13651
13781
|
if (eco === "pypi") {
|
|
13652
13782
|
if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
|
|
13653
13783
|
return true;
|
|
13654
|
-
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ?
|
|
13784
|
+
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join59(home, ".config", "pip", "pip.conf") : "", home ? join59(home, ".pip", "pip.conf") : "", join59(repoDir, "pip.conf")];
|
|
13655
13785
|
return files(...cfg).some((t) => t.split(`
|
|
13656
13786
|
`).some((l) => {
|
|
13657
13787
|
const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
|
|
@@ -13661,7 +13791,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13661
13791
|
if (eco === "cargo") {
|
|
13662
13792
|
if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
|
|
13663
13793
|
return true;
|
|
13664
|
-
return files(
|
|
13794
|
+
return files(join59(repoDir, ".cargo", "config.toml"), join59(repoDir, ".cargo", "config"), home ? join59(home, ".cargo", "config.toml") : "", home ? join59(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
|
|
13665
13795
|
}
|
|
13666
13796
|
if (eco === "go") {
|
|
13667
13797
|
const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
|
|
@@ -13784,7 +13914,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
|
|
|
13784
13914
|
}
|
|
13785
13915
|
}, PUBLIC_HOSTS, hostOf = (u) => u.trim().toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "").replace(/^www\./, "").split(/[/?#\s]/)[0].replace(/^[^@]*@/, "").replace(/:\d+$/, ""), privateUrl = (u) => /[a-z0-9]/i.test(u) && !PUBLIC_HOSTS.has(hostOf(u)), readSmall = (p) => {
|
|
13786
13916
|
try {
|
|
13787
|
-
return statSync18(p).size <= MAX_FILE_BYTES ?
|
|
13917
|
+
return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync44(p, "utf8") : "";
|
|
13788
13918
|
} catch {
|
|
13789
13919
|
return "";
|
|
13790
13920
|
}
|
|
@@ -13800,8 +13930,8 @@ var init_supply_guard = __esm(() => {
|
|
|
13800
13930
|
|
|
13801
13931
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
13802
13932
|
import { createHash as createHash15 } from "crypto";
|
|
13803
|
-
import { existsSync as existsSync38, readdirSync as
|
|
13804
|
-
import { basename as basename21, isAbsolute as isAbsolute9, join as
|
|
13933
|
+
import { existsSync as existsSync38, readdirSync as readdirSync19, mkdirSync as mkdirSync24, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
|
|
13934
|
+
import { basename as basename21, isAbsolute as isAbsolute9, join as join60, relative as relative9 } from "path";
|
|
13805
13935
|
function sanitizeReason(s) {
|
|
13806
13936
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
13807
13937
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
@@ -13876,7 +14006,7 @@ function renderReceiptMd(r) {
|
|
|
13876
14006
|
`);
|
|
13877
14007
|
}
|
|
13878
14008
|
var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash15("sha256").update(s).digest("hex"), str3 = (v) => typeof v === "string" && v !== "" ? v : null, strs2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : [], receiptRel = (ctx) => {
|
|
13879
|
-
const r = relative9(ctx.repoDir,
|
|
14009
|
+
const r = relative9(ctx.repoDir, join60(ctx.runDir, "receipt.json"));
|
|
13880
14010
|
return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
13881
14011
|
}, commitStage, sealStage;
|
|
13882
14012
|
var init_seal = __esm(() => {
|
|
@@ -13979,7 +14109,7 @@ var init_seal = __esm(() => {
|
|
|
13979
14109
|
if (wallNotRun > 0) {
|
|
13980
14110
|
try {
|
|
13981
14111
|
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
|
|
13982
|
-
for (const n2 of
|
|
14112
|
+
for (const n2 of readdirSync19(join60(ctx.runDir, "wall")).sort())
|
|
13983
14113
|
if (!kept.has(n2))
|
|
13984
14114
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
13985
14115
|
} catch {}
|
|
@@ -14040,19 +14170,19 @@ var init_seal = __esm(() => {
|
|
|
14040
14170
|
for (const [s, d] of Object.entries(o))
|
|
14041
14171
|
if (typeof d?.duration_s === "number")
|
|
14042
14172
|
stages[s] = d.duration_s;
|
|
14043
|
-
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(
|
|
14173
|
+
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join60(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
|
|
14044
14174
|
const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
|
|
14045
14175
|
if (iterIds.length === 0)
|
|
14046
14176
|
notProven.add("cost not measured (no iteration ids recorded)");
|
|
14047
14177
|
const cost = ctx.cost.read(ctx.repoDir, iterIds);
|
|
14048
14178
|
if (cost.unmetered)
|
|
14049
14179
|
notProven.add("cost unmetered (CLI invoker; recorded as 0)");
|
|
14050
|
-
const eventsPath =
|
|
14180
|
+
const eventsPath = join60(ctx.runDir, "events.jsonl");
|
|
14051
14181
|
const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
|
|
14052
14182
|
const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
|
|
14053
14183
|
if (wallFiles.length > 0 && wallPassed === null)
|
|
14054
14184
|
notProven.add("wall result not recorded by verify");
|
|
14055
|
-
const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(
|
|
14185
|
+
const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join60(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
|
|
14056
14186
|
if (routeBlock)
|
|
14057
14187
|
for (const l of routeNotProven(routeBlock))
|
|
14058
14188
|
notProven.add(l);
|
|
@@ -14094,7 +14224,7 @@ var init_seal = __esm(() => {
|
|
|
14094
14224
|
provider: ctx.provider,
|
|
14095
14225
|
model: ctx.model,
|
|
14096
14226
|
resumed: o.intake?.resumed === true,
|
|
14097
|
-
events_sha256: sha2562(existsSync38(eventsPath) ?
|
|
14227
|
+
events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync45(eventsPath) : ""),
|
|
14098
14228
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
14099
14229
|
log_seal: true,
|
|
14100
14230
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -14114,11 +14244,11 @@ var init_seal = __esm(() => {
|
|
|
14114
14244
|
hash = receiptSha256(body);
|
|
14115
14245
|
}
|
|
14116
14246
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
14117
|
-
|
|
14118
|
-
const path =
|
|
14119
|
-
|
|
14247
|
+
mkdirSync24(ctx.runDir, { recursive: true });
|
|
14248
|
+
const path = join60(ctx.runDir, "receipt.json");
|
|
14249
|
+
writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
|
|
14120
14250
|
`);
|
|
14121
|
-
|
|
14251
|
+
writeFileSync26(join60(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
14122
14252
|
recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
|
|
14123
14253
|
const signed = sig.jwt !== null;
|
|
14124
14254
|
const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...mp ? { mutation_line: mp.line } : {}, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} };
|
|
@@ -14137,8 +14267,8 @@ var init_seal = __esm(() => {
|
|
|
14137
14267
|
});
|
|
14138
14268
|
|
|
14139
14269
|
// ../../loki-ts/src/features/receipt_dsse.ts
|
|
14140
|
-
import { createPublicKey as createPublicKey3, sign as sign2, verify as
|
|
14141
|
-
import { readFileSync as
|
|
14270
|
+
import { createPublicKey as createPublicKey3, sign as sign2, verify as verify3 } from "crypto";
|
|
14271
|
+
import { readFileSync as readFileSync46 } from "fs";
|
|
14142
14272
|
function pae(payloadType, body) {
|
|
14143
14273
|
const t = Buffer.from(payloadType, "utf8");
|
|
14144
14274
|
return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
|
|
@@ -14157,7 +14287,7 @@ function verifyEnvelope(env, pubFor) {
|
|
|
14157
14287
|
continue;
|
|
14158
14288
|
}
|
|
14159
14289
|
known++;
|
|
14160
|
-
if (
|
|
14290
|
+
if (verify3(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
|
|
14161
14291
|
kid = s.keyid;
|
|
14162
14292
|
break;
|
|
14163
14293
|
}
|
|
@@ -14215,7 +14345,7 @@ function verifyDsseReceipt(env, d) {
|
|
|
14215
14345
|
}
|
|
14216
14346
|
function runIdGuard(receiptPath, runId) {
|
|
14217
14347
|
try {
|
|
14218
|
-
return envelopeRunIdProblem(JSON.parse(
|
|
14348
|
+
return envelopeRunIdProblem(JSON.parse(readFileSync46(receiptPath, "utf8")), runId);
|
|
14219
14349
|
} catch {
|
|
14220
14350
|
return null;
|
|
14221
14351
|
}
|
|
@@ -14229,16 +14359,16 @@ var init_keys_cmd = __esm(() => {
|
|
|
14229
14359
|
});
|
|
14230
14360
|
|
|
14231
14361
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
14232
|
-
import { existsSync as existsSync39, readFileSync as
|
|
14233
|
-
import { basename as basename22, dirname as dirname27, join as
|
|
14234
|
-
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as
|
|
14362
|
+
import { existsSync as existsSync39, readFileSync as readFileSync47, renameSync as renameSync10, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
|
|
14363
|
+
import { basename as basename22, dirname as dirname27, join as join61 } from "path";
|
|
14364
|
+
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify4 } from "crypto";
|
|
14235
14365
|
function pubFor(kid, given) {
|
|
14236
14366
|
if (given)
|
|
14237
14367
|
return kidOf2(given) === kid ? given : undefined;
|
|
14238
14368
|
const active = loadSigningKey(false);
|
|
14239
14369
|
const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
|
|
14240
14370
|
try {
|
|
14241
|
-
return [createPublicKey4(
|
|
14371
|
+
return [createPublicKey4(readFileSync47(f))];
|
|
14242
14372
|
} catch {
|
|
14243
14373
|
return [];
|
|
14244
14374
|
}
|
|
@@ -14262,7 +14392,7 @@ function checkAttestation(jwt, expectedHash, given) {
|
|
|
14262
14392
|
const pub = pubFor(header.kid, given);
|
|
14263
14393
|
if (!pub)
|
|
14264
14394
|
return { status: "unchecked", reason: `no key for kid ${String(header.kid)} on this machine (set LOKI_RECEIPT_SIGNING_KEY_FILE or LOKI_RECEIPT_RETIRED_PUBKEYS)` };
|
|
14265
|
-
if (!
|
|
14395
|
+
if (!verify4(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url")))
|
|
14266
14396
|
return bad("signature does not verify");
|
|
14267
14397
|
return payload?.receipt_sha256 === expectedHash ? { status: "verified", reason: null } : bad("attestation binds a different receipt hash");
|
|
14268
14398
|
}
|
|
@@ -14270,17 +14400,17 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14270
14400
|
const bound = receipt["events_sha256"];
|
|
14271
14401
|
if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
|
|
14272
14402
|
return null;
|
|
14273
|
-
const path =
|
|
14403
|
+
const path = join61(dirname27(receiptPath), "events.jsonl");
|
|
14274
14404
|
if (!existsSync39(path))
|
|
14275
14405
|
return "events.jsonl is missing";
|
|
14276
|
-
const raw2 =
|
|
14406
|
+
const raw2 = readFileSync47(path, "utf8"), events2 = readEvents(path), lines2 = raw2.split(`
|
|
14277
14407
|
`).filter((l) => l.trim() !== "");
|
|
14278
|
-
if (events2.length !==
|
|
14408
|
+
if (events2.length !== lines2.length || events2.some((e, i) => e.seq !== i))
|
|
14279
14409
|
return "events.jsonl has a forged, edited or missing line";
|
|
14280
14410
|
if (events2.some((e) => e.type === "tamper.detected"))
|
|
14281
14411
|
return "the supervisor detected events.jsonl being modified during the run";
|
|
14282
14412
|
const h = createHash16("sha256");
|
|
14283
|
-
for (const l of
|
|
14413
|
+
for (const l of lines2) {
|
|
14284
14414
|
if (h.copy().digest("hex") === bound)
|
|
14285
14415
|
return null;
|
|
14286
14416
|
h.update(l + `
|
|
@@ -14289,7 +14419,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14289
14419
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
14290
14420
|
}
|
|
14291
14421
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
14292
|
-
const path =
|
|
14422
|
+
const path = join61(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
14293
14423
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
14294
14424
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
14295
14425
|
if (done < 0)
|
|
@@ -14297,12 +14427,12 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14297
14427
|
const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
|
|
14298
14428
|
if (!d)
|
|
14299
14429
|
return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
|
|
14300
|
-
const
|
|
14301
|
-
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(
|
|
14430
|
+
const lines2 = readFileSync47(path, "utf8").split(`
|
|
14431
|
+
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines2.slice(0, done + 1).join(`
|
|
14302
14432
|
`) + `
|
|
14303
14433
|
`).digest("hex");
|
|
14304
14434
|
const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
|
|
14305
|
-
if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !
|
|
14435
|
+
if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !verify4(null, Buffer.from(`${sha3}:false`), pub, Buffer.from(d["sig"], "base64url")))
|
|
14306
14436
|
return { verdict: "TAMPERED", reason: "log.sealed line is invalid, forged, or records a tamper" };
|
|
14307
14437
|
return null;
|
|
14308
14438
|
}
|
|
@@ -14311,7 +14441,7 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14311
14441
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
14312
14442
|
let receipt;
|
|
14313
14443
|
try {
|
|
14314
|
-
receipt = JSON.parse((deps.read ?? ((p) =>
|
|
14444
|
+
receipt = JSON.parse((deps.read ?? ((p) => readFileSync47(p, "utf8")))(receiptPath));
|
|
14315
14445
|
} catch {
|
|
14316
14446
|
return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
|
|
14317
14447
|
}
|
|
@@ -14321,9 +14451,8 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14321
14451
|
return verifyCompositeFile(receiptPath, receipt, deps);
|
|
14322
14452
|
const recorded = receipt["receipt_sha256"];
|
|
14323
14453
|
const computed = computeReceiptHash(receipt);
|
|
14324
|
-
if (typeof recorded !== "string" || recorded !== computed)
|
|
14454
|
+
if (typeof recorded !== "string" || recorded !== computed)
|
|
14325
14455
|
return { verdict: "TAMPERED", reasons: [`receipt_sha256 mismatch: recorded ${JSON.stringify(recorded)}, computed ${computed}`] };
|
|
14326
|
-
}
|
|
14327
14456
|
const logProblem = checkEventLog(receiptPath, receipt);
|
|
14328
14457
|
if (logProblem)
|
|
14329
14458
|
return { verdict: "TAMPERED", reasons: [logProblem] };
|
|
@@ -14383,6 +14512,7 @@ async function verifyCompositeFile(receiptPath, receipt, deps) {
|
|
|
14383
14512
|
var computeReceiptHash = (receipt) => receiptSha256(receipt);
|
|
14384
14513
|
var init_verify_cmd = __esm(() => {
|
|
14385
14514
|
init_paths();
|
|
14515
|
+
init_receipt_signer();
|
|
14386
14516
|
init_run_status();
|
|
14387
14517
|
init_events();
|
|
14388
14518
|
init_seal();
|
|
@@ -14393,8 +14523,8 @@ var init_verify_cmd = __esm(() => {
|
|
|
14393
14523
|
});
|
|
14394
14524
|
|
|
14395
14525
|
// src/server/app.ts
|
|
14396
|
-
import { existsSync as existsSync41, readFileSync as
|
|
14397
|
-
import { join as
|
|
14526
|
+
import { existsSync as existsSync41, readFileSync as readFileSync50, statSync as statSync21 } from "fs";
|
|
14527
|
+
import { join as join64, resolve as resolve15, sep as sep14 } from "path";
|
|
14398
14528
|
|
|
14399
14529
|
// node_modules/hono/dist/request/constants.js
|
|
14400
14530
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -20166,6 +20296,9 @@ var SHA = /^[0-9a-f]{40,64}$/;
|
|
|
20166
20296
|
var MAX_RECEIPT_BYTES = 1024 * 1024;
|
|
20167
20297
|
var GIT_TIMEOUT_MS = 5000;
|
|
20168
20298
|
var cache = new Map;
|
|
20299
|
+
var FAIL_TTL_MS = 60000;
|
|
20300
|
+
var failed = new Map;
|
|
20301
|
+
var diffStatGitRuns = { n: 0 };
|
|
20169
20302
|
var gitEnv = () => ({ PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" });
|
|
20170
20303
|
function parseNumstat(out) {
|
|
20171
20304
|
const files = [];
|
|
@@ -20223,13 +20356,19 @@ function diffStatFor(db, source, run) {
|
|
|
20223
20356
|
const base = typeof r?.base_sha === "string" ? r.base_sha : "", head = typeof r?.head_sha === "string" ? r.head_sha : "";
|
|
20224
20357
|
if (!SHA.test(base) || !SHA.test(head))
|
|
20225
20358
|
return null;
|
|
20359
|
+
const failKey = `${key}:${base}:${head}`;
|
|
20360
|
+
const failedAt = failed.get(failKey);
|
|
20361
|
+
if (failedAt !== undefined && Date.now() - failedAt < FAIL_TTL_MS)
|
|
20362
|
+
return null;
|
|
20226
20363
|
try {
|
|
20364
|
+
diffStatGitRuns.n++;
|
|
20227
20365
|
const out = execFileSync("git", ["-C", repoReal, "-c", "core.quotePath=false", "diff", "--numstat", "--no-renames", `${base}..${head}`, "--"], { env: gitEnv(), timeout: GIT_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
20228
20366
|
const files = parseNumstat(out);
|
|
20229
20367
|
const stat = { base, head, files, added: files.reduce((n2, f) => n2 + (f.added ?? 0), 0), removed: files.reduce((n2, f) => n2 + (f.removed ?? 0), 0), source: "git" };
|
|
20230
20368
|
cache.set(key, stat);
|
|
20231
20369
|
return stat;
|
|
20232
20370
|
} catch {
|
|
20371
|
+
failed.set(failKey, Date.now());
|
|
20233
20372
|
return null;
|
|
20234
20373
|
}
|
|
20235
20374
|
}
|
|
@@ -20598,10 +20737,10 @@ function stopReason(evs, verdict) {
|
|
|
20598
20737
|
return { reason: /[.!?]$/.test(reason) ? reason : `${reason}.`, ownRules: OWN_RULES.test(text2) };
|
|
20599
20738
|
}
|
|
20600
20739
|
if (verdict === "FAILED") {
|
|
20601
|
-
const
|
|
20602
|
-
const why = typeof
|
|
20603
|
-
if (
|
|
20604
|
-
return { reason: `The run failed in the ${
|
|
20740
|
+
const failed2 = [...evs].reverse().find((e) => e.type === "stage.failed");
|
|
20741
|
+
const why = typeof failed2?.data?.reason === "string" ? oneLine(failed2.data.reason, 240) : "";
|
|
20742
|
+
if (failed2 && failed2.stage)
|
|
20743
|
+
return { reason: `The run failed in the ${failed2.stage} stage${why ? `: ${why}` : ""}.`, ownRules: false };
|
|
20605
20744
|
const bad = evs.filter((e) => e.type === "test.result" && e.data.result === "fail").length;
|
|
20606
20745
|
return { reason: bad ? `The run failed: ${bad} check${bad === 1 ? "" : "s"} did not pass.` : "The run failed and did not record a reason.", ownRules: false };
|
|
20607
20746
|
}
|
|
@@ -22191,6 +22330,7 @@ var isObj4 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
|
22191
22330
|
var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
|
|
22192
22331
|
var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
22193
22332
|
var MAX_CHECKPOINTS = 50;
|
|
22333
|
+
var ROLLBACK_ALLOW = new Set(["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json", "state", "queue"]);
|
|
22194
22334
|
var validCheckpointId = (id) => SAFE_ID.test(id);
|
|
22195
22335
|
var storeOf = (repoDir) => join17(repoDir, ".loki", "state", "checkpoints");
|
|
22196
22336
|
var lokiOf = (repoDir) => join17(repoDir, ".loki");
|
|
@@ -22330,16 +22470,33 @@ function rollbackCheckpoint(repoDir, id, now2 = new Date) {
|
|
|
22330
22470
|
`);
|
|
22331
22471
|
let restored = 0;
|
|
22332
22472
|
const errors = [];
|
|
22473
|
+
const skipped = (src) => src === join17(real2, "state", "checkpoints");
|
|
22333
22474
|
for (const e of readdirSync4(real2, { withFileTypes: true })) {
|
|
22334
22475
|
if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
|
|
22335
22476
|
continue;
|
|
22336
|
-
|
|
22477
|
+
if (!ROLLBACK_ALLOW.has(e.name)) {
|
|
22478
|
+
errors.push(`${e.name}: not in the rollback allowlist, skipped`);
|
|
22479
|
+
continue;
|
|
22480
|
+
}
|
|
22481
|
+
const src = join17(real2, e.name), dst = join17(loki, e.name);
|
|
22337
22482
|
try {
|
|
22338
22483
|
if (lstatSync2(src).isSymbolicLink()) {
|
|
22339
22484
|
errors.push(`${e.name}: symlink skipped`);
|
|
22340
22485
|
continue;
|
|
22341
22486
|
}
|
|
22342
|
-
|
|
22487
|
+
if (existsSync8(dst) && lstatSync2(dst).isSymbolicLink()) {
|
|
22488
|
+
errors.push(`${e.name}: destination is a symlink, skipped`);
|
|
22489
|
+
continue;
|
|
22490
|
+
}
|
|
22491
|
+
cpSync(src, dst, { recursive: true, filter: (s) => {
|
|
22492
|
+
if (skipped(s))
|
|
22493
|
+
return false;
|
|
22494
|
+
if (lstatSync2(s).isSymbolicLink()) {
|
|
22495
|
+
errors.push(`${s.slice(real2.length + 1)}: nested symlink skipped`);
|
|
22496
|
+
return false;
|
|
22497
|
+
}
|
|
22498
|
+
return true;
|
|
22499
|
+
} });
|
|
22343
22500
|
restored++;
|
|
22344
22501
|
} catch (err) {
|
|
22345
22502
|
errors.push(`${e.name}: ${err.message}`);
|
|
@@ -23561,8 +23718,8 @@ function tasksList(loki, status) {
|
|
|
23561
23718
|
const out2 = order2.map((o) => ("ref" in o) ? byId.get(o.ref) : o);
|
|
23562
23719
|
for (const t of out2) {
|
|
23563
23720
|
if (t.type === "iteration" && !t.description) {
|
|
23564
|
-
const ec = t.exitCode,
|
|
23565
|
-
const o = Number.isInteger(ec) && ec === 0 && !
|
|
23721
|
+
const ec = t.exitCode, failed2 = t._terminal_outcome === "failed";
|
|
23722
|
+
const o = Number.isInteger(ec) && ec === 0 && !failed2 ? "completed cleanly (exit 0)" : Number.isInteger(ec) && ec !== 0 ? `failed (exit ${ec})` : failed2 ? "failed" : t._terminal_outcome === "completed" ? "completed" : t.status === "done" ? "finished" : null;
|
|
23566
23723
|
if (o)
|
|
23567
23724
|
t.description = `Iteration ${o}${t.provider ? `, built by ${t.provider}` : ""}.`;
|
|
23568
23725
|
}
|
|
@@ -24569,7 +24726,7 @@ function mount19(ctx) {
|
|
|
24569
24726
|
function computeStats(db, since) {
|
|
24570
24727
|
const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
|
|
24571
24728
|
const by = {};
|
|
24572
|
-
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0,
|
|
24729
|
+
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed2 = 0, satisfied = 0, other = 0;
|
|
24573
24730
|
let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
|
|
24574
24731
|
for (const r of all) {
|
|
24575
24732
|
if (r.endedAt)
|
|
@@ -24587,7 +24744,7 @@ function computeStats(db, since) {
|
|
|
24587
24744
|
else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
|
|
24588
24745
|
satisfied++;
|
|
24589
24746
|
else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
|
|
24590
|
-
|
|
24747
|
+
failed2++;
|
|
24591
24748
|
else
|
|
24592
24749
|
other++;
|
|
24593
24750
|
}
|
|
@@ -24619,8 +24776,8 @@ function computeStats(db, since) {
|
|
|
24619
24776
|
runs_running: running,
|
|
24620
24777
|
by_verdict: by,
|
|
24621
24778
|
blocked_waiting: blocked,
|
|
24622
|
-
verified_rate: verified + unchecked +
|
|
24623
|
-
breakdown: { verified, failed, already_satisfied: satisfied, other },
|
|
24779
|
+
verified_rate: verified + unchecked + failed2 ? verified / (verified + unchecked + failed2) : null,
|
|
24780
|
+
breakdown: { verified, failed: failed2, already_satisfied: satisfied, other },
|
|
24624
24781
|
verified_unchecked: unchecked,
|
|
24625
24782
|
keys_configured: pubkeysFromEnv().configured === true,
|
|
24626
24783
|
...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
|
|
@@ -24644,9 +24801,9 @@ function mount20(ctx) {
|
|
|
24644
24801
|
}
|
|
24645
24802
|
|
|
24646
24803
|
// src/server/routes/verify.ts
|
|
24647
|
-
import { readFileSync as
|
|
24804
|
+
import { readFileSync as readFileSync48, realpathSync as realpathSync20 } from "fs";
|
|
24648
24805
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
24649
|
-
import { join as
|
|
24806
|
+
import { join as join62, sep as sep13 } from "path";
|
|
24650
24807
|
init_verify_cmd();
|
|
24651
24808
|
init_seal();
|
|
24652
24809
|
init_receipt_dsse();
|
|
@@ -24673,11 +24830,11 @@ function mount21(ctx) {
|
|
|
24673
24830
|
return done(404, "receipt not available", "not_found");
|
|
24674
24831
|
let runDir, file;
|
|
24675
24832
|
try {
|
|
24676
|
-
const root = realpathSync20(
|
|
24677
|
-
runDir = realpathSync20(
|
|
24678
|
-
if (runDir !==
|
|
24833
|
+
const root = realpathSync20(join62(repo.p, ".loki", "runs"));
|
|
24834
|
+
runDir = realpathSync20(join62(root, run2));
|
|
24835
|
+
if (runDir !== join62(root, run2))
|
|
24679
24836
|
return done(404, "receipt not found", "not_found");
|
|
24680
|
-
file = realpathSync20(
|
|
24837
|
+
file = realpathSync20(join62(runDir, "receipt.json"));
|
|
24681
24838
|
} catch {
|
|
24682
24839
|
return done(404, "receipt not found", "not_found");
|
|
24683
24840
|
}
|
|
@@ -24696,7 +24853,7 @@ function mount21(ctx) {
|
|
|
24696
24853
|
sha3 = r.receiptSha256 ?? null;
|
|
24697
24854
|
}
|
|
24698
24855
|
try {
|
|
24699
|
-
outcome = outcomeOf(JSON.parse(
|
|
24856
|
+
outcome = outcomeOf(JSON.parse(readFileSync48(file, "utf8")));
|
|
24700
24857
|
} catch {
|
|
24701
24858
|
outcome = "UNREADABLE";
|
|
24702
24859
|
}
|
|
@@ -24758,8 +24915,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
24758
24915
|
}
|
|
24759
24916
|
|
|
24760
24917
|
// src/server/legacy/shim.ts
|
|
24761
|
-
import { existsSync as existsSync40, readFileSync as
|
|
24762
|
-
import { join as
|
|
24918
|
+
import { existsSync as existsSync40, readFileSync as readFileSync49 } from "fs";
|
|
24919
|
+
import { join as join63 } from "path";
|
|
24763
24920
|
|
|
24764
24921
|
// src/server/legacy/routes.ts
|
|
24765
24922
|
var LEGACY_ROUTES = [
|
|
@@ -25102,11 +25259,12 @@ function metricsMapped(repoDir) {
|
|
|
25102
25259
|
// src/server/legacy/routes-control.ts
|
|
25103
25260
|
function controlMapped(db, repoDir) {
|
|
25104
25261
|
const loki = () => lokiDirOf(repoDir);
|
|
25262
|
+
const ctl = (action) => (c) => peerIsLoopback(c) && isLoopbackHost(c.req.header("host")) ? runControl(db, repoDir, action, c, "legacy") : c.json({ error: "loopback only" }, 403);
|
|
25105
25263
|
return {
|
|
25106
|
-
"POST /api/control/pause": (
|
|
25107
|
-
"POST /api/control/resume": (
|
|
25108
|
-
"POST /api/control/stop": (
|
|
25109
|
-
"POST /api/council/force-review": (
|
|
25264
|
+
"POST /api/control/pause": ctl("pause"),
|
|
25265
|
+
"POST /api/control/resume": ctl("resume"),
|
|
25266
|
+
"POST /api/control/stop": ctl("stop"),
|
|
25267
|
+
"POST /api/council/force-review": ctl("council-review"),
|
|
25110
25268
|
"GET /api/council/state": (c) => c.json(councilState(loki())),
|
|
25111
25269
|
"GET /api/council/verdicts": (c) => {
|
|
25112
25270
|
const n2 = intQ(c.req.query("limit"), 1000);
|
|
@@ -25131,10 +25289,10 @@ var logged = new Set;
|
|
|
25131
25289
|
var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
|
|
25132
25290
|
var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
25133
25291
|
var repoVersion = () => {
|
|
25134
|
-
for (const f of [
|
|
25292
|
+
for (const f of [join63(import.meta.dir, "../../../../../VERSION")]) {
|
|
25135
25293
|
try {
|
|
25136
25294
|
if (existsSync40(f))
|
|
25137
|
-
return
|
|
25295
|
+
return readFileSync49(f, "utf8").trim();
|
|
25138
25296
|
} catch {}
|
|
25139
25297
|
}
|
|
25140
25298
|
return "unknown";
|
|
@@ -25316,7 +25474,7 @@ function legacyShim(opts) {
|
|
|
25316
25474
|
|
|
25317
25475
|
// src/server/app.ts
|
|
25318
25476
|
var MAX_BODY2 = 1e6;
|
|
25319
|
-
var defaultUiDir = () => [
|
|
25477
|
+
var defaultUiDir = () => [join64(import.meta.dir, "../../ui/dist"), join64(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join64(d, "index.html")));
|
|
25320
25478
|
function createApp(opts) {
|
|
25321
25479
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
25322
25480
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -25345,10 +25503,10 @@ function createApp(opts) {
|
|
|
25345
25503
|
await next();
|
|
25346
25504
|
});
|
|
25347
25505
|
const readInstalled = () => {
|
|
25348
|
-
for (const f of [process.env.LOKI_VERSION_FILE,
|
|
25506
|
+
for (const f of [process.env.LOKI_VERSION_FILE, join64(import.meta.dir, "../../../../VERSION")]) {
|
|
25349
25507
|
try {
|
|
25350
25508
|
if (f)
|
|
25351
|
-
return
|
|
25509
|
+
return readFileSync50(f, "utf8").trim() || "unknown";
|
|
25352
25510
|
} catch {}
|
|
25353
25511
|
}
|
|
25354
25512
|
return "unknown";
|
|
@@ -25463,7 +25621,7 @@ function createApp(opts) {
|
|
|
25463
25621
|
return new Response(Bun.file(f));
|
|
25464
25622
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
25465
25623
|
return c.json({ error: "not found" }, 404);
|
|
25466
|
-
return new Response(Bun.file(
|
|
25624
|
+
return new Response(Bun.file(join64(root, "index.html")));
|
|
25467
25625
|
});
|
|
25468
25626
|
return { app, db, close: () => {
|
|
25469
25627
|
clearInterval(reconciler);
|