loki-mode 11.6.0 → 11.8.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/README.md +19 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/backlog.py +48 -4
- package/autonomy/lib/chat.sh +60 -3
- package/autonomy/lib/loki_yaml.py +8 -2
- package/autonomy/lib/workspace.py +267 -11
- package/autonomy/loki +315 -21
- package/autonomy/trigger-server.py +454 -4
- package/autonomy/verify.sh +9 -0
- package/completions/_loki +4 -0
- package/completions/loki.bash +20 -2
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +2 -1
- package/docs/DEPLOY-PLAN.md +17 -8
- package/docs/EVALUATING.md +8 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +15 -2
- package/docs/dev/architecture-reference.md +8 -0
- package/docs/sentry-intake.md +33 -0
- package/docs/v10/CLI-MODERN.md +1 -1
- package/docs/v10/D61-SPEED.md +12 -0
- package/docs/v10/PROGRESS.md +10 -0
- package/loki-ts/dist/loki.js +1055 -1004
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +789 -634
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/schemas/loki-yaml.schema.json +5 -2
- package/src/policies/engine.js +41 -2
- package/src/policies/types.js +49 -0
- package/web-app/dist/assets/{AdminPage-DhXdJNQk.js → AdminPage-BnIg9ilA.js} +1 -1
- package/web-app/dist/assets/{Avatar-ixVj76fM.js → Avatar-0BTVsQ2A.js} +1 -1
- package/web-app/dist/assets/{Badge-BGMSp22s.js → Badge-Bbq8CXgn.js} +1 -1
- package/web-app/dist/assets/{Button-pYJc32tG.js → Button-CKD4n0ld.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-C1JBBouA.js → CockpitPage-DJhLNLAz.js} +1 -1
- package/web-app/dist/assets/{ComparePage-qD0NV4RK.js → ComparePage-Di2akuII.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-C-yxO58j.js → ErrorBoundary-9lhsYG-z.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-ByPzuE1S.js → EvidenceReceiptPanel-CQDGXvDr.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-BNv_2BEJ.js → GitHubIssuesPanel-Y_w2k9Ej.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CuLZQ9CS.js → GitHubPRsPanel-pMSQedCv.js} +1 -1
- package/web-app/dist/assets/{HomePage-CS_FW8Je.js → HomePage-CIAkiGdt.js} +2 -2
- package/web-app/dist/assets/{LoginPage-DwiNf1Wm.js → LoginPage-BES4Pk6k.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CmQybLPZ.js → MagicPage-B154fMhi.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-D9ERdpv8.js → MetricsPage-BcNyaiUO.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D9ccgdbF.js → NotFoundPage-D28FqYxG.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-B9Jslmgv.js → ProjectPage-C_O3u8Jx.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DnU99Go2.js → ProjectsPage-uqdahuPA.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DcTyx6wo.js → SettingsPage-DLvVA9OZ.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-jEY9ghSr.js → ShowcasePage-BAZP8vsm.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-CSGltW-8.js → SystemSettingsPage-DPYZkLWh.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-gUuJXb9l.js → TeamsPage-BgSyb5Pd.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-37Zhe--v.js → TemplatesPage-D_Jh5VU1.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-B09hRwaR.js → TerminalOutput-4v8poDKb.js} +1 -1
- package/web-app/dist/assets/{activity-q_4MY9J0.js → activity-CE2b6kdw.js} +1 -1
- package/web-app/dist/assets/{bell-X8OC_PFP.js → bell-CxJDlLSR.js} +1 -1
- package/web-app/dist/assets/{bot-C-N1PUOD.js → bot-C2WuK4zw.js} +1 -1
- package/web-app/dist/assets/{check-B2c76Jmp.js → check-81epVldw.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BF76YLNw.js → chevron-left-B6EXh_3C.js} +1 -1
- package/web-app/dist/assets/{circle-alert-AeFKBD8h.js → circle-alert-0iqoo-iy.js} +1 -1
- package/web-app/dist/assets/{clock-CiXRf-FH.js → clock-CFrBpNUp.js} +1 -1
- package/web-app/dist/assets/{cloud-DKQ8SRJ5.js → cloud-BBSFhUT_.js} +1 -1
- package/web-app/dist/assets/{code-xml-DEb6jXWP.js → code-xml-BCU2V1rq.js} +1 -1
- package/web-app/dist/assets/{database-6W7e-E9c.js → database-D2hvO52K.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-Ds2GOuyW.js → dollar-sign-CXFA9b6T.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CZU0jg52.js → file-code-corner-CqTCEEfS.js} +1 -1
- package/web-app/dist/assets/{file-plus-DTVYnXCN.js → file-plus-DYFbkGbs.js} +1 -1
- package/web-app/dist/assets/{globe-0mUzKYDe.js → globe-DpYHLobk.js} +1 -1
- package/web-app/dist/assets/{hammer-BMHZg3YX.js → hammer-CZ97SEYo.js} +1 -1
- package/web-app/dist/assets/{index-BvOTvjax.js → index-DzswgpRo.js} +3 -3
- package/web-app/dist/assets/{layers-Df7TdcWB.js → layers-2Tq0edx4.js} +1 -1
- package/web-app/dist/assets/{loader-circle-kPsVfrSY.js → loader-circle-BFP4kUX-.js} +1 -1
- package/web-app/dist/assets/{lock-CXYcbEc5.js → lock-DkKdZH4d.js} +1 -1
- package/web-app/dist/assets/{package-Cqhua1ID.js → package-ChOGD8yf.js} +1 -1
- package/web-app/dist/assets/{plus-lseXhsn-.js → plus-hggB1o6b.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-OZriI6gD.js → refresh-cw-DYwVLOeF.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CKCfjPg8.js → rotate-ccw-CUdY506q.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DoVyyfKb.js → scroll-text-Wvidz5uN.js} +1 -1
- package/web-app/dist/assets/{server-DGy7TflW.js → server-O11QmkCN.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BCzRcMXi.js → shield-alert-DXXx27uV.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-C_znJd5g.js → thumbs-up-DF8ivs3A.js} +1 -1
- package/web-app/dist/assets/{trash-2-BosCbeeA.js → trash-2-LA6PW0Nf.js} +1 -1
- package/web-app/dist/assets/{trending-down-CkR5zDkV.js → trending-down-Bw_vLKHT.js} +1 -1
- package/web-app/dist/assets/{trending-up-DpUIfOH4.js → trending-up-tX8motha.js} +1 -1
- package/web-app/dist/assets/{upload-CH9aKc8L.js → upload-CxtTWJd2.js} +1 -1
- package/web-app/dist/assets/{usePolling-pzxrHJeg.js → usePolling-D-P5Iofq.js} +1 -1
- package/web-app/dist/assets/{user-B7OHa-VQ.js → user-dKTcuUFq.js} +1 -1
- package/web-app/dist/index.html +1 -1
|
@@ -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 } from "crypto";
|
|
7330
|
+
import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readFileSync as readFileSync18, 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);
|
|
@@ -7674,6 +7740,74 @@ var init_mutation_proof = __esm(() => {
|
|
|
7674
7740
|
DEP_DIRS = ["node_modules", ".venv", "venv"];
|
|
7675
7741
|
});
|
|
7676
7742
|
|
|
7743
|
+
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
7744
|
+
import { closeSync as closeSync5, constants, fstatSync as fstatSync4, lstatSync as lstatSync5, openSync as openSync5, readSync as readSync4, realpathSync as realpathSync14 } from "fs";
|
|
7745
|
+
function readBounded(path) {
|
|
7746
|
+
const l = lstatSync5(path);
|
|
7747
|
+
if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
|
|
7748
|
+
return null;
|
|
7749
|
+
const fd = openSync5(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
7750
|
+
try {
|
|
7751
|
+
const st = fstatSync4(fd);
|
|
7752
|
+
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
7753
|
+
return null;
|
|
7754
|
+
const buf = Buffer.alloc(st.size + 1);
|
|
7755
|
+
let n2 = 0;
|
|
7756
|
+
for (;; ) {
|
|
7757
|
+
const r = readSync4(fd, buf, n2, buf.length - n2, null);
|
|
7758
|
+
if (r <= 0)
|
|
7759
|
+
break;
|
|
7760
|
+
n2 += r;
|
|
7761
|
+
if (n2 >= buf.length)
|
|
7762
|
+
return null;
|
|
7763
|
+
}
|
|
7764
|
+
return buf.subarray(0, n2).toString("utf8");
|
|
7765
|
+
} finally {
|
|
7766
|
+
closeSync5(fd);
|
|
7767
|
+
}
|
|
7768
|
+
}
|
|
7769
|
+
function parseText(txt) {
|
|
7770
|
+
try {
|
|
7771
|
+
const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
|
|
7772
|
+
if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
|
|
7773
|
+
return null;
|
|
7774
|
+
const acc = j.acceptance === undefined ? [] : strs(j.acceptance);
|
|
7775
|
+
if (acc === null)
|
|
7776
|
+
return null;
|
|
7777
|
+
return { id: j.id, writeSet: ws, pack, tokenBudget: tb, ...acc.length ? { acceptance: acc.map((a) => a.slice(0, 500)).slice(0, 50) } : {} };
|
|
7778
|
+
} catch {
|
|
7779
|
+
return null;
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
function parseSpec(path) {
|
|
7783
|
+
try {
|
|
7784
|
+
const txt = readBounded(path);
|
|
7785
|
+
return txt === null ? null : parseText(txt);
|
|
7786
|
+
} catch {
|
|
7787
|
+
return null;
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
function unitSpec(env = process.env) {
|
|
7791
|
+
const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
|
|
7792
|
+
if (env["LOKI_SPEED"] === "0" || !path)
|
|
7793
|
+
return null;
|
|
7794
|
+
if (frozen !== undefined)
|
|
7795
|
+
return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
|
|
7796
|
+
if (!memo.has(path))
|
|
7797
|
+
memo.set(path, parseSpec(path));
|
|
7798
|
+
return memo.get(path) ?? null;
|
|
7799
|
+
}
|
|
7800
|
+
function unitFlags(staged, env = process.env, pre = {}) {
|
|
7801
|
+
const s = unitSpec(env);
|
|
7802
|
+
return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
|
|
7803
|
+
}
|
|
7804
|
+
var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm2 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm2) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
|
|
7805
|
+
var init_unit_mode = __esm(() => {
|
|
7806
|
+
MAX_SPEC_BYTES = 1024 * 1024;
|
|
7807
|
+
CTRL = /[\x00-\x1f\x7f]/;
|
|
7808
|
+
memo = new Map;
|
|
7809
|
+
});
|
|
7810
|
+
|
|
7677
7811
|
// ../../loki-ts/src/features/lean_prefix.ts
|
|
7678
7812
|
var LEAN_PREFIX, STAGE_PREFIX, withStagePrefix = (brief) => process.env["LOKI_SPEED"] !== "0" ? `${STAGE_PREFIX}
|
|
7679
7813
|
|
|
@@ -7683,10 +7817,216 @@ var init_lean_prefix = __esm(() => {
|
|
|
7683
7817
|
STAGE_PREFIX = "You are Loki, an autonomous coding agent working one stage of a run. " + "Follow the stage prompt exactly, including the role it gives you and the rules it sets, " + "and report only what you did. " + "Stage-specific instructions and the task follow this block.";
|
|
7684
7818
|
});
|
|
7685
7819
|
|
|
7686
|
-
// ../../loki-ts/src/
|
|
7820
|
+
// ../../loki-ts/src/util/shell.ts
|
|
7821
|
+
async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
|
|
7822
|
+
const reader = stream.getReader();
|
|
7823
|
+
const decoder = new TextDecoder;
|
|
7824
|
+
let out2 = "";
|
|
7825
|
+
let total = 0;
|
|
7826
|
+
try {
|
|
7827
|
+
while (total < maxBytes) {
|
|
7828
|
+
const { done, value } = await reader.read();
|
|
7829
|
+
if (done)
|
|
7830
|
+
break;
|
|
7831
|
+
if (!value)
|
|
7832
|
+
continue;
|
|
7833
|
+
total += value.byteLength;
|
|
7834
|
+
if (total > maxBytes) {
|
|
7835
|
+
const keep = value.byteLength - (total - maxBytes);
|
|
7836
|
+
out2 += decoder.decode(value.subarray(0, keep), { stream: true });
|
|
7837
|
+
break;
|
|
7838
|
+
}
|
|
7839
|
+
out2 += decoder.decode(value, { stream: true });
|
|
7840
|
+
}
|
|
7841
|
+
out2 += decoder.decode();
|
|
7842
|
+
} finally {
|
|
7843
|
+
try {
|
|
7844
|
+
await reader.cancel();
|
|
7845
|
+
} catch {}
|
|
7846
|
+
reader.releaseLock();
|
|
7847
|
+
}
|
|
7848
|
+
return out2;
|
|
7849
|
+
}
|
|
7850
|
+
async function run(argv, opts = {}) {
|
|
7851
|
+
const proc = Bun.spawn({
|
|
7852
|
+
cmd: [...argv],
|
|
7853
|
+
stdout: "pipe",
|
|
7854
|
+
stderr: "pipe",
|
|
7855
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
7856
|
+
cwd: opts.cwd
|
|
7857
|
+
});
|
|
7858
|
+
let timer;
|
|
7859
|
+
let killTimer;
|
|
7860
|
+
if (opts.timeoutMs && opts.timeoutMs > 0) {
|
|
7861
|
+
timer = setTimeout(() => {
|
|
7862
|
+
try {
|
|
7863
|
+
proc.kill("SIGTERM");
|
|
7864
|
+
} catch {}
|
|
7865
|
+
killTimer = setTimeout(() => {
|
|
7866
|
+
try {
|
|
7867
|
+
proc.kill("SIGKILL");
|
|
7868
|
+
} catch {}
|
|
7869
|
+
}, 2000);
|
|
7870
|
+
}, opts.timeoutMs);
|
|
7871
|
+
}
|
|
7872
|
+
try {
|
|
7873
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
7874
|
+
readStreamCapped(proc.stdout),
|
|
7875
|
+
new Response(proc.stderr).text(),
|
|
7876
|
+
proc.exited
|
|
7877
|
+
]);
|
|
7878
|
+
return { stdout, stderr, exitCode };
|
|
7879
|
+
} finally {
|
|
7880
|
+
if (timer)
|
|
7881
|
+
clearTimeout(timer);
|
|
7882
|
+
if (killTimer)
|
|
7883
|
+
clearTimeout(killTimer);
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
var MAX_STDOUT_BYTES;
|
|
7887
|
+
var init_shell = __esm(() => {
|
|
7888
|
+
MAX_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
7889
|
+
});
|
|
7890
|
+
|
|
7891
|
+
// ../../loki-ts/src/util/python.ts
|
|
7892
|
+
import { accessSync, constants as constants2, existsSync as existsSync16, statSync as statSync13 } from "fs";
|
|
7687
7893
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7688
|
-
import { accessSync, constants, statSync as statSync12 } from "fs";
|
|
7689
7894
|
import { tmpdir as tmpdir5 } from "os";
|
|
7895
|
+
function resolvePython3() {
|
|
7896
|
+
for (const dir of (process.env.PATH ?? "").split(":")) {
|
|
7897
|
+
if (!dir || !dir.startsWith("/"))
|
|
7898
|
+
continue;
|
|
7899
|
+
const cand = `${dir}/python3`;
|
|
7900
|
+
try {
|
|
7901
|
+
accessSync(cand, constants2.X_OK);
|
|
7902
|
+
if (statSync13(cand).isFile())
|
|
7903
|
+
return cand;
|
|
7904
|
+
} catch {}
|
|
7905
|
+
}
|
|
7906
|
+
return null;
|
|
7907
|
+
}
|
|
7908
|
+
function pySignatures(src) {
|
|
7909
|
+
try {
|
|
7910
|
+
const py = resolvePython3();
|
|
7911
|
+
if (!py)
|
|
7912
|
+
return [];
|
|
7913
|
+
const r = spawnSync2(py, ["-I", "-S", "-c", PY_SCRIPT], {
|
|
7914
|
+
input: src.replace(/\r\n?/g, `
|
|
7915
|
+
`),
|
|
7916
|
+
cwd: tmpdir5(),
|
|
7917
|
+
timeout: PY_TIMEOUT_MS,
|
|
7918
|
+
encoding: "utf8",
|
|
7919
|
+
env: { PATH: process.env.PATH ?? "/usr/bin:/bin" },
|
|
7920
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
7921
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
7922
|
+
});
|
|
7923
|
+
if (r.error || r.status !== 0)
|
|
7924
|
+
return [];
|
|
7925
|
+
const parsed = JSON.parse(r.stdout);
|
|
7926
|
+
return Array.isArray(parsed) && parsed.every((l) => typeof l === "string") ? parsed : [];
|
|
7927
|
+
} catch {
|
|
7928
|
+
return [];
|
|
7929
|
+
}
|
|
7930
|
+
}
|
|
7931
|
+
var PY_TIMEOUT_MS = 5000, PY_SCRIPT = `
|
|
7932
|
+
import ast, sys, json, re
|
|
7933
|
+
def u(n): return ast.unparse(n)
|
|
7934
|
+
def chain(n):
|
|
7935
|
+
if isinstance(n, ast.Name): return n.id
|
|
7936
|
+
if isinstance(n, ast.Attribute):
|
|
7937
|
+
c = chain(n.value)
|
|
7938
|
+
return None if c is None else c + "." + n.attr
|
|
7939
|
+
return None
|
|
7940
|
+
def deco(d):
|
|
7941
|
+
if isinstance(d, ast.Call):
|
|
7942
|
+
c = chain(d.func)
|
|
7943
|
+
return "@" + c + "(...)" if c else "@..."
|
|
7944
|
+
c = chain(d)
|
|
7945
|
+
return "@" + c if c else "@..."
|
|
7946
|
+
def lit(n):
|
|
7947
|
+
if isinstance(n, ast.Constant): return u(n)
|
|
7948
|
+
if isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub) and isinstance(n.operand, ast.Constant): return u(n)
|
|
7949
|
+
if isinstance(n, ast.Tuple):
|
|
7950
|
+
p = [lit(e) for e in n.elts]
|
|
7951
|
+
return None if None in p else ", ".join(p)
|
|
7952
|
+
return chain(n)
|
|
7953
|
+
def ann(n):
|
|
7954
|
+
if isinstance(n, (ast.Name, ast.Attribute)): return chain(n)
|
|
7955
|
+
if isinstance(n, ast.Constant): return u(n) if (n.value is None or n.value is Ellipsis) else None
|
|
7956
|
+
if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
|
|
7957
|
+
l, r = ann(n.left), ann(n.right)
|
|
7958
|
+
return None if l is None or r is None else l + " | " + r
|
|
7959
|
+
if isinstance(n, ast.List):
|
|
7960
|
+
p = [ann(e) for e in n.elts]
|
|
7961
|
+
return None if None in p else "[" + ", ".join(p) + "]"
|
|
7962
|
+
if isinstance(n, ast.Tuple):
|
|
7963
|
+
p = [ann(e) for e in n.elts]
|
|
7964
|
+
return None if None in p else ", ".join(p)
|
|
7965
|
+
if isinstance(n, ast.Subscript):
|
|
7966
|
+
v = chain(n.value)
|
|
7967
|
+
if v is None: return None
|
|
7968
|
+
last = v.split(".")[-1]
|
|
7969
|
+
if last == "Literal":
|
|
7970
|
+
s = lit(n.slice)
|
|
7971
|
+
return None if s is None else v + "[" + s + "]"
|
|
7972
|
+
if last == "Annotated" and isinstance(n.slice, ast.Tuple) and n.slice.elts:
|
|
7973
|
+
first = ann(n.slice.elts[0])
|
|
7974
|
+
return None if first is None else v + "[" + first + ", ...]"
|
|
7975
|
+
s = ann(n.slice)
|
|
7976
|
+
return None if s is None else v + "[" + s + "]"
|
|
7977
|
+
return None
|
|
7978
|
+
def a_or_dots(n):
|
|
7979
|
+
r = ann(n)
|
|
7980
|
+
return "..." if r is None else r
|
|
7981
|
+
def clean(n):
|
|
7982
|
+
r = ann(n)
|
|
7983
|
+
return "..." if r is None else r
|
|
7984
|
+
def arg(a, has_default):
|
|
7985
|
+
t = a.arg
|
|
7986
|
+
if a.annotation is not None: t += ": " + a_or_dots(a.annotation)
|
|
7987
|
+
if has_default: t += " = ..." if a.annotation is not None else "=..."
|
|
7988
|
+
return t
|
|
7989
|
+
def args(a):
|
|
7990
|
+
pos = list(a.posonlyargs) + list(a.args)
|
|
7991
|
+
first_default = len(pos) - len(a.defaults)
|
|
7992
|
+
out = []
|
|
7993
|
+
for i, x in enumerate(pos):
|
|
7994
|
+
out.append(arg(x, i >= first_default))
|
|
7995
|
+
if a.posonlyargs and i == len(a.posonlyargs) - 1: out.append("/")
|
|
7996
|
+
if a.vararg is not None: out.append("*" + arg(a.vararg, False))
|
|
7997
|
+
elif a.kwonlyargs: out.append("*")
|
|
7998
|
+
for x, d in zip(a.kwonlyargs, a.kw_defaults): out.append(arg(x, d is not None))
|
|
7999
|
+
if a.kwarg is not None: out.append("**" + arg(a.kwarg, False))
|
|
8000
|
+
return ", ".join(out)
|
|
8001
|
+
def fn(n, ind):
|
|
8002
|
+
r = [ind + deco(d) for d in n.decorator_list]
|
|
8003
|
+
h = ind + ("async def " if isinstance(n, ast.AsyncFunctionDef) else "def ") + n.name + "(" + args(n.args) + ")"
|
|
8004
|
+
if n.returns is not None: h += " -> " + a_or_dots(n.returns)
|
|
8005
|
+
return r + [h + ":"]
|
|
8006
|
+
def cls(n):
|
|
8007
|
+
r = [deco(d) for d in n.decorator_list]
|
|
8008
|
+
parts = [clean(b) for b in n.bases] + [(k.arg + "=" + clean(k.value)) if k.arg else "**..." for k in n.keywords]
|
|
8009
|
+
return r + ["class " + n.name + ("(" + ", ".join(parts) + ")" if parts else "") + ":"]
|
|
8010
|
+
tree = ast.parse(sys.stdin.read())
|
|
8011
|
+
out = []
|
|
8012
|
+
for n in tree.body:
|
|
8013
|
+
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and not n.name.startswith("_"):
|
|
8014
|
+
out += fn(n, "")
|
|
8015
|
+
elif isinstance(n, ast.ClassDef) and not n.name.startswith("_"):
|
|
8016
|
+
out += cls(n)
|
|
8017
|
+
for m in n.body:
|
|
8018
|
+
if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)) and (not m.name.startswith("_") or re.fullmatch(r"__\\w+__", m.name)):
|
|
8019
|
+
out += fn(m, " ")
|
|
8020
|
+
else:
|
|
8021
|
+
t = n.targets[0] if isinstance(n, ast.Assign) and len(n.targets) == 1 else (n.target if isinstance(n, ast.AnnAssign) else None)
|
|
8022
|
+
if isinstance(t, ast.Name) and re.fullmatch(r"[A-Z][A-Z0-9_]*", t.id): out.append(t.id + " = ...")
|
|
8023
|
+
print(json.dumps(out))
|
|
8024
|
+
`;
|
|
8025
|
+
var init_python = __esm(() => {
|
|
8026
|
+
init_shell();
|
|
8027
|
+
});
|
|
8028
|
+
|
|
8029
|
+
// ../../loki-ts/src/util/ts_signatures.ts
|
|
7690
8030
|
function templateEnd(s, i) {
|
|
7691
8031
|
for (let j = i + 1;j < s.length; ) {
|
|
7692
8032
|
const c = s[j];
|
|
@@ -8412,41 +8752,14 @@ function tsSignatures(src, jsx) {
|
|
|
8412
8752
|
}
|
|
8413
8753
|
return out2;
|
|
8414
8754
|
}
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
return cand;
|
|
8424
|
-
} catch {}
|
|
8425
|
-
}
|
|
8426
|
-
return null;
|
|
8427
|
-
}
|
|
8428
|
-
function pySignatures(src) {
|
|
8429
|
-
try {
|
|
8430
|
-
const py = resolvePython3();
|
|
8431
|
-
if (!py)
|
|
8432
|
-
return [];
|
|
8433
|
-
const r = spawnSync2(py, ["-I", "-S", "-c", PY_SCRIPT], {
|
|
8434
|
-
input: norm2(src),
|
|
8435
|
-
cwd: tmpdir5(),
|
|
8436
|
-
timeout: PY_TIMEOUT_MS,
|
|
8437
|
-
encoding: "utf8",
|
|
8438
|
-
env: { PATH: process.env.PATH ?? "/usr/bin:/bin" },
|
|
8439
|
-
maxBuffer: 8 * 1024 * 1024,
|
|
8440
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
8441
|
-
});
|
|
8442
|
-
if (r.error || r.status !== 0)
|
|
8443
|
-
return [];
|
|
8444
|
-
const parsed = JSON.parse(r.stdout);
|
|
8445
|
-
return Array.isArray(parsed) && parsed.every((l) => typeof l === "string") ? parsed : [];
|
|
8446
|
-
} catch {
|
|
8447
|
-
return [];
|
|
8448
|
-
}
|
|
8449
|
-
}
|
|
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
|
|
8450
8763
|
function packageRunner(content) {
|
|
8451
8764
|
try {
|
|
8452
8765
|
const pkg = JSON.parse(content);
|
|
@@ -8459,7 +8772,7 @@ function packageRunner(content) {
|
|
|
8459
8772
|
}
|
|
8460
8773
|
}
|
|
8461
8774
|
function pytestSection(content, header) {
|
|
8462
|
-
const lines =
|
|
8775
|
+
const lines = norm3(content).split(`
|
|
8463
8776
|
`);
|
|
8464
8777
|
const at = lines.findIndex((l) => header.test(l));
|
|
8465
8778
|
if (at < 0)
|
|
@@ -8476,10 +8789,10 @@ function runnerSection(files) {
|
|
|
8476
8789
|
if (js)
|
|
8477
8790
|
out2.push(`runner: ${js}`, "config: package.json (scripts.test only)");
|
|
8478
8791
|
for (const f of get(/^(bunfig\.toml|(vitest|jest)\.config\.[cm]?[jt]s)$/))
|
|
8479
|
-
out2.push(`config: ${f.path}`, ...
|
|
8792
|
+
out2.push(`config: ${f.path}`, ...norm3(f.content).split(`
|
|
8480
8793
|
`).slice(0, 20));
|
|
8481
8794
|
const py = [
|
|
8482
|
-
...get(/^pytest\.ini$/).map((f) => ({ f, lines:
|
|
8795
|
+
...get(/^pytest\.ini$/).map((f) => ({ f, lines: norm3(f.content).split(`
|
|
8483
8796
|
`).filter((l) => l.trim()) })),
|
|
8484
8797
|
...get(/^pyproject\.toml$/).map((f) => ({ f, lines: pytestSection(f.content, /^\[tool\.pytest/) })),
|
|
8485
8798
|
...get(/^(setup\.cfg|tox\.ini)$/).map((f) => ({ f, lines: pytestSection(f.content, /^\[(tool:)?pytest\]/) }))
|
|
@@ -8567,13 +8880,13 @@ function importsNamed(path, raw2, stems, importStems = stems, hits = () => false
|
|
|
8567
8880
|
function safeTs(content, path) {
|
|
8568
8881
|
const jsx = /\.[jt]sx$/.test(path);
|
|
8569
8882
|
try {
|
|
8570
|
-
return tsAmbiguous(
|
|
8883
|
+
return tsAmbiguous(norm3(content), jsx) ? [] : tsSignatures(content, jsx);
|
|
8571
8884
|
} catch {
|
|
8572
8885
|
return [];
|
|
8573
8886
|
}
|
|
8574
8887
|
}
|
|
8575
8888
|
function buildWallManifest(files, taskModules, alsoNamed = []) {
|
|
8576
|
-
const all = [...files].sort(byPath).map((f) => ({ path: f.path.replace(/^\.\//, ""), content:
|
|
8889
|
+
const all = [...files].sort(byPath).map((f) => ({ path: f.path.replace(/^\.\//, ""), content: norm3(f.content) }));
|
|
8577
8890
|
const mods = [...new Set(taskModules.map((m) => m.replace(/^\.\//, "")))].sort();
|
|
8578
8891
|
const excl = [...new Set([...mods, ...alsoNamed.map((m) => m.replace(/^\.\//, ""))])];
|
|
8579
8892
|
const stems = new Set(excl.flatMap(stemsOfTarget));
|
|
@@ -8623,127 +8936,33 @@ var MANIFEST_MAX_LINES = 400, MAX_EXAMPLES = 2, EXAMPLE_LINES = 40, MAX_LAYOUT =
|
|
|
8623
8936
|
const c = content.toLowerCase().replace(/[._-]+/g, ".");
|
|
8624
8937
|
return stems.some((s) => new RegExp(`(?<![a-z0-9])${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![a-z0-9])`).test(c));
|
|
8625
8938
|
}, dirOf = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "", resolveRel = (dir, spec) => {
|
|
8626
|
-
const out2 = dir ? dir.split("/") : [];
|
|
8627
|
-
for (const seg of spec.split("/")) {
|
|
8628
|
-
if (seg === "" || seg === ".")
|
|
8629
|
-
continue;
|
|
8630
|
-
if (seg === "..")
|
|
8631
|
-
out2.pop();
|
|
8632
|
-
else
|
|
8633
|
-
out2.push(seg);
|
|
8634
|
-
}
|
|
8635
|
-
return out2.join("/");
|
|
8636
|
-
}, stemsOfTarget = (m) => {
|
|
8637
|
-
const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
|
|
8638
|
-
return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
|
|
8639
|
-
},
|
|
8640
|
-
`), 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
|
|
8641
|
-
import ast, sys, json, re
|
|
8642
|
-
def u(n): return ast.unparse(n)
|
|
8643
|
-
def chain(n):
|
|
8644
|
-
if isinstance(n, ast.Name): return n.id
|
|
8645
|
-
if isinstance(n, ast.Attribute):
|
|
8646
|
-
c = chain(n.value)
|
|
8647
|
-
return None if c is None else c + "." + n.attr
|
|
8648
|
-
return None
|
|
8649
|
-
def deco(d):
|
|
8650
|
-
if isinstance(d, ast.Call):
|
|
8651
|
-
c = chain(d.func)
|
|
8652
|
-
return "@" + c + "(...)" if c else "@..."
|
|
8653
|
-
c = chain(d)
|
|
8654
|
-
return "@" + c if c else "@..."
|
|
8655
|
-
def lit(n):
|
|
8656
|
-
if isinstance(n, ast.Constant): return u(n)
|
|
8657
|
-
if isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub) and isinstance(n.operand, ast.Constant): return u(n)
|
|
8658
|
-
if isinstance(n, ast.Tuple):
|
|
8659
|
-
p = [lit(e) for e in n.elts]
|
|
8660
|
-
return None if None in p else ", ".join(p)
|
|
8661
|
-
return chain(n)
|
|
8662
|
-
def ann(n):
|
|
8663
|
-
if isinstance(n, (ast.Name, ast.Attribute)): return chain(n)
|
|
8664
|
-
if isinstance(n, ast.Constant): return u(n) if (n.value is None or n.value is Ellipsis) else None
|
|
8665
|
-
if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
|
|
8666
|
-
l, r = ann(n.left), ann(n.right)
|
|
8667
|
-
return None if l is None or r is None else l + " | " + r
|
|
8668
|
-
if isinstance(n, ast.List):
|
|
8669
|
-
p = [ann(e) for e in n.elts]
|
|
8670
|
-
return None if None in p else "[" + ", ".join(p) + "]"
|
|
8671
|
-
if isinstance(n, ast.Tuple):
|
|
8672
|
-
p = [ann(e) for e in n.elts]
|
|
8673
|
-
return None if None in p else ", ".join(p)
|
|
8674
|
-
if isinstance(n, ast.Subscript):
|
|
8675
|
-
v = chain(n.value)
|
|
8676
|
-
if v is None: return None
|
|
8677
|
-
last = v.split(".")[-1]
|
|
8678
|
-
if last == "Literal":
|
|
8679
|
-
s = lit(n.slice)
|
|
8680
|
-
return None if s is None else v + "[" + s + "]"
|
|
8681
|
-
if last == "Annotated" and isinstance(n.slice, ast.Tuple) and n.slice.elts:
|
|
8682
|
-
first = ann(n.slice.elts[0])
|
|
8683
|
-
return None if first is None else v + "[" + first + ", ...]"
|
|
8684
|
-
s = ann(n.slice)
|
|
8685
|
-
return None if s is None else v + "[" + s + "]"
|
|
8686
|
-
return None
|
|
8687
|
-
def a_or_dots(n):
|
|
8688
|
-
r = ann(n)
|
|
8689
|
-
return "..." if r is None else r
|
|
8690
|
-
def clean(n):
|
|
8691
|
-
r = ann(n)
|
|
8692
|
-
return "..." if r is None else r
|
|
8693
|
-
def arg(a, has_default):
|
|
8694
|
-
t = a.arg
|
|
8695
|
-
if a.annotation is not None: t += ": " + a_or_dots(a.annotation)
|
|
8696
|
-
if has_default: t += " = ..." if a.annotation is not None else "=..."
|
|
8697
|
-
return t
|
|
8698
|
-
def args(a):
|
|
8699
|
-
pos = list(a.posonlyargs) + list(a.args)
|
|
8700
|
-
first_default = len(pos) - len(a.defaults)
|
|
8701
|
-
out = []
|
|
8702
|
-
for i, x in enumerate(pos):
|
|
8703
|
-
out.append(arg(x, i >= first_default))
|
|
8704
|
-
if a.posonlyargs and i == len(a.posonlyargs) - 1: out.append("/")
|
|
8705
|
-
if a.vararg is not None: out.append("*" + arg(a.vararg, False))
|
|
8706
|
-
elif a.kwonlyargs: out.append("*")
|
|
8707
|
-
for x, d in zip(a.kwonlyargs, a.kw_defaults): out.append(arg(x, d is not None))
|
|
8708
|
-
if a.kwarg is not None: out.append("**" + arg(a.kwarg, False))
|
|
8709
|
-
return ", ".join(out)
|
|
8710
|
-
def fn(n, ind):
|
|
8711
|
-
r = [ind + deco(d) for d in n.decorator_list]
|
|
8712
|
-
h = ind + ("async def " if isinstance(n, ast.AsyncFunctionDef) else "def ") + n.name + "(" + args(n.args) + ")"
|
|
8713
|
-
if n.returns is not None: h += " -> " + a_or_dots(n.returns)
|
|
8714
|
-
return r + [h + ":"]
|
|
8715
|
-
def cls(n):
|
|
8716
|
-
r = [deco(d) for d in n.decorator_list]
|
|
8717
|
-
parts = [clean(b) for b in n.bases] + [(k.arg + "=" + clean(k.value)) if k.arg else "**..." for k in n.keywords]
|
|
8718
|
-
return r + ["class " + n.name + ("(" + ", ".join(parts) + ")" if parts else "") + ":"]
|
|
8719
|
-
tree = ast.parse(sys.stdin.read())
|
|
8720
|
-
out = []
|
|
8721
|
-
for n in tree.body:
|
|
8722
|
-
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and not n.name.startswith("_"):
|
|
8723
|
-
out += fn(n, "")
|
|
8724
|
-
elif isinstance(n, ast.ClassDef) and not n.name.startswith("_"):
|
|
8725
|
-
out += cls(n)
|
|
8726
|
-
for m in n.body:
|
|
8727
|
-
if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)) and (not m.name.startswith("_") or re.fullmatch(r"__\\w+__", m.name)):
|
|
8728
|
-
out += fn(m, " ")
|
|
8729
|
-
else:
|
|
8730
|
-
t = n.targets[0] if isinstance(n, ast.Assign) and len(n.targets) == 1 else (n.target if isinstance(n, ast.AnnAssign) else None)
|
|
8731
|
-
if isinstance(t, ast.Name) and re.fullmatch(r"[A-Z][A-Z0-9_]*", t.id): out.append(t.id + " = ...")
|
|
8732
|
-
print(json.dumps(out))
|
|
8733
|
-
`;
|
|
8939
|
+
const out2 = dir ? dir.split("/") : [];
|
|
8940
|
+
for (const seg of spec.split("/")) {
|
|
8941
|
+
if (seg === "" || seg === ".")
|
|
8942
|
+
continue;
|
|
8943
|
+
if (seg === "..")
|
|
8944
|
+
out2.pop();
|
|
8945
|
+
else
|
|
8946
|
+
out2.push(seg);
|
|
8947
|
+
}
|
|
8948
|
+
return out2.join("/");
|
|
8949
|
+
}, stemsOfTarget = (m) => {
|
|
8950
|
+
const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
|
|
8951
|
+
return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
|
|
8952
|
+
}, norm3 = (s) => s.replace(/\r\n?/g, `
|
|
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;
|
|
8734
8954
|
var init_wall_manifest = __esm(() => {
|
|
8955
|
+
init_python();
|
|
8956
|
+
init_ts_signatures();
|
|
8735
8957
|
TEST_PATH = /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.(py|go)$/;
|
|
8736
8958
|
TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
8737
8959
|
EXAMPLE_EXT = /\.(py|[cm]?[jt]sx?)$/;
|
|
8738
8960
|
GENERIC_STEMS = ["index", "init", "mod", "main"];
|
|
8739
|
-
MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
|
|
8740
|
-
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/;
|
|
8741
|
-
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$])/;
|
|
8742
8961
|
});
|
|
8743
8962
|
|
|
8744
8963
|
// ../../loki-ts/src/features/wall_manifest_wire.ts
|
|
8745
|
-
import { createHash as
|
|
8746
|
-
import { basename as basename9, dirname as
|
|
8964
|
+
import { createHash as createHash7 } from "crypto";
|
|
8965
|
+
import { basename as basename9, dirname as dirname9 } from "path";
|
|
8747
8966
|
function blobEntries(ls) {
|
|
8748
8967
|
const out2 = [];
|
|
8749
8968
|
for (const raw2 of ls.toString("latin1").split("\x00")) {
|
|
@@ -8773,7 +8992,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
|
|
|
8773
8992
|
return null;
|
|
8774
8993
|
const low = task.toLowerCase().replace(/\\/g, "/");
|
|
8775
8994
|
const token = (n2) => new RegExp(`(?<![A-Za-z0-9_])${n2.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`).test(low);
|
|
8776
|
-
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));
|
|
8777
8996
|
const matched = all.filter((e) => SOURCE.test(e.path) && (token(basename9(e.path)) || token(e.path) || token(basename9(e.path).replace(/\.[^.]*$/, "")) || dirModule(e)));
|
|
8778
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]);
|
|
8779
8998
|
const occ = new Map(matched.map((e) => [e, spans(e.path)]));
|
|
@@ -8802,7 +9021,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
|
|
|
8802
9021
|
if (at !== cat.length)
|
|
8803
9022
|
return null;
|
|
8804
9023
|
const text2 = buildWallManifest([...files, ...listed.map((e) => ({ path: e.path, content: "" }))], named.map((e) => e.path), matched.map((e) => e.path));
|
|
8805
|
-
return { text: text2, sha256:
|
|
9024
|
+
return { text: text2, sha256: createHash7("sha256").update(text2).digest("hex") };
|
|
8806
9025
|
} catch {
|
|
8807
9026
|
return null;
|
|
8808
9027
|
}
|
|
@@ -8834,12 +9053,12 @@ Example: test('name', () => { assert.strictEqual(fn(1), 2); }); Run: node --tes
|
|
|
8834
9053
|
});
|
|
8835
9054
|
|
|
8836
9055
|
// ../../loki-ts/src/engine10/cache.ts
|
|
8837
|
-
import { createHash as
|
|
8838
|
-
import { existsSync as
|
|
9056
|
+
import { createHash as createHash8 } from "crypto";
|
|
9057
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
|
|
8839
9058
|
import { resolve as resolve8 } from "path";
|
|
8840
9059
|
function repoKey(originUrl, repoDir) {
|
|
8841
9060
|
const basis = originUrl && originUrl.trim() !== "" ? originUrl : resolve8(repoDir);
|
|
8842
|
-
return
|
|
9061
|
+
return createHash8("sha256").update(basis).digest("hex");
|
|
8843
9062
|
}
|
|
8844
9063
|
function defaultCacheRoot() {
|
|
8845
9064
|
return resolve8(homeLokiDir(), "cache", "v10");
|
|
@@ -8848,10 +9067,10 @@ function repoCacheDir(key, cacheRoot = defaultCacheRoot()) {
|
|
|
8848
9067
|
return resolve8(cacheRoot, key);
|
|
8849
9068
|
}
|
|
8850
9069
|
function readJson3(path, isValid = (_v) => true) {
|
|
8851
|
-
if (!
|
|
9070
|
+
if (!existsSync17(path))
|
|
8852
9071
|
return null;
|
|
8853
9072
|
try {
|
|
8854
|
-
const parsed = JSON.parse(
|
|
9073
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
8855
9074
|
return isValid(parsed) ? parsed : null;
|
|
8856
9075
|
} catch {
|
|
8857
9076
|
return null;
|
|
@@ -8901,12 +9120,12 @@ function speedLikelyFiles(task, map, env = process.env) {
|
|
|
8901
9120
|
var init_lean_select = () => {};
|
|
8902
9121
|
|
|
8903
9122
|
// ../../loki-ts/src/util/claude_model.ts
|
|
8904
|
-
import { readFileSync as
|
|
9123
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
8905
9124
|
import { resolve as resolve9 } from "path";
|
|
8906
9125
|
function resolveClaudeModel(want) {
|
|
8907
9126
|
try {
|
|
8908
9127
|
const path = process.env["LOKI_MODEL_CATALOG"] || resolve9(REPO_ROOT, "providers/model_catalog.json");
|
|
8909
|
-
return JSON.parse(
|
|
9128
|
+
return JSON.parse(readFileSync21(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
|
|
8910
9129
|
} catch {
|
|
8911
9130
|
return want;
|
|
8912
9131
|
}
|
|
@@ -8966,8 +9185,8 @@ function routerMode(env = process.env) {
|
|
|
8966
9185
|
var routerEnabled = (env = process.env) => routerMode(env) === "on";
|
|
8967
9186
|
|
|
8968
9187
|
// ../../loki-ts/src/engine10/cost.ts
|
|
8969
|
-
import {
|
|
8970
|
-
import { join as
|
|
9188
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync22 } from "fs";
|
|
9189
|
+
import { join as join30 } from "path";
|
|
8971
9190
|
function routerTelemetryOn(env = process.env) {
|
|
8972
9191
|
return routerEnabled(env);
|
|
8973
9192
|
}
|
|
@@ -8975,7 +9194,7 @@ function num3(v) {
|
|
|
8975
9194
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
8976
9195
|
}
|
|
8977
9196
|
function resultCostPath(lokiRoot, iteration) {
|
|
8978
|
-
return
|
|
9197
|
+
return join30(lokiRoot, "metrics", `result-cost-${iteration}.json`);
|
|
8979
9198
|
}
|
|
8980
9199
|
function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
8981
9200
|
const out2 = {
|
|
@@ -9006,7 +9225,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9006
9225
|
const parsed = new Map;
|
|
9007
9226
|
for (const iter of iterations) {
|
|
9008
9227
|
try {
|
|
9009
|
-
parsed.set(iter, JSON.parse(
|
|
9228
|
+
parsed.set(iter, JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")));
|
|
9010
9229
|
} catch {}
|
|
9011
9230
|
}
|
|
9012
9231
|
const own = new Set(iterations);
|
|
@@ -9015,7 +9234,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9015
9234
|
if (own.has(iter))
|
|
9016
9235
|
continue;
|
|
9017
9236
|
try {
|
|
9018
|
-
lookup.push({ iter, rec: JSON.parse(
|
|
9237
|
+
lookup.push({ iter, rec: JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")) });
|
|
9019
9238
|
} catch {}
|
|
9020
9239
|
}
|
|
9021
9240
|
const verdicts = hooks.costRecords?.resumeVerdicts(lookup) ?? { ambiguous: [], separate: [] };
|
|
@@ -9110,34 +9329,36 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9110
9329
|
var UNMETERED = "cli-invoker-unmetered";
|
|
9111
9330
|
var init_cost = __esm(() => {
|
|
9112
9331
|
init_hooks();
|
|
9332
|
+
init_provider_cost();
|
|
9333
|
+
init_provider_cost();
|
|
9113
9334
|
});
|
|
9114
9335
|
|
|
9115
9336
|
// ../../loki-ts/src/util/atomic.ts
|
|
9116
9337
|
import {
|
|
9117
|
-
closeSync as
|
|
9118
|
-
fstatSync as
|
|
9338
|
+
closeSync as closeSync6,
|
|
9339
|
+
fstatSync as fstatSync5,
|
|
9119
9340
|
fsyncSync as fsyncSync2,
|
|
9120
|
-
lstatSync as
|
|
9121
|
-
mkdirSync as
|
|
9122
|
-
openSync as
|
|
9123
|
-
readSync as
|
|
9341
|
+
lstatSync as lstatSync6,
|
|
9342
|
+
mkdirSync as mkdirSync9,
|
|
9343
|
+
openSync as openSync6,
|
|
9344
|
+
readSync as readSync5,
|
|
9124
9345
|
renameSync as renameSync4,
|
|
9125
9346
|
rmSync as rmSync6,
|
|
9126
|
-
statSync as
|
|
9127
|
-
unlinkSync as
|
|
9128
|
-
writeFileSync as
|
|
9347
|
+
statSync as statSync14,
|
|
9348
|
+
unlinkSync as unlinkSync4,
|
|
9349
|
+
writeFileSync as writeFileSync12,
|
|
9129
9350
|
writeSync as writeSync3
|
|
9130
9351
|
} from "fs";
|
|
9131
|
-
import { dirname as
|
|
9352
|
+
import { dirname as dirname10 } from "path";
|
|
9132
9353
|
function durableWriteThenRename(target, body) {
|
|
9133
|
-
|
|
9354
|
+
mkdirSync9(dirname10(target), { recursive: true });
|
|
9134
9355
|
const tmp = tmpPathFor(target);
|
|
9135
|
-
const fd =
|
|
9356
|
+
const fd = openSync6(tmp, "w");
|
|
9136
9357
|
try {
|
|
9137
9358
|
writeSync3(fd, body);
|
|
9138
9359
|
fsyncSync2(fd);
|
|
9139
9360
|
} finally {
|
|
9140
|
-
|
|
9361
|
+
closeSync6(fd);
|
|
9141
9362
|
}
|
|
9142
9363
|
renameSync4(tmp, target);
|
|
9143
9364
|
fsyncParentDir(target);
|
|
@@ -9145,12 +9366,12 @@ function durableWriteThenRename(target, body) {
|
|
|
9145
9366
|
function fsyncParentDir(target) {
|
|
9146
9367
|
let dfd = null;
|
|
9147
9368
|
try {
|
|
9148
|
-
dfd =
|
|
9369
|
+
dfd = openSync6(dirname10(target), "r");
|
|
9149
9370
|
fsyncSync2(dfd);
|
|
9150
9371
|
} catch {} finally {
|
|
9151
9372
|
if (dfd !== null) {
|
|
9152
9373
|
try {
|
|
9153
|
-
|
|
9374
|
+
closeSync6(dfd);
|
|
9154
9375
|
} catch {}
|
|
9155
9376
|
}
|
|
9156
9377
|
}
|
|
@@ -9168,14 +9389,14 @@ var init_atomic = __esm(() => {
|
|
|
9168
9389
|
});
|
|
9169
9390
|
|
|
9170
9391
|
// ../../loki-ts/src/runner/budget.ts
|
|
9171
|
-
import { existsSync as
|
|
9172
|
-
import { dirname as
|
|
9392
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
|
|
9393
|
+
import { dirname as dirname11, join as join31, resolve as resolve10 } from "path";
|
|
9173
9394
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9174
9395
|
function loadDefaultMaxCostUsd() {
|
|
9175
|
-
const here =
|
|
9396
|
+
const here = dirname11(fileURLToPath2(import.meta.url));
|
|
9176
9397
|
for (const rel of [["..", "..", "data"], ["..", "data"]]) {
|
|
9177
9398
|
try {
|
|
9178
|
-
const j = JSON.parse(
|
|
9399
|
+
const j = JSON.parse(readFileSync23(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
|
|
9179
9400
|
if (typeof j.default_max_cost_usd === "number" && Number.isFinite(j.default_max_cost_usd) && j.default_max_cost_usd > 0)
|
|
9180
9401
|
return j.default_max_cost_usd;
|
|
9181
9402
|
} catch {}
|
|
@@ -9184,11 +9405,11 @@ function loadDefaultMaxCostUsd() {
|
|
|
9184
9405
|
}
|
|
9185
9406
|
function _loadPricing() {
|
|
9186
9407
|
try {
|
|
9187
|
-
const moduleDir =
|
|
9408
|
+
const moduleDir = dirname11(fileURLToPath2(import.meta.url));
|
|
9188
9409
|
const jsonPath = resolve10(moduleDir, "..", "..", "data", "model-pricing.json");
|
|
9189
|
-
if (!
|
|
9410
|
+
if (!existsSync18(jsonPath))
|
|
9190
9411
|
return _FALLBACK_PRICING;
|
|
9191
|
-
const raw2 = JSON.parse(
|
|
9412
|
+
const raw2 = JSON.parse(readFileSync23(jsonPath, "utf8"));
|
|
9192
9413
|
const pricing2 = raw2["pricing"];
|
|
9193
9414
|
if (!pricing2 || typeof pricing2 !== "object")
|
|
9194
9415
|
return _FALLBACK_PRICING;
|
|
@@ -9318,7 +9539,7 @@ var init_model_rank = __esm(() => {
|
|
|
9318
9539
|
});
|
|
9319
9540
|
|
|
9320
9541
|
// ../../loki-ts/src/engine10/sizing.ts
|
|
9321
|
-
import { existsSync as
|
|
9542
|
+
import { existsSync as existsSync19, readFileSync as readFileSync24 } from "fs";
|
|
9322
9543
|
import { basename as basename10 } from "path";
|
|
9323
9544
|
function namedFiles(task, map) {
|
|
9324
9545
|
return (map?.files ?? []).filter((f) => task.toLowerCase().includes(basename10(f).toLowerCase()));
|
|
@@ -9337,10 +9558,10 @@ function sizeTask(task, map, tests) {
|
|
|
9337
9558
|
return why.length ? { size: "normal", reasons: why } : { size: "small", reasons: [`task ${task.length} chars, names ${named} files`] };
|
|
9338
9559
|
}
|
|
9339
9560
|
function loadRepoMap(repomapRef) {
|
|
9340
|
-
if (!repomapRef || !
|
|
9561
|
+
if (!repomapRef || !existsSync19(repomapRef))
|
|
9341
9562
|
return null;
|
|
9342
9563
|
try {
|
|
9343
|
-
return JSON.parse(
|
|
9564
|
+
return JSON.parse(readFileSync24(repomapRef, "utf8"));
|
|
9344
9565
|
} catch {
|
|
9345
9566
|
return null;
|
|
9346
9567
|
}
|
|
@@ -9371,8 +9592,8 @@ var init_sizing = __esm(() => {
|
|
|
9371
9592
|
});
|
|
9372
9593
|
|
|
9373
9594
|
// ../../loki-ts/src/project_model/schema.ts
|
|
9374
|
-
import { existsSync as
|
|
9375
|
-
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";
|
|
9376
9597
|
function safeRel(p) {
|
|
9377
9598
|
const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
|
|
9378
9599
|
if (n2 === "" || isAbsolute3(n2) || n2 === ".." || n2.startsWith("../"))
|
|
@@ -9381,8 +9602,8 @@ function safeRel(p) {
|
|
|
9381
9602
|
}
|
|
9382
9603
|
function insideRepo(repoDir, rel) {
|
|
9383
9604
|
try {
|
|
9384
|
-
const real2 =
|
|
9385
|
-
const base =
|
|
9605
|
+
const real2 = realpathSync15(join33(repoDir, rel));
|
|
9606
|
+
const base = realpathSync15(repoDir);
|
|
9386
9607
|
return real2 === base || real2.startsWith(base + sep9);
|
|
9387
9608
|
} catch {
|
|
9388
9609
|
return false;
|
|
@@ -9394,8 +9615,8 @@ function checkCitation(repoDir, raw2) {
|
|
|
9394
9615
|
const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
|
|
9395
9616
|
if (rel === null)
|
|
9396
9617
|
return `citation "${raw2}" is not a repo-relative path`;
|
|
9397
|
-
const abs =
|
|
9398
|
-
if (!
|
|
9618
|
+
const abs = join33(repoDir, rel);
|
|
9619
|
+
if (!existsSync20(abs) || !statSync15(abs).isFile() || !insideRepo(repoDir, rel))
|
|
9399
9620
|
return `citation "${raw2}" names a file that does not exist`;
|
|
9400
9621
|
return null;
|
|
9401
9622
|
}
|
|
@@ -9406,7 +9627,7 @@ function checkFingerprint(repoDir, raw2) {
|
|
|
9406
9627
|
if (rel === null)
|
|
9407
9628
|
return `fingerprint "${raw2}" is not a repo-relative path`;
|
|
9408
9629
|
try {
|
|
9409
|
-
if (!
|
|
9630
|
+
if (!lstatSync7(join33(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
|
|
9410
9631
|
return `fingerprint "${raw2}" is not a regular file inside the repo`;
|
|
9411
9632
|
} catch {
|
|
9412
9633
|
return `fingerprint "${raw2}" names a file that does not exist`;
|
|
@@ -9434,8 +9655,8 @@ function checkDir(repoDir, v, where, errs) {
|
|
|
9434
9655
|
errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
|
|
9435
9656
|
return ".";
|
|
9436
9657
|
}
|
|
9437
|
-
const abs =
|
|
9438
|
-
if (!
|
|
9658
|
+
const abs = join33(repoDir, rel);
|
|
9659
|
+
if (!existsSync20(abs) || !statSync15(abs).isDirectory() || !insideRepo(repoDir, rel))
|
|
9439
9660
|
errs.push(`${where}: directory "${rel}" does not exist`);
|
|
9440
9661
|
return rel;
|
|
9441
9662
|
}
|
|
@@ -9537,9 +9758,9 @@ var init_schema = __esm(() => {
|
|
|
9537
9758
|
});
|
|
9538
9759
|
|
|
9539
9760
|
// ../../loki-ts/src/project_model/gather.ts
|
|
9540
|
-
import { createHash as
|
|
9541
|
-
import { closeSync as
|
|
9542
|
-
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";
|
|
9543
9764
|
function listShallow(repoDir) {
|
|
9544
9765
|
try {
|
|
9545
9766
|
const out2 = safeGit(repoDir, ["ls-files", "-z"], { maxBuffer: 256 * 1024 * 1024 });
|
|
@@ -9549,10 +9770,10 @@ function listShallow(repoDir) {
|
|
|
9549
9770
|
}
|
|
9550
9771
|
}
|
|
9551
9772
|
function shallowDirs(repoDir) {
|
|
9552
|
-
return [...new Set(listShallow(repoDir).map((p) =>
|
|
9773
|
+
return [...new Set(listShallow(repoDir).map((p) => dirname12(p)))].sort();
|
|
9553
9774
|
}
|
|
9554
9775
|
function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
9555
|
-
const h =
|
|
9776
|
+
const h = createHash9("sha256");
|
|
9556
9777
|
h.update(`${MODEL_REV}
|
|
9557
9778
|
`);
|
|
9558
9779
|
if (committedHash !== null)
|
|
@@ -9565,9 +9786,9 @@ function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
|
9565
9786
|
if (checkFingerprint(repoDir, f) !== null)
|
|
9566
9787
|
h.update("invalid");
|
|
9567
9788
|
else {
|
|
9568
|
-
const abs =
|
|
9569
|
-
const size =
|
|
9570
|
-
h.update(size <= GATHER_CAPS.maxHashBytes ?
|
|
9789
|
+
const abs = join34(repoDir, f);
|
|
9790
|
+
const size = lstatSync8(abs).size;
|
|
9791
|
+
h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync25(abs) : `too-large:${size}`);
|
|
9571
9792
|
}
|
|
9572
9793
|
} catch {
|
|
9573
9794
|
h.update("missing");
|
|
@@ -9592,7 +9813,7 @@ function committedModelHash(raw2) {
|
|
|
9592
9813
|
if (typeof raw2 !== "object" || raw2 === null || Array.isArray(raw2))
|
|
9593
9814
|
return null;
|
|
9594
9815
|
const { key: _key, expiresAt: _exp, ...rest } = raw2;
|
|
9595
|
-
return
|
|
9816
|
+
return createHash9("sha256").update(JSON.stringify(rest)).digest("hex");
|
|
9596
9817
|
}
|
|
9597
9818
|
var GATHER_CAPS, depthOf = (p) => p.split("/").length, MODEL_REV = "model-rev:2";
|
|
9598
9819
|
var init_gather = __esm(() => {
|
|
@@ -9610,14 +9831,14 @@ var init_gather = __esm(() => {
|
|
|
9610
9831
|
});
|
|
9611
9832
|
|
|
9612
9833
|
// ../../loki-ts/src/project_model/discover.ts
|
|
9613
|
-
import { existsSync as
|
|
9614
|
-
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";
|
|
9615
9836
|
function projectModelEnabled(env = process.env) {
|
|
9616
9837
|
return env["LOKI_E10_PROJECT_MODEL"] !== "0";
|
|
9617
9838
|
}
|
|
9618
9839
|
function loadCached(repoDir, now2 = Date.now()) {
|
|
9619
9840
|
try {
|
|
9620
|
-
return parseCached(repoDir, JSON.parse(
|
|
9841
|
+
return parseCached(repoDir, JSON.parse(readFileSync26(join35(repoDir, PROJECT_FILE), "utf8")), now2);
|
|
9621
9842
|
} catch {
|
|
9622
9843
|
return null;
|
|
9623
9844
|
}
|
|
@@ -9632,7 +9853,7 @@ function loadCommitted(repoDir) {
|
|
|
9632
9853
|
};
|
|
9633
9854
|
let raw2;
|
|
9634
9855
|
try {
|
|
9635
|
-
raw2 = JSON.parse(
|
|
9856
|
+
raw2 = JSON.parse(readFileSync26(join35(repoDir, PROJECT_FILE), "utf8"));
|
|
9636
9857
|
} catch (e) {
|
|
9637
9858
|
return ignore(`unreadable JSON (${e instanceof Error ? e.message : String(e)})`);
|
|
9638
9859
|
}
|
|
@@ -9705,7 +9926,7 @@ var clean2 = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/"
|
|
|
9705
9926
|
var init_api = () => {};
|
|
9706
9927
|
|
|
9707
9928
|
// ../../loki-ts/src/project_model/resolve.ts
|
|
9708
|
-
import { join as
|
|
9929
|
+
import { join as join36 } from "path";
|
|
9709
9930
|
function loadProjectApi(repoDir, env = process.env) {
|
|
9710
9931
|
if (!projectModelEnabled(env))
|
|
9711
9932
|
return null;
|
|
@@ -9727,7 +9948,7 @@ function siteFor(api, repoDir, file) {
|
|
|
9727
9948
|
const rel = api.relativeToRoot(file);
|
|
9728
9949
|
if (!pkg || pkg.root === "." || rel === null)
|
|
9729
9950
|
return null;
|
|
9730
|
-
return { pkg, root: pkg.root, cwd:
|
|
9951
|
+
return { pkg, root: pkg.root, cwd: join36(repoDir, pkg.root), file: rel };
|
|
9731
9952
|
}
|
|
9732
9953
|
function groupByPackage(api, repoDir, files) {
|
|
9733
9954
|
const out2 = new Map;
|
|
@@ -9760,9 +9981,9 @@ var init_scope = __esm(() => {
|
|
|
9760
9981
|
|
|
9761
9982
|
// ../../loki-ts/src/e10ext/assert_delta.ts
|
|
9762
9983
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
9763
|
-
import { mkdtempSync as mkdtempSync4, readFileSync as
|
|
9984
|
+
import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync27, rmSync as rmSync8 } from "fs";
|
|
9764
9985
|
import { tmpdir as tmpdir6 } from "os";
|
|
9765
|
-
import { join as
|
|
9986
|
+
import { join as join37 } from "path";
|
|
9766
9987
|
import { basename as basename11 } from "path";
|
|
9767
9988
|
function classifyAssertDelta(inp) {
|
|
9768
9989
|
const f = basename11(inp.path);
|
|
@@ -9772,7 +9993,7 @@ function classifyAssertDelta(inp) {
|
|
|
9772
9993
|
return WEAKENED;
|
|
9773
9994
|
let cwd = "";
|
|
9774
9995
|
try {
|
|
9775
|
-
cwd = mkdtempSync4(
|
|
9996
|
+
cwd = mkdtempSync4(join37(tmpdir6(), "loki-ad-"));
|
|
9776
9997
|
const raw2 = execFileSync5("python3", ["-I", "-c", PY], {
|
|
9777
9998
|
input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
|
|
9778
9999
|
cwd,
|
|
@@ -9803,7 +10024,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
|
|
|
9803
10024
|
return null;
|
|
9804
10025
|
try {
|
|
9805
10026
|
const show = (ref) => safeGit(repoDir, ["show", `${ref}:${path}`]);
|
|
9806
|
-
const head = headRef ? show(headRef) :
|
|
10027
|
+
const head = headRef ? show(headRef) : readFileSync27(join37(repoDir, path), "utf8");
|
|
9807
10028
|
const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
|
|
9808
10029
|
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;
|
|
9809
10030
|
} catch {
|
|
@@ -10027,7 +10248,7 @@ var init_runner_errors = __esm(() => {
|
|
|
10027
10248
|
});
|
|
10028
10249
|
|
|
10029
10250
|
// ../../loki-ts/src/engine10/testmap.ts
|
|
10030
|
-
import { basename as basename12, extname, join as
|
|
10251
|
+
import { basename as basename12, extname, join as join38, relative as relative3, sep as sep10 } from "path";
|
|
10031
10252
|
function isTestFile(rel) {
|
|
10032
10253
|
const name = basename12(rel);
|
|
10033
10254
|
return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
|
|
@@ -10057,8 +10278,8 @@ var init_testmap = __esm(() => {
|
|
|
10057
10278
|
});
|
|
10058
10279
|
|
|
10059
10280
|
// ../../loki-ts/src/util/check_result.ts
|
|
10060
|
-
import { mkdirSync as
|
|
10061
|
-
import { basename as basename13, join as
|
|
10281
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync5, writeFileSync as writeFileSync15 } from "fs";
|
|
10282
|
+
import { basename as basename13, join as join39 } from "path";
|
|
10062
10283
|
function plainTestEnv(base = process.env) {
|
|
10063
10284
|
const e = {};
|
|
10064
10285
|
for (const [k, v] of Object.entries(tokenFreeEnv(base)))
|
|
@@ -10324,10 +10545,10 @@ function classifyRisk(files, env = process.env) {
|
|
|
10324
10545
|
};
|
|
10325
10546
|
}
|
|
10326
10547
|
function changedFiles(repoDir, baseSha) {
|
|
10327
|
-
const
|
|
10548
|
+
const run2 = (args) => safeGit(repoDir, args).split(`
|
|
10328
10549
|
`).map((l) => l.trim()).filter(Boolean);
|
|
10329
|
-
const tracked =
|
|
10330
|
-
const untracked =
|
|
10550
|
+
const tracked = run2(["diff", "--name-only", baseSha]);
|
|
10551
|
+
const untracked = run2(["ls-files", "--others", "--exclude-standard"]);
|
|
10331
10552
|
return [...new Set([...tracked, ...untracked])].filter((f) => !f.startsWith(".loki/"));
|
|
10332
10553
|
}
|
|
10333
10554
|
function diffFiles(repoDir, baseSha) {
|
|
@@ -10350,7 +10571,7 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10350
10571
|
return f;
|
|
10351
10572
|
let n2 = 0;
|
|
10352
10573
|
try {
|
|
10353
|
-
n2 =
|
|
10574
|
+
n2 = readFileSync28(join39(repoDir, path), "utf8").split(`
|
|
10354
10575
|
`).length;
|
|
10355
10576
|
} catch {}
|
|
10356
10577
|
return { path, added: n2, deleted: 0, status: "added" };
|
|
@@ -10358,9 +10579,9 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10358
10579
|
}
|
|
10359
10580
|
function recordRisk(repoDir, a) {
|
|
10360
10581
|
try {
|
|
10361
|
-
const dir =
|
|
10362
|
-
|
|
10363
|
-
|
|
10582
|
+
const dir = join39(repoDir, ".loki", "state"), p = join39(dir, "risk-tier.json");
|
|
10583
|
+
mkdirSync12(dir, { recursive: true });
|
|
10584
|
+
writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
|
|
10364
10585
|
`);
|
|
10365
10586
|
renameSync5(`${p}.tmp`, p);
|
|
10366
10587
|
return p;
|
|
@@ -10401,12 +10622,12 @@ var init_check_result = __esm(() => {
|
|
|
10401
10622
|
|
|
10402
10623
|
// ../../loki-ts/src/runner/load_owner.ts
|
|
10403
10624
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10404
|
-
import { mkdtempSync as mkdtempSync5, realpathSync as
|
|
10625
|
+
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
|
|
10405
10626
|
import { tmpdir as tmpdir7 } from "os";
|
|
10406
|
-
import { basename as basename14, extname as extname2, join as
|
|
10627
|
+
import { basename as basename14, extname as extname2, join as join40 } from "path";
|
|
10407
10628
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
10408
10629
|
return changed.filter((f) => {
|
|
10409
|
-
if (out2.includes(f) || out2.includes(
|
|
10630
|
+
if (out2.includes(f) || out2.includes(join40(repoDir, f)))
|
|
10410
10631
|
return true;
|
|
10411
10632
|
const segs = f.split("/");
|
|
10412
10633
|
if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
|
|
@@ -10423,7 +10644,7 @@ function loadSignature(out2, roots) {
|
|
|
10423
10644
|
let o = out2;
|
|
10424
10645
|
for (const r of roots.flatMap((x) => {
|
|
10425
10646
|
try {
|
|
10426
|
-
return [x,
|
|
10647
|
+
return [x, realpathSync16(x)];
|
|
10427
10648
|
} catch {
|
|
10428
10649
|
return [x];
|
|
10429
10650
|
}
|
|
@@ -10517,7 +10738,7 @@ async function loadErrorIsHarnessOwned(i) {
|
|
|
10517
10738
|
return false;
|
|
10518
10739
|
if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
|
|
10519
10740
|
return false;
|
|
10520
|
-
const dir = mkdtempSync5(
|
|
10741
|
+
const dir = mkdtempSync5(join40(tmpdir7(), "e10-loadown-"));
|
|
10521
10742
|
const git3 = (args) => {
|
|
10522
10743
|
safeGit(i.repoDir, args, { stdio: "ignore" });
|
|
10523
10744
|
};
|
|
@@ -10549,7 +10770,7 @@ var init_load_owner = __esm(() => {
|
|
|
10549
10770
|
});
|
|
10550
10771
|
|
|
10551
10772
|
// ../../loki-ts/src/util/wall_owned.ts
|
|
10552
|
-
import { isAbsolute as isAbsolute4, join as
|
|
10773
|
+
import { isAbsolute as isAbsolute4, join as join41, relative as relative4 } from "path";
|
|
10553
10774
|
function failingLocations(raw2, cwd, repoDir) {
|
|
10554
10775
|
const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
|
|
10555
10776
|
`), files = new Set;
|
|
@@ -10581,7 +10802,7 @@ function failingLocations(raw2, cwd, repoDir) {
|
|
|
10581
10802
|
}
|
|
10582
10803
|
if (errors2 === 0 || located !== errors2)
|
|
10583
10804
|
return null;
|
|
10584
|
-
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f :
|
|
10805
|
+
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join41(cwd, f)));
|
|
10585
10806
|
}
|
|
10586
10807
|
function wallOwnedFailure(raw2, cwd, repoDir, wall) {
|
|
10587
10808
|
const locs = failingLocations(raw2, cwd, repoDir);
|
|
@@ -10610,11 +10831,11 @@ var init_state = __esm(() => {
|
|
|
10610
10831
|
});
|
|
10611
10832
|
|
|
10612
10833
|
// ../../loki-ts/src/runner/repo_profile.ts
|
|
10613
|
-
import { existsSync as
|
|
10614
|
-
import { basename as basename15, join as
|
|
10834
|
+
import { existsSync as existsSync22, readFileSync as readFileSync29, mkdirSync as mkdirSync13, readdirSync as readdirSync11, renameSync as renameSync6, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
|
|
10835
|
+
import { basename as basename15, join as join42, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
|
|
10615
10836
|
function loadProfile(repoDir) {
|
|
10616
10837
|
try {
|
|
10617
|
-
const p = JSON.parse(
|
|
10838
|
+
const p = JSON.parse(readFileSync29(commandProfilePath(repoDir), "utf8"));
|
|
10618
10839
|
return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
|
|
10619
10840
|
} catch {
|
|
10620
10841
|
return null;
|
|
@@ -10637,15 +10858,15 @@ function attribute(repoDir, baseSha, failing) {
|
|
|
10637
10858
|
}
|
|
10638
10859
|
function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
|
|
10639
10860
|
const bad = full.filter((c) => c.result === "fail"), none = { preExistingChecks: [], preExistingIds: [], introducedChecks: bad.map((c) => c.name), line: null };
|
|
10640
|
-
const
|
|
10861
|
+
const norm4 = (s) => s.replace(/\s+--silent\b/, "").trim(), p = loadProfile(repoDir);
|
|
10641
10862
|
if (!bad.length || !p || p.revision !== baseSha || !p.baseline.attributable || !p.baseline.failing_ids.length)
|
|
10642
10863
|
return none;
|
|
10643
|
-
if (bad.some((c) =>
|
|
10864
|
+
if (bad.some((c) => norm4(c.cmd) !== norm4(p.baseline.command ?? "")))
|
|
10644
10865
|
return { ...none, line: `baseline: ran "${p.baseline.command}", not the full suite command, ids not comparable, full suite failures not excused` };
|
|
10645
10866
|
const block = new Set(blocked);
|
|
10646
10867
|
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))) })));
|
|
10647
10868
|
}
|
|
10648
|
-
var commandProfilePath = (repoDir) =>
|
|
10869
|
+
var commandProfilePath = (repoDir) => join42(repoDir, ".loki", "state", "repo-profile.json");
|
|
10649
10870
|
var init_repo_profile = __esm(() => {
|
|
10650
10871
|
init_failures();
|
|
10651
10872
|
init_check_result();
|
|
@@ -10655,23 +10876,23 @@ var init_repo_profile = __esm(() => {
|
|
|
10655
10876
|
});
|
|
10656
10877
|
|
|
10657
10878
|
// ../../loki-ts/src/project_graph.ts
|
|
10658
|
-
import { createHash as
|
|
10879
|
+
import { createHash as createHash10 } from "crypto";
|
|
10659
10880
|
import {
|
|
10660
|
-
existsSync as
|
|
10881
|
+
existsSync as existsSync23,
|
|
10661
10882
|
readdirSync as readdirSync12,
|
|
10662
|
-
readFileSync as
|
|
10663
|
-
statSync as
|
|
10664
|
-
writeFileSync as
|
|
10665
|
-
mkdirSync as
|
|
10883
|
+
readFileSync as readFileSync30,
|
|
10884
|
+
statSync as statSync16,
|
|
10885
|
+
writeFileSync as writeFileSync17,
|
|
10886
|
+
mkdirSync as mkdirSync14
|
|
10666
10887
|
} from "fs";
|
|
10667
|
-
import { basename as basename16, dirname as
|
|
10888
|
+
import { basename as basename16, dirname as dirname13, join as join43, resolve as resolve12 } from "path";
|
|
10668
10889
|
function readManifest(path) {
|
|
10669
|
-
if (!
|
|
10890
|
+
if (!existsSync23(path)) {
|
|
10670
10891
|
return null;
|
|
10671
10892
|
}
|
|
10672
10893
|
let raw2;
|
|
10673
10894
|
try {
|
|
10674
|
-
raw2 =
|
|
10895
|
+
raw2 = readFileSync30(path, "utf8");
|
|
10675
10896
|
} catch {
|
|
10676
10897
|
return null;
|
|
10677
10898
|
}
|
|
@@ -10723,12 +10944,12 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10723
10944
|
for (const child of children) {
|
|
10724
10945
|
if (!matchPattern(name, child))
|
|
10725
10946
|
continue;
|
|
10726
|
-
const full =
|
|
10947
|
+
const full = join43(rootDir, child);
|
|
10727
10948
|
if (seen.has(full))
|
|
10728
10949
|
continue;
|
|
10729
10950
|
let st;
|
|
10730
10951
|
try {
|
|
10731
|
-
st =
|
|
10952
|
+
st = statSync16(full);
|
|
10732
10953
|
} catch {
|
|
10733
10954
|
continue;
|
|
10734
10955
|
}
|
|
@@ -10740,18 +10961,18 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10740
10961
|
}
|
|
10741
10962
|
if (declared.length === 0) {
|
|
10742
10963
|
for (const child of children) {
|
|
10743
|
-
const full =
|
|
10964
|
+
const full = join43(rootDir, child);
|
|
10744
10965
|
if (seen.has(full))
|
|
10745
10966
|
continue;
|
|
10746
10967
|
let st;
|
|
10747
10968
|
try {
|
|
10748
|
-
st =
|
|
10969
|
+
st = statSync16(full);
|
|
10749
10970
|
} catch {
|
|
10750
10971
|
continue;
|
|
10751
10972
|
}
|
|
10752
10973
|
if (!st.isDirectory())
|
|
10753
10974
|
continue;
|
|
10754
|
-
if (!
|
|
10975
|
+
if (!existsSync23(join43(full, ".loki", "app.json")))
|
|
10755
10976
|
continue;
|
|
10756
10977
|
seen.add(full);
|
|
10757
10978
|
out2.push(full);
|
|
@@ -10764,34 +10985,34 @@ function cacheKey(manifestPaths) {
|
|
|
10764
10985
|
for (const p of manifestPaths.sort()) {
|
|
10765
10986
|
let mtime = 0;
|
|
10766
10987
|
try {
|
|
10767
|
-
mtime =
|
|
10988
|
+
mtime = statSync16(p).mtimeMs;
|
|
10768
10989
|
} catch {}
|
|
10769
10990
|
parts.push(`${p}:${mtime}`);
|
|
10770
10991
|
}
|
|
10771
|
-
return
|
|
10992
|
+
return createHash10("sha256").update(parts.join("|")).digest("hex");
|
|
10772
10993
|
}
|
|
10773
10994
|
function writeCache(targetDir, key, result) {
|
|
10774
10995
|
try {
|
|
10775
|
-
const cacheDir =
|
|
10776
|
-
|
|
10777
|
-
const cachePath =
|
|
10778
|
-
|
|
10996
|
+
const cacheDir = join43(targetDir, ".loki", "state");
|
|
10997
|
+
mkdirSync14(cacheDir, { recursive: true });
|
|
10998
|
+
const cachePath = join43(cacheDir, "project-graph.json");
|
|
10999
|
+
writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
|
|
10779
11000
|
} catch {}
|
|
10780
11001
|
}
|
|
10781
11002
|
function discoverProjectGraph(targetDir) {
|
|
10782
11003
|
const absTarget = resolve12(targetDir);
|
|
10783
|
-
const targetManifest =
|
|
10784
|
-
const parentDir =
|
|
10785
|
-
const parentManifest =
|
|
11004
|
+
const targetManifest = join43(absTarget, ".loki", "app.json");
|
|
11005
|
+
const parentDir = dirname13(absTarget);
|
|
11006
|
+
const parentManifest = join43(parentDir, ".loki", "app.json");
|
|
10786
11007
|
const examined = [];
|
|
10787
11008
|
let chosenManifest = null;
|
|
10788
11009
|
let chosenRoot = "";
|
|
10789
|
-
if (
|
|
11010
|
+
if (existsSync23(parentManifest) && parentDir !== absTarget) {
|
|
10790
11011
|
examined.push(parentManifest);
|
|
10791
11012
|
chosenManifest = readManifest(parentManifest);
|
|
10792
11013
|
chosenRoot = parentDir;
|
|
10793
11014
|
}
|
|
10794
|
-
if (!chosenManifest &&
|
|
11015
|
+
if (!chosenManifest && existsSync23(targetManifest)) {
|
|
10795
11016
|
examined.push(targetManifest);
|
|
10796
11017
|
chosenManifest = readManifest(targetManifest);
|
|
10797
11018
|
chosenRoot = absTarget;
|
|
@@ -10802,8 +11023,8 @@ function discoverProjectGraph(targetDir) {
|
|
|
10802
11023
|
const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
|
|
10803
11024
|
const filtered = [];
|
|
10804
11025
|
for (const m of members) {
|
|
10805
|
-
const sibManifestPath =
|
|
10806
|
-
if (
|
|
11026
|
+
const sibManifestPath = join43(m, ".loki", "app.json");
|
|
11027
|
+
if (existsSync23(sibManifestPath)) {
|
|
10807
11028
|
examined.push(sibManifestPath);
|
|
10808
11029
|
const sibManifest = readManifest(sibManifestPath);
|
|
10809
11030
|
if (sibManifest && sibManifest.app_id !== chosenManifest.app_id) {
|
|
@@ -10826,77 +11047,6 @@ var init_project_graph = __esm(() => {
|
|
|
10826
11047
|
APP_ID_REGEX = /^[a-z0-9-]{3,40}$/;
|
|
10827
11048
|
});
|
|
10828
11049
|
|
|
10829
|
-
// ../../loki-ts/src/util/shell.ts
|
|
10830
|
-
async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
|
|
10831
|
-
const reader = stream.getReader();
|
|
10832
|
-
const decoder = new TextDecoder;
|
|
10833
|
-
let out2 = "";
|
|
10834
|
-
let total = 0;
|
|
10835
|
-
try {
|
|
10836
|
-
while (total < maxBytes) {
|
|
10837
|
-
const { done, value } = await reader.read();
|
|
10838
|
-
if (done)
|
|
10839
|
-
break;
|
|
10840
|
-
if (!value)
|
|
10841
|
-
continue;
|
|
10842
|
-
total += value.byteLength;
|
|
10843
|
-
if (total > maxBytes) {
|
|
10844
|
-
const keep = value.byteLength - (total - maxBytes);
|
|
10845
|
-
out2 += decoder.decode(value.subarray(0, keep), { stream: true });
|
|
10846
|
-
break;
|
|
10847
|
-
}
|
|
10848
|
-
out2 += decoder.decode(value, { stream: true });
|
|
10849
|
-
}
|
|
10850
|
-
out2 += decoder.decode();
|
|
10851
|
-
} finally {
|
|
10852
|
-
try {
|
|
10853
|
-
await reader.cancel();
|
|
10854
|
-
} catch {}
|
|
10855
|
-
reader.releaseLock();
|
|
10856
|
-
}
|
|
10857
|
-
return out2;
|
|
10858
|
-
}
|
|
10859
|
-
async function run(argv, opts = {}) {
|
|
10860
|
-
const proc = Bun.spawn({
|
|
10861
|
-
cmd: [...argv],
|
|
10862
|
-
stdout: "pipe",
|
|
10863
|
-
stderr: "pipe",
|
|
10864
|
-
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
10865
|
-
cwd: opts.cwd
|
|
10866
|
-
});
|
|
10867
|
-
let timer;
|
|
10868
|
-
let killTimer;
|
|
10869
|
-
if (opts.timeoutMs && opts.timeoutMs > 0) {
|
|
10870
|
-
timer = setTimeout(() => {
|
|
10871
|
-
try {
|
|
10872
|
-
proc.kill("SIGTERM");
|
|
10873
|
-
} catch {}
|
|
10874
|
-
killTimer = setTimeout(() => {
|
|
10875
|
-
try {
|
|
10876
|
-
proc.kill("SIGKILL");
|
|
10877
|
-
} catch {}
|
|
10878
|
-
}, 2000);
|
|
10879
|
-
}, opts.timeoutMs);
|
|
10880
|
-
}
|
|
10881
|
-
try {
|
|
10882
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
10883
|
-
readStreamCapped(proc.stdout),
|
|
10884
|
-
new Response(proc.stderr).text(),
|
|
10885
|
-
proc.exited
|
|
10886
|
-
]);
|
|
10887
|
-
return { stdout, stderr, exitCode };
|
|
10888
|
-
} finally {
|
|
10889
|
-
if (timer)
|
|
10890
|
-
clearTimeout(timer);
|
|
10891
|
-
if (killTimer)
|
|
10892
|
-
clearTimeout(killTimer);
|
|
10893
|
-
}
|
|
10894
|
-
}
|
|
10895
|
-
var MAX_STDOUT_BYTES;
|
|
10896
|
-
var init_shell = __esm(() => {
|
|
10897
|
-
MAX_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
10898
|
-
});
|
|
10899
|
-
|
|
10900
11050
|
// ../../loki-ts/src/e10ext/budget_cap.ts
|
|
10901
11051
|
var DEFAULT_MAX_COST_USD;
|
|
10902
11052
|
var init_budget_cap = __esm(() => {
|
|
@@ -10908,71 +11058,6 @@ var init_budget_cap = __esm(() => {
|
|
|
10908
11058
|
// ../../loki-ts/src/runner/retry_class.ts
|
|
10909
11059
|
var init_retry_class = () => {};
|
|
10910
11060
|
|
|
10911
|
-
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
10912
|
-
import { closeSync as closeSync7, constants as constants2, fstatSync as fstatSync5, lstatSync as lstatSync8, openSync as openSync7, readSync as readSync6, realpathSync as realpathSync16 } from "fs";
|
|
10913
|
-
function readBounded(path) {
|
|
10914
|
-
const l = lstatSync8(path);
|
|
10915
|
-
if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
|
|
10916
|
-
return null;
|
|
10917
|
-
const fd = openSync7(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
10918
|
-
try {
|
|
10919
|
-
const st = fstatSync5(fd);
|
|
10920
|
-
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
10921
|
-
return null;
|
|
10922
|
-
const buf = Buffer.alloc(st.size + 1);
|
|
10923
|
-
let n2 = 0;
|
|
10924
|
-
for (;; ) {
|
|
10925
|
-
const r = readSync6(fd, buf, n2, buf.length - n2, null);
|
|
10926
|
-
if (r <= 0)
|
|
10927
|
-
break;
|
|
10928
|
-
n2 += r;
|
|
10929
|
-
if (n2 >= buf.length)
|
|
10930
|
-
return null;
|
|
10931
|
-
}
|
|
10932
|
-
return buf.subarray(0, n2).toString("utf8");
|
|
10933
|
-
} finally {
|
|
10934
|
-
closeSync7(fd);
|
|
10935
|
-
}
|
|
10936
|
-
}
|
|
10937
|
-
function parseText(txt) {
|
|
10938
|
-
try {
|
|
10939
|
-
const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
|
|
10940
|
-
if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
|
|
10941
|
-
return null;
|
|
10942
|
-
return { id: j.id, writeSet: ws, pack, tokenBudget: tb };
|
|
10943
|
-
} catch {
|
|
10944
|
-
return null;
|
|
10945
|
-
}
|
|
10946
|
-
}
|
|
10947
|
-
function parseSpec(path) {
|
|
10948
|
-
try {
|
|
10949
|
-
const txt = readBounded(path);
|
|
10950
|
-
return txt === null ? null : parseText(txt);
|
|
10951
|
-
} catch {
|
|
10952
|
-
return null;
|
|
10953
|
-
}
|
|
10954
|
-
}
|
|
10955
|
-
function unitSpec(env = process.env) {
|
|
10956
|
-
const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
|
|
10957
|
-
if (env["LOKI_SPEED"] === "0" || !path)
|
|
10958
|
-
return null;
|
|
10959
|
-
if (frozen !== undefined)
|
|
10960
|
-
return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
|
|
10961
|
-
if (!memo.has(path))
|
|
10962
|
-
memo.set(path, parseSpec(path));
|
|
10963
|
-
return memo.get(path) ?? null;
|
|
10964
|
-
}
|
|
10965
|
-
function unitFlags(staged, env = process.env, pre = {}) {
|
|
10966
|
-
const s = unitSpec(env);
|
|
10967
|
-
return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
|
|
10968
|
-
}
|
|
10969
|
-
var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm3 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm3) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
|
|
10970
|
-
var init_unit_mode = __esm(() => {
|
|
10971
|
-
MAX_SPEC_BYTES = 1024 * 1024;
|
|
10972
|
-
CTRL = /[\x00-\x1f\x7f]/;
|
|
10973
|
-
memo = new Map;
|
|
10974
|
-
});
|
|
10975
|
-
|
|
10976
11061
|
// ../../loki-ts/src/e10ext/repomemory.ts
|
|
10977
11062
|
var init_repomemory = __esm(() => {
|
|
10978
11063
|
init_cache();
|
|
@@ -11011,14 +11096,15 @@ var init_steer = __esm(() => {
|
|
|
11011
11096
|
});
|
|
11012
11097
|
|
|
11013
11098
|
// ../../loki-ts/src/util/pr_lessons.ts
|
|
11014
|
-
import { existsSync as
|
|
11015
|
-
import { dirname as
|
|
11099
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync31 } from "fs";
|
|
11100
|
+
import { dirname as dirname14, join as join44 } from "path";
|
|
11101
|
+
import { createHash as createHash11 } from "crypto";
|
|
11016
11102
|
function loadLessons(repoDir) {
|
|
11017
11103
|
const p = lessonsPath(repoDir);
|
|
11018
|
-
if (!
|
|
11104
|
+
if (!existsSync24(p))
|
|
11019
11105
|
return [];
|
|
11020
11106
|
try {
|
|
11021
|
-
const j = JSON.parse(
|
|
11107
|
+
const j = JSON.parse(readFileSync31(p, "utf-8"));
|
|
11022
11108
|
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
11023
11109
|
} catch {
|
|
11024
11110
|
return [];
|
|
@@ -11026,9 +11112,28 @@ function loadLessons(repoDir) {
|
|
|
11026
11112
|
}
|
|
11027
11113
|
function save(repoDir, lessons) {
|
|
11028
11114
|
const p = lessonsPath(repoDir);
|
|
11029
|
-
|
|
11115
|
+
mkdirSync15(dirname14(p), { recursive: true });
|
|
11030
11116
|
atomicWriteJson(p, { version: 2, lessons });
|
|
11031
11117
|
}
|
|
11118
|
+
function appendReceiptFact(repoDir, f, now2 = () => new Date) {
|
|
11119
|
+
const id = `rcp-${createHash11("sha256").update(f.runId).digest("hex").slice(0, 12)}`;
|
|
11120
|
+
const lessons = loadLessons(repoDir);
|
|
11121
|
+
if (lessons.some((l) => l.id === id))
|
|
11122
|
+
return false;
|
|
11123
|
+
const cited = f.receiptSha256 !== null;
|
|
11124
|
+
const title = f.title.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
11125
|
+
lessons.push({
|
|
11126
|
+
id,
|
|
11127
|
+
kind: "receipt",
|
|
11128
|
+
text: `Run ${f.runId} sealed ${f.verdict}: ${title}`,
|
|
11129
|
+
source: { pr: `run:${f.runId}`, pr_url: "", comment_url: "", kind: "receipt", author: "loki" },
|
|
11130
|
+
learned_at: now2().toISOString(),
|
|
11131
|
+
uses: [],
|
|
11132
|
+
justified_by: { run_id: f.runId, receipt_sha256: f.receiptSha256, verdict: cited ? f.verdict : "unverified" }
|
|
11133
|
+
});
|
|
11134
|
+
save(repoDir, lessons);
|
|
11135
|
+
return true;
|
|
11136
|
+
}
|
|
11032
11137
|
function recordRunVerdict(repoDir, runId, verdict) {
|
|
11033
11138
|
const lessons = loadLessons(repoDir);
|
|
11034
11139
|
let dirty = false;
|
|
@@ -11041,9 +11146,10 @@ function recordRunVerdict(repoDir, runId, verdict) {
|
|
|
11041
11146
|
if (dirty)
|
|
11042
11147
|
save(repoDir, lessons);
|
|
11043
11148
|
}
|
|
11044
|
-
var lessonsPath = (repoDir) =>
|
|
11149
|
+
var lessonsPath = (repoDir) => join44(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
|
|
11045
11150
|
var init_pr_lessons = __esm(() => {
|
|
11046
11151
|
init_atomic();
|
|
11152
|
+
KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
|
|
11047
11153
|
});
|
|
11048
11154
|
|
|
11049
11155
|
// ../../loki-ts/src/runner/model_downgrades.ts
|
|
@@ -11056,19 +11162,19 @@ var init_model_downgrades = __esm(() => {
|
|
|
11056
11162
|
var init_mcp_config = () => {};
|
|
11057
11163
|
|
|
11058
11164
|
// ../../loki-ts/src/providers/claude_flags.ts
|
|
11059
|
-
import { existsSync as
|
|
11060
|
-
import { resolve as resolvePath, dirname as
|
|
11165
|
+
import { existsSync as existsSync25, readFileSync as readFileSync32 } from "fs";
|
|
11166
|
+
import { resolve as resolvePath, dirname as dirname15 } from "path";
|
|
11061
11167
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11062
11168
|
function designArchetypesText() {
|
|
11063
11169
|
try {
|
|
11064
|
-
const moduleDir =
|
|
11170
|
+
const moduleDir = dirname15(fileURLToPath3(import.meta.url));
|
|
11065
11171
|
for (const rel of [
|
|
11066
11172
|
["..", "..", "..", "references", "design-archetypes.md"],
|
|
11067
11173
|
["..", "..", "references", "design-archetypes.md"]
|
|
11068
11174
|
]) {
|
|
11069
11175
|
const p = resolvePath(moduleDir, ...rel);
|
|
11070
|
-
if (
|
|
11071
|
-
return
|
|
11176
|
+
if (existsSync25(p))
|
|
11177
|
+
return readFileSync32(p, "utf8");
|
|
11072
11178
|
}
|
|
11073
11179
|
} catch {}
|
|
11074
11180
|
return "";
|
|
@@ -11146,14 +11252,14 @@ var init_providers = __esm(() => {
|
|
|
11146
11252
|
});
|
|
11147
11253
|
|
|
11148
11254
|
// ../../loki-ts/src/runner/router/route_record.ts
|
|
11149
|
-
import { existsSync as
|
|
11150
|
-
import { join as
|
|
11255
|
+
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync33, writeFileSync as writeFileSync18 } from "fs";
|
|
11256
|
+
import { join as join45 } from "path";
|
|
11151
11257
|
function loadRouteRecord(runDir) {
|
|
11152
11258
|
try {
|
|
11153
|
-
const p =
|
|
11154
|
-
if (!
|
|
11259
|
+
const p = join45(runDir, ROUTE_RECORD_FILE);
|
|
11260
|
+
if (!existsSync26(p) || !lstatSync9(p).isFile())
|
|
11155
11261
|
return;
|
|
11156
|
-
const v = JSON.parse(
|
|
11262
|
+
const v = JSON.parse(readFileSync33(p, "utf8"));
|
|
11157
11263
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
11158
11264
|
} catch {
|
|
11159
11265
|
return;
|
|
@@ -11163,7 +11269,7 @@ var ROUTE_RECORD_FILE = "route.json";
|
|
|
11163
11269
|
var init_route_record = () => {};
|
|
11164
11270
|
|
|
11165
11271
|
// ../../loki-ts/src/runner/router/history_store.ts
|
|
11166
|
-
import { existsSync as
|
|
11272
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync34, renameSync as renameSync7, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
|
|
11167
11273
|
import { resolve as resolve13 } from "path";
|
|
11168
11274
|
function validRun(v) {
|
|
11169
11275
|
if (typeof v !== "object" || v === null)
|
|
@@ -11176,10 +11282,10 @@ function historyPath(key, cacheRoot) {
|
|
|
11176
11282
|
}
|
|
11177
11283
|
function readRunHistory(key, cacheRoot) {
|
|
11178
11284
|
const path = historyPath(key, cacheRoot);
|
|
11179
|
-
if (!
|
|
11285
|
+
if (!existsSync27(path))
|
|
11180
11286
|
return [];
|
|
11181
11287
|
try {
|
|
11182
|
-
const parsed = JSON.parse(
|
|
11288
|
+
const parsed = JSON.parse(readFileSync34(path, "utf8"));
|
|
11183
11289
|
const runs2 = parsed?.runs;
|
|
11184
11290
|
return Array.isArray(runs2) ? runs2.filter(validRun) : [];
|
|
11185
11291
|
} catch {
|
|
@@ -11189,11 +11295,11 @@ function readRunHistory(key, cacheRoot) {
|
|
|
11189
11295
|
function appendRunOutcome(key, outcome, cacheRoot) {
|
|
11190
11296
|
try {
|
|
11191
11297
|
const runs2 = [...readRunHistory(key, cacheRoot), outcome].slice(-HISTORY_CAP);
|
|
11192
|
-
|
|
11298
|
+
mkdirSync17(repoCacheDir(key, cacheRoot), { recursive: true });
|
|
11193
11299
|
const target = historyPath(key, cacheRoot);
|
|
11194
11300
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
11195
11301
|
try {
|
|
11196
|
-
|
|
11302
|
+
writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
|
|
11197
11303
|
renameSync7(tmp, target);
|
|
11198
11304
|
} catch (e) {
|
|
11199
11305
|
rmSync11(tmp, { force: true, recursive: false });
|
|
@@ -11213,7 +11319,7 @@ var init_history_store = __esm(() => {
|
|
|
11213
11319
|
});
|
|
11214
11320
|
|
|
11215
11321
|
// ../../loki-ts/src/runner/router/history.ts
|
|
11216
|
-
import { dirname as
|
|
11322
|
+
import { dirname as dirname16, join as join46, resolve as resolve14 } from "path";
|
|
11217
11323
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11218
11324
|
function shapeKey(model) {
|
|
11219
11325
|
if (!model || model.status !== "ok")
|
|
@@ -11242,7 +11348,7 @@ var init_history = __esm(() => {
|
|
|
11242
11348
|
init_discover2();
|
|
11243
11349
|
init_gather();
|
|
11244
11350
|
init_history_store();
|
|
11245
|
-
SHAPE_DEFAULTS_FILE = resolve14(
|
|
11351
|
+
SHAPE_DEFAULTS_FILE = resolve14(dirname16(fileURLToPath4(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
|
|
11246
11352
|
});
|
|
11247
11353
|
|
|
11248
11354
|
// ../../loki-ts/src/runner/router/plan_route.ts
|
|
@@ -11373,7 +11479,7 @@ var init_worker = __esm(() => {
|
|
|
11373
11479
|
});
|
|
11374
11480
|
|
|
11375
11481
|
// ../../loki-ts/src/project_model/package_suite.ts
|
|
11376
|
-
import { join as
|
|
11482
|
+
import { join as join47 } from "path";
|
|
11377
11483
|
async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
11378
11484
|
const api = loadProjectApi(repoDir);
|
|
11379
11485
|
if (!isMultiRoot(api))
|
|
@@ -11384,7 +11490,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11384
11490
|
for (const pkg of withTests) {
|
|
11385
11491
|
const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
|
|
11386
11492
|
const started = Date.now();
|
|
11387
|
-
const r = await run(["bash", "-c", tc.cmd], { cwd:
|
|
11493
|
+
const r = await run(["bash", "-c", tc.cmd], { cwd: join47(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11388
11494
|
const duration_s = (Date.now() - started) / 1000;
|
|
11389
11495
|
if (signal.aborted) {
|
|
11390
11496
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
@@ -11393,7 +11499,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11393
11499
|
}
|
|
11394
11500
|
const out2 = `${r.stdout}
|
|
11395
11501
|
${r.stderr}`;
|
|
11396
|
-
const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd:
|
|
11502
|
+
const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join47(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11397
11503
|
if (lr) {
|
|
11398
11504
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
11399
11505
|
notProven.add(`${lr} (${name}; harness-owned)`);
|
|
@@ -11426,8 +11532,8 @@ __export(exports_deep, {
|
|
|
11426
11532
|
DEFAULT_SECRET_SCAN_SH: () => DEFAULT_SECRET_SCAN_SH,
|
|
11427
11533
|
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
|
|
11428
11534
|
});
|
|
11429
|
-
import { existsSync as
|
|
11430
|
-
import { join as
|
|
11535
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
|
|
11536
|
+
import { join as join48 } from "path";
|
|
11431
11537
|
async function runFullSuite(ctx, signal, opts, checks, notProven) {
|
|
11432
11538
|
if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
|
|
11433
11539
|
return;
|
|
@@ -11513,7 +11619,7 @@ async function runCouncilCheck(ctx, changed, baseSha, signal, opts, checks, notP
|
|
|
11513
11619
|
async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
11514
11620
|
const scriptPath = opts.secretScanShPath ?? DEFAULT_SECRET_SCAN_SH;
|
|
11515
11621
|
const name = "security scan";
|
|
11516
|
-
if (!
|
|
11622
|
+
if (!existsSync28(scriptPath)) {
|
|
11517
11623
|
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: 0 });
|
|
11518
11624
|
notProven.add(`not run: ${name} (secret-scan.sh not found)`);
|
|
11519
11625
|
return;
|
|
@@ -11524,7 +11630,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
|
11524
11630
|
}
|
|
11525
11631
|
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"';
|
|
11526
11632
|
const started = Date.now();
|
|
11527
|
-
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) =>
|
|
11633
|
+
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join48(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
|
|
11528
11634
|
const durationS = (Date.now() - started) / 1000;
|
|
11529
11635
|
if (r.exitCode === 3) {
|
|
11530
11636
|
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
|
|
@@ -11564,9 +11670,9 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
|
|
|
11564
11670
|
const prUrl = ctx.outputs().pr?.pr_url;
|
|
11565
11671
|
const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
|
|
11566
11672
|
if (prNumber) {
|
|
11567
|
-
|
|
11568
|
-
const bodyFile =
|
|
11569
|
-
|
|
11673
|
+
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11674
|
+
const bodyFile = join48(ctx.runDir, "deep-comment.md");
|
|
11675
|
+
writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
|
|
11570
11676
|
const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
11571
11677
|
if (commentResult.exitCode !== 0)
|
|
11572
11678
|
out2.push(`deep comment not posted (engine10-push.sh exit ${commentResult.exitCode})`);
|
|
@@ -11618,8 +11724,8 @@ async function runDeep(ctx, signal, opts = {}) {
|
|
|
11618
11724
|
const addendumSha256 = sha2562(canonicalJson(addendumBody));
|
|
11619
11725
|
const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
|
|
11620
11726
|
const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
11621
|
-
|
|
11622
|
-
|
|
11727
|
+
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11728
|
+
writeFileSync20(join48(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
|
|
11623
11729
|
`);
|
|
11624
11730
|
const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
|
|
11625
11731
|
const allNotProven = [...notProvenList, ...pushNotProven];
|
|
@@ -11662,9 +11768,9 @@ var init_deep = __esm(() => {
|
|
|
11662
11768
|
});
|
|
11663
11769
|
|
|
11664
11770
|
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
11665
|
-
import { existsSync as
|
|
11771
|
+
import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
|
|
11666
11772
|
import { tmpdir as tmpdir8 } from "os";
|
|
11667
|
-
import { dirname as
|
|
11773
|
+
import { dirname as dirname17, isAbsolute as isAbsolute6, join as join49, relative as relative5 } from "path";
|
|
11668
11774
|
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
11669
11775
|
const inRepo = (p) => {
|
|
11670
11776
|
try {
|
|
@@ -11675,7 +11781,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
|
11675
11781
|
}
|
|
11676
11782
|
};
|
|
11677
11783
|
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
11678
|
-
const project = [
|
|
11784
|
+
const project = [join49(repoDir, ".venv", "bin", bin), join49(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join49(venvEnv, "bin", bin)] : []].find(existsSync29);
|
|
11679
11785
|
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
11680
11786
|
}
|
|
11681
11787
|
function runnerCmd(t, repoDir) {
|
|
@@ -11695,7 +11801,7 @@ function runnerCmd(t, repoDir) {
|
|
|
11695
11801
|
case "npm":
|
|
11696
11802
|
return ["npm", ["test", "--silent"]];
|
|
11697
11803
|
case "go":
|
|
11698
|
-
return ["go", ["test", "-json", `./${
|
|
11804
|
+
return ["go", ["test", "-json", `./${dirname17(t.path)}`]];
|
|
11699
11805
|
case "cargo":
|
|
11700
11806
|
return ["cargo", ["test"]];
|
|
11701
11807
|
}
|
|
@@ -11793,7 +11899,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11793
11899
|
const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
|
|
11794
11900
|
if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
|
|
11795
11901
|
return out2;
|
|
11796
|
-
const dir = mkdtempSync6(
|
|
11902
|
+
const dir = mkdtempSync6(join49(tmpdir8(), "e10-base-"));
|
|
11797
11903
|
const git3 = (args) => {
|
|
11798
11904
|
safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
|
|
11799
11905
|
};
|
|
@@ -11828,7 +11934,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11828
11934
|
return out2;
|
|
11829
11935
|
}
|
|
11830
11936
|
function isLokiModeRepo(repoDir) {
|
|
11831
|
-
return
|
|
11937
|
+
return existsSync29(join49(repoDir, "scripts", "select-tests.sh"));
|
|
11832
11938
|
}
|
|
11833
11939
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
11834
11940
|
const { api, ...runOpts } = opts;
|
|
@@ -11845,9 +11951,9 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
11845
11951
|
}
|
|
11846
11952
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
11847
11953
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
11848
|
-
if (
|
|
11954
|
+
if (existsSync29(join49(g.cwd, "tsconfig.json")))
|
|
11849
11955
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
11850
|
-
if (ESLINT_CONFIGS.some((f) =>
|
|
11956
|
+
if (ESLINT_CONFIGS.some((f) => existsSync29(join49(g.cwd, f))))
|
|
11851
11957
|
await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
11852
11958
|
}
|
|
11853
11959
|
}
|
|
@@ -11979,8 +12085,8 @@ var init_verify = __esm(() => {
|
|
|
11979
12085
|
});
|
|
11980
12086
|
|
|
11981
12087
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
11982
|
-
import { existsSync as
|
|
11983
|
-
import { dirname as
|
|
12088
|
+
import { existsSync as existsSync30, readdirSync as readdirSync13, readFileSync as readFileSync35 } from "fs";
|
|
12089
|
+
import { dirname as dirname18, join as join50 } from "path";
|
|
11984
12090
|
function parseJsonc(text2) {
|
|
11985
12091
|
let out2 = "", i = 0;
|
|
11986
12092
|
while (i < text2.length) {
|
|
@@ -12015,8 +12121,8 @@ function moduleOf(file, depth = 0) {
|
|
|
12015
12121
|
const ext = typeof j.extends === "string" ? j.extends : null;
|
|
12016
12122
|
if (!ext || !ext.startsWith("."))
|
|
12017
12123
|
return null;
|
|
12018
|
-
const next =
|
|
12019
|
-
return
|
|
12124
|
+
const next = join50(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
12125
|
+
return existsSync30(next) ? moduleOf(next, depth + 1) : null;
|
|
12020
12126
|
}
|
|
12021
12127
|
function readPackageConventions(pkgDir, runner = null) {
|
|
12022
12128
|
let names = [];
|
|
@@ -12027,13 +12133,13 @@ function readPackageConventions(pkgDir, runner = null) {
|
|
|
12027
12133
|
}
|
|
12028
12134
|
let packageType = null, tsModule = null;
|
|
12029
12135
|
for (const n2 of names) {
|
|
12030
|
-
const j = readJson4(
|
|
12136
|
+
const j = readJson4(join50(pkgDir, n2));
|
|
12031
12137
|
if (!j)
|
|
12032
12138
|
continue;
|
|
12033
12139
|
if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
|
|
12034
12140
|
packageType = j.type;
|
|
12035
12141
|
if (tsModule === null && j.compilerOptions !== undefined)
|
|
12036
|
-
tsModule = moduleOf(
|
|
12142
|
+
tsModule = moduleOf(join50(pkgDir, n2));
|
|
12037
12143
|
}
|
|
12038
12144
|
if (packageType === null && tsModule === null)
|
|
12039
12145
|
return null;
|
|
@@ -12055,7 +12161,7 @@ function conventionViolation(c, fileName, content) {
|
|
|
12055
12161
|
}
|
|
12056
12162
|
var readJson4 = (p) => {
|
|
12057
12163
|
try {
|
|
12058
|
-
const v = parseJsonc(
|
|
12164
|
+
const v = parseJsonc(readFileSync35(p, "utf8"));
|
|
12059
12165
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12060
12166
|
} catch {
|
|
12061
12167
|
return null;
|
|
@@ -12065,15 +12171,15 @@ var init_conventions = () => {};
|
|
|
12065
12171
|
|
|
12066
12172
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
12067
12173
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12068
|
-
import { existsSync as
|
|
12174
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync36, readdirSync as readdirSync14, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync21 } from "fs";
|
|
12069
12175
|
import { tmpdir as tmpdir9 } from "os";
|
|
12070
|
-
import { dirname as
|
|
12176
|
+
import { dirname as dirname19, isAbsolute as isAbsolute7, join as join51, relative as relative6 } from "path";
|
|
12071
12177
|
function pytestCollectionIsRed(output, repoDir) {
|
|
12072
12178
|
if (/ModuleNotFoundError/.test(output))
|
|
12073
12179
|
return false;
|
|
12074
12180
|
const real2 = (p) => {
|
|
12075
12181
|
try {
|
|
12076
|
-
return realpathSync18(isAbsolute7(p) ? p :
|
|
12182
|
+
return realpathSync18(isAbsolute7(p) ? p : join51(repoDir, p));
|
|
12077
12183
|
} catch {
|
|
12078
12184
|
return null;
|
|
12079
12185
|
}
|
|
@@ -12110,7 +12216,7 @@ function pytestCollectionIsRed(output, repoDir) {
|
|
|
12110
12216
|
}
|
|
12111
12217
|
function moduleUnderRepo(repoDir, name) {
|
|
12112
12218
|
const rel = name.replace(/\./g, "/");
|
|
12113
|
-
return
|
|
12219
|
+
return existsSync31(join51(repoDir, `${rel}.py`)) || existsSync31(join51(repoDir, rel, "__init__.py"));
|
|
12114
12220
|
}
|
|
12115
12221
|
function pytestExit1IsRed(output) {
|
|
12116
12222
|
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
@@ -12187,13 +12293,16 @@ ${r.stderr ?? ""}`), repoDir, interpreter);
|
|
|
12187
12293
|
return { pass, fail, not_run };
|
|
12188
12294
|
}
|
|
12189
12295
|
}
|
|
12190
|
-
function buildWallBrief(task, repomapText = "", runners = [], hasManifest = false, conventions = "") {
|
|
12296
|
+
function buildWallBrief(task, repomapText = "", runners = [], hasManifest = false, conventions = "", acceptance = []) {
|
|
12191
12297
|
return withStagePrefix([
|
|
12192
12298
|
"You are the Loki 10 Wall author.",
|
|
12193
12299
|
`You cannot see the repository. This directory holds only task.md and repomap.txt${hasManifest ? " and wall_manifest.txt (signatures, runner config, test layout and test-style examples)" : ""}.`,
|
|
12194
12300
|
...repomapText ? [`Repository paths (repomap.txt):
|
|
12195
12301
|
${repomapText}`] : [],
|
|
12196
12302
|
...taskBlock(task),
|
|
12303
|
+
...acceptance.length ? [`Acceptance criteria frozen for this unit (each must be covered by a test):
|
|
12304
|
+
${acceptance.map((a) => `Acceptance: ${a}`).join(`
|
|
12305
|
+
`)}`] : [],
|
|
12197
12306
|
"Write behavioral acceptance tests that prove the task is done. A test that errors on import, uses another framework's globals, or fails for a reason unrelated to the task is discarded.",
|
|
12198
12307
|
`Test runner: ${runners.map((r) => RUNNER_HINT[r]).find(Boolean) ?? "the framework named in repomap.txt"}`,
|
|
12199
12308
|
...conventions ? [conventions] : [],
|
|
@@ -12205,10 +12314,10 @@ ${repomapText}`] : [],
|
|
|
12205
12314
|
function loadTaskText(ctx, fromPrior) {
|
|
12206
12315
|
if (fromPrior)
|
|
12207
12316
|
return fromPrior;
|
|
12208
|
-
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ??
|
|
12209
|
-
if (
|
|
12317
|
+
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join51(ctx.runDir, "issue.json");
|
|
12318
|
+
if (existsSync31(issueJsonPath)) {
|
|
12210
12319
|
try {
|
|
12211
|
-
const issue = JSON.parse(
|
|
12320
|
+
const issue = JSON.parse(readFileSync36(issueJsonPath, "utf8"));
|
|
12212
12321
|
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
12213
12322
|
|
|
12214
12323
|
`);
|
|
@@ -12220,7 +12329,7 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
12220
12329
|
}
|
|
12221
12330
|
function wallTargetDir(repoDir, existingTests) {
|
|
12222
12331
|
const first = existingTests[0];
|
|
12223
|
-
return first ?
|
|
12332
|
+
return first ? join51(repoDir, dirname19(first.path)) : join51(repoDir, "tests");
|
|
12224
12333
|
}
|
|
12225
12334
|
function guessRunner(fileName, runners) {
|
|
12226
12335
|
if (fileName.endsWith(".py"))
|
|
@@ -12251,14 +12360,14 @@ async function wallAuthor(ctx, signal) {
|
|
|
12251
12360
|
const tree = prior.intake?.tree;
|
|
12252
12361
|
const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
|
|
12253
12362
|
const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
|
|
12254
|
-
const conv2 = readPackageConventions(
|
|
12255
|
-
const cwd = mkdtempSync7(
|
|
12363
|
+
const conv2 = readPackageConventions(join51(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
|
|
12364
|
+
const cwd = mkdtempSync7(join51(tmpdir9(), "loki-e15-wall-"));
|
|
12256
12365
|
const wm = wallManifestFor(ctx.repoDir, tree, task);
|
|
12257
12366
|
for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
|
|
12258
|
-
|
|
12367
|
+
writeFileSync21(join51(cwd, n2), c, "utf8");
|
|
12259
12368
|
const session = await ctx.sessions.run({
|
|
12260
12369
|
stage: "wall",
|
|
12261
|
-
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2)),
|
|
12370
|
+
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
|
|
12262
12371
|
tier: "development",
|
|
12263
12372
|
model: wallModel(),
|
|
12264
12373
|
iterationId: `${ctx.runId}-wall`,
|
|
@@ -12271,14 +12380,14 @@ async function wallAuthor(ctx, signal) {
|
|
|
12271
12380
|
return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
|
|
12272
12381
|
}
|
|
12273
12382
|
const generated = readdirSync14(cwd).filter((f) => f.startsWith(WALL_PREFIX));
|
|
12274
|
-
const sealedDir =
|
|
12383
|
+
const sealedDir = join51(ctx.runDir, "wall");
|
|
12275
12384
|
if (generated.length > 0)
|
|
12276
|
-
|
|
12385
|
+
mkdirSync19(sealedDir, { recursive: true });
|
|
12277
12386
|
const contents = new Map;
|
|
12278
12387
|
const discarded = [];
|
|
12279
12388
|
for (const name of generated) {
|
|
12280
|
-
const content =
|
|
12281
|
-
|
|
12389
|
+
const content = readFileSync36(join51(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
|
|
12390
|
+
writeFileSync21(join51(sealedDir, name), content, "utf8");
|
|
12282
12391
|
if (bad)
|
|
12283
12392
|
discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
|
|
12284
12393
|
else
|
|
@@ -12290,7 +12399,7 @@ async function wallAuthor(ctx, signal) {
|
|
|
12290
12399
|
function installWall(ctx, a, baseDir, opts = {}) {
|
|
12291
12400
|
const { generated, contents, discarded, targetDir, runners } = a;
|
|
12292
12401
|
if (generated.length > 0)
|
|
12293
|
-
|
|
12402
|
+
mkdirSync19(targetDir, { recursive: true });
|
|
12294
12403
|
const sealedFiles = [];
|
|
12295
12404
|
const readOnlyFiles = [];
|
|
12296
12405
|
const wallTests = [];
|
|
@@ -12298,8 +12407,8 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12298
12407
|
const content = contents.get(name);
|
|
12299
12408
|
if (content === undefined)
|
|
12300
12409
|
continue;
|
|
12301
|
-
const dest =
|
|
12302
|
-
|
|
12410
|
+
const dest = join51(targetDir, name), runner = guessRunner(name, runners);
|
|
12411
|
+
writeFileSync21(dest, content, "utf8");
|
|
12303
12412
|
sealedFiles.push({ path: dest, sha256: sha2562(content) });
|
|
12304
12413
|
readOnlyFiles.push({ path: dest, content });
|
|
12305
12414
|
if (runner)
|
|
@@ -12308,7 +12417,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12308
12417
|
ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
|
|
12309
12418
|
const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
|
|
12310
12419
|
for (const t of wallTests) {
|
|
12311
|
-
const r = baseRunner.run(baseDir, [t]), abs =
|
|
12420
|
+
const r = baseRunner.run(baseDir, [t]), abs = join51(ctx.repoDir, t.path);
|
|
12312
12421
|
baseRun.pass += r.pass;
|
|
12313
12422
|
baseRun.fail += r.fail;
|
|
12314
12423
|
baseRun.not_run += r.not_run ?? 0;
|
|
@@ -12341,6 +12450,7 @@ async function runWall(ctx, signal, opts = {}) {
|
|
|
12341
12450
|
var WALL_PREFIX = "loki_wall_", BASE_RUN_TIMEOUT_MS = 60000, WALL_MAP_MAX_LINES = 200, wallStage, stage2;
|
|
12342
12451
|
var init_wall = __esm(() => {
|
|
12343
12452
|
init_types();
|
|
12453
|
+
init_unit_mode();
|
|
12344
12454
|
init_lean_prefix();
|
|
12345
12455
|
init_wall_hints();
|
|
12346
12456
|
init_sizing();
|
|
@@ -12359,20 +12469,20 @@ var init_wall = __esm(() => {
|
|
|
12359
12469
|
});
|
|
12360
12470
|
|
|
12361
12471
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
12362
|
-
import { chmodSync as
|
|
12363
|
-
import { dirname as
|
|
12472
|
+
import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as readFileSync37, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
|
|
12473
|
+
import { dirname as dirname20, join as join52, relative as relative7, sep as sep11 } from "path";
|
|
12364
12474
|
function safeRestore(repoDir, f, want, mode) {
|
|
12365
|
-
const root = realpathSync19(repoDir), fp =
|
|
12475
|
+
const root = realpathSync19(repoDir), fp = join52(root, f), rel = relative7(root, dirname20(fp));
|
|
12366
12476
|
if (rel.startsWith(".."))
|
|
12367
12477
|
throw new Error("outside repo");
|
|
12368
12478
|
let cur = root;
|
|
12369
12479
|
for (const part of rel === "" ? [] : rel.split(sep11)) {
|
|
12370
|
-
cur =
|
|
12480
|
+
cur = join52(cur, part);
|
|
12371
12481
|
let st2;
|
|
12372
12482
|
try {
|
|
12373
12483
|
st2 = lstatSync10(cur);
|
|
12374
12484
|
} catch {
|
|
12375
|
-
|
|
12485
|
+
mkdirSync20(cur);
|
|
12376
12486
|
continue;
|
|
12377
12487
|
}
|
|
12378
12488
|
if (!st2.isDirectory())
|
|
@@ -12384,23 +12494,23 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
12384
12494
|
} catch {
|
|
12385
12495
|
st = null;
|
|
12386
12496
|
}
|
|
12387
|
-
if (st?.isFile() &&
|
|
12388
|
-
|
|
12497
|
+
if (st?.isFile() && readFileSync37(fp).equals(want)) {
|
|
12498
|
+
chmodSync5(fp, mode);
|
|
12389
12499
|
return;
|
|
12390
12500
|
}
|
|
12391
12501
|
if (st) {
|
|
12392
12502
|
if (st.isDirectory())
|
|
12393
12503
|
rmdirSync(fp);
|
|
12394
12504
|
else
|
|
12395
|
-
|
|
12505
|
+
unlinkSync5(fp);
|
|
12396
12506
|
}
|
|
12397
12507
|
const fd = openSync8(fp, "wx", mode);
|
|
12398
12508
|
try {
|
|
12399
|
-
|
|
12509
|
+
writeFileSync22(fd, want);
|
|
12400
12510
|
} finally {
|
|
12401
12511
|
closeSync8(fd);
|
|
12402
12512
|
}
|
|
12403
|
-
|
|
12513
|
+
chmodSync5(fp, mode);
|
|
12404
12514
|
}
|
|
12405
12515
|
function alreadySatisfied(o) {
|
|
12406
12516
|
const b = o.wall?.base_run ?? {};
|
|
@@ -12422,7 +12532,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12422
12532
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
12423
12533
|
const notProven = [];
|
|
12424
12534
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
12425
|
-
const fp =
|
|
12535
|
+
const fp = join52(repoDir, f);
|
|
12426
12536
|
try {
|
|
12427
12537
|
if (v.startsWith("!")) {
|
|
12428
12538
|
const st = lstatSync10(fp);
|
|
@@ -12441,7 +12551,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12441
12551
|
try {
|
|
12442
12552
|
let m = 420;
|
|
12443
12553
|
try {
|
|
12444
|
-
const l = lstatSync10(
|
|
12554
|
+
const l = lstatSync10(join52(repoDir, f));
|
|
12445
12555
|
if (l.isFile())
|
|
12446
12556
|
m = l.mode & 511;
|
|
12447
12557
|
} catch {}
|
|
@@ -12474,9 +12584,9 @@ var init_preexisting_dirty = __esm(() => {
|
|
|
12474
12584
|
});
|
|
12475
12585
|
|
|
12476
12586
|
// ../../loki-ts/src/e10ext/commit_filter.ts
|
|
12477
|
-
import { dirname as
|
|
12587
|
+
import { dirname as dirname21 } from "path";
|
|
12478
12588
|
function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
|
|
12479
|
-
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) &&
|
|
12589
|
+
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));
|
|
12480
12590
|
}
|
|
12481
12591
|
var parseStaged = (out2) => out2.split("\x00").reduce((a, t, i, all) => i % 2 === 0 && t ? [...a, { st: t, f: all[i + 1] }] : a, []);
|
|
12482
12592
|
var init_commit_filter = __esm(() => {
|
|
@@ -12485,7 +12595,7 @@ var init_commit_filter = __esm(() => {
|
|
|
12485
12595
|
});
|
|
12486
12596
|
|
|
12487
12597
|
// ../../loki-ts/src/e10ext/scope.ts
|
|
12488
|
-
import { basename as basename18, dirname as
|
|
12598
|
+
import { basename as basename18, dirname as dirname22 } from "path";
|
|
12489
12599
|
function contractText(o) {
|
|
12490
12600
|
const c = o.intake?.contract_snapshot?.contract;
|
|
12491
12601
|
return Array.isArray(c?.criteria) ? c.criteria.map((x) => typeof x?.text === "string" ? x.text : "").join(`
|
|
@@ -12499,8 +12609,8 @@ function outsideScope(o, staged) {
|
|
|
12499
12609
|
const text2 = `${plan}
|
|
12500
12610
|
${typeof o.intake?.task === "string" ? o.intake.task : ""}
|
|
12501
12611
|
${contractText(o)}`, pre = o.intake?.preexisting_dirty ?? {};
|
|
12502
|
-
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) =>
|
|
12503
|
-
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(
|
|
12612
|
+
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) => dirname22(r)).filter((d) => d !== "."));
|
|
12613
|
+
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);
|
|
12504
12614
|
}
|
|
12505
12615
|
function flagOutsideScope(o, staged) {
|
|
12506
12616
|
const unit = unitFlags(staged, process.env, o.intake?.preexisting_dirty ?? {});
|
|
@@ -12514,76 +12624,10 @@ var init_scope2 = __esm(() => {
|
|
|
12514
12624
|
STOP = new Set(["the", "and", "for", "with", "that", "this", "all", "any", "src", "from", "into", "each", "across", "apply", "add", "use", "make"]);
|
|
12515
12625
|
});
|
|
12516
12626
|
|
|
12517
|
-
// ../../loki-ts/src/util/receipt_signer.ts
|
|
12518
|
-
import { createHash as createHash10, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
|
|
12519
|
-
import { chmodSync as chmodSync5, linkSync, mkdirSync as mkdirSync20, readFileSync as readFileSync37, statSync as statSync16, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
|
|
12520
|
-
import { homedir as homedir8 } from "os";
|
|
12521
|
-
import { dirname as dirname22, join as join52 } from "path";
|
|
12522
|
-
function canonicalJson(x) {
|
|
12523
|
-
const walk = (v) => {
|
|
12524
|
-
if (Array.isArray(v))
|
|
12525
|
-
return `[${v.map(walk).join(",")}]`;
|
|
12526
|
-
if (v !== null && typeof v === "object") {
|
|
12527
|
-
const o = v;
|
|
12528
|
-
return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
|
|
12529
|
-
}
|
|
12530
|
-
return JSON.stringify(v ?? null);
|
|
12531
|
-
};
|
|
12532
|
-
return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
12533
|
-
}
|
|
12534
|
-
function loadSigningKey(generate = true) {
|
|
12535
|
-
try {
|
|
12536
|
-
const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
|
|
12537
|
-
let pem = inline ?? "";
|
|
12538
|
-
if (!inline) {
|
|
12539
|
-
const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
12540
|
-
const file = given || join52(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
|
|
12541
|
-
try {
|
|
12542
|
-
pem = readFileSync37(file);
|
|
12543
|
-
if (!given) {
|
|
12544
|
-
for (const [f, m] of [[file, 384], [dirname22(file), 448]])
|
|
12545
|
-
if (statSync16(f).mode & 63)
|
|
12546
|
-
chmodSync5(f, m);
|
|
12547
|
-
}
|
|
12548
|
-
} catch (e) {
|
|
12549
|
-
if (e.code !== "ENOENT" || !generate)
|
|
12550
|
-
return null;
|
|
12551
|
-
mkdirSync20(dirname22(dirname22(file)), { recursive: true, mode: 448 });
|
|
12552
|
-
mkdirSync20(dirname22(file), { recursive: true, mode: 448 });
|
|
12553
|
-
const tmp = `${file}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
12554
|
-
writeFileSync22(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
|
|
12555
|
-
try {
|
|
12556
|
-
linkSync(tmp, file);
|
|
12557
|
-
} catch (l) {
|
|
12558
|
-
if (l.code !== "EEXIST")
|
|
12559
|
-
throw l;
|
|
12560
|
-
} finally {
|
|
12561
|
-
unlinkSync5(tmp);
|
|
12562
|
-
}
|
|
12563
|
-
pem = readFileSync37(file);
|
|
12564
|
-
}
|
|
12565
|
-
}
|
|
12566
|
-
const k = createPrivateKey(pem);
|
|
12567
|
-
return k.asymmetricKeyType === "ed25519" ? k : null;
|
|
12568
|
-
} catch {
|
|
12569
|
-
return null;
|
|
12570
|
-
}
|
|
12571
|
-
}
|
|
12572
|
-
function signReceipt(runId, hash) {
|
|
12573
|
-
const key = loadSigningKey();
|
|
12574
|
-
if (!key)
|
|
12575
|
-
return { jwt: null, kid: null };
|
|
12576
|
-
const kid = kidOf2(createPublicKey2(key));
|
|
12577
|
-
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) }))}`;
|
|
12578
|
-
return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
|
|
12579
|
-
}
|
|
12580
|
-
var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash10("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest());
|
|
12581
|
-
var init_receipt_signer = () => {};
|
|
12582
|
-
|
|
12583
12627
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
12584
12628
|
import { spawn as spawn7 } from "child_process";
|
|
12585
|
-
import { createHash as
|
|
12586
|
-
import { existsSync as
|
|
12629
|
+
import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
|
|
12630
|
+
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
|
|
12587
12631
|
import { createServer } from "net";
|
|
12588
12632
|
import { dirname as dirname23, isAbsolute as isAbsolute8, join as join53, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
|
|
12589
12633
|
function routeFor(file) {
|
|
@@ -12615,7 +12659,7 @@ function checkScreens(runDir, screens) {
|
|
|
12615
12659
|
if (isSymlink(cur))
|
|
12616
12660
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
12617
12661
|
}
|
|
12618
|
-
if (!
|
|
12662
|
+
if (!existsSync32(file))
|
|
12619
12663
|
return `evidence screenshot is missing: ${s.path}`;
|
|
12620
12664
|
if (!isRegularFile(file))
|
|
12621
12665
|
return `evidence screenshot is not a regular file: ${s.path}`;
|
|
@@ -12659,7 +12703,7 @@ async function waitUp(url, ms, signal) {
|
|
|
12659
12703
|
function openapiPaths(repoDir) {
|
|
12660
12704
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
12661
12705
|
const p = join53(repoDir, f);
|
|
12662
|
-
if (!
|
|
12706
|
+
if (!existsSync32(p))
|
|
12663
12707
|
continue;
|
|
12664
12708
|
try {
|
|
12665
12709
|
const txt = readFileSync38(p, "utf8");
|
|
@@ -12766,7 +12810,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12766
12810
|
const pkgPath = join53(appDir, "package.json");
|
|
12767
12811
|
let script = null;
|
|
12768
12812
|
if (!boot) {
|
|
12769
|
-
if (!
|
|
12813
|
+
if (!existsSync32(pkgPath))
|
|
12770
12814
|
return skip("no package.json");
|
|
12771
12815
|
script = pickScript(JSON.parse(readFileSync38(pkgPath, "utf8")));
|
|
12772
12816
|
if (!script)
|
|
@@ -12808,7 +12852,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12808
12852
|
break;
|
|
12809
12853
|
const rel = join53(rel0, `${screenName(route)}.png`);
|
|
12810
12854
|
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join53(runDir, rel)], appDir, left(), signal);
|
|
12811
|
-
if (ok && !aborted() && !isSymlink(join53(runDir, rel)) &&
|
|
12855
|
+
if (ok && !aborted() && !isSymlink(join53(runDir, rel)) && existsSync32(join53(runDir, rel)))
|
|
12812
12856
|
rels.push(rel);
|
|
12813
12857
|
}
|
|
12814
12858
|
if (aborted())
|
|
@@ -12860,12 +12904,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
12860
12904
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
12861
12905
|
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
|
|
12862
12906
|
}
|
|
12863
|
-
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) =>
|
|
12907
|
+
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(readFileSync38(p)).digest("hex"), playwrightBin = (repoDir) => {
|
|
12864
12908
|
const bin = join53(repoDir, "node_modules", ".bin", "playwright");
|
|
12865
|
-
return
|
|
12909
|
+
return existsSync32(bin) ? bin : null;
|
|
12866
12910
|
}, playwrightTestPkg = (repoDir) => {
|
|
12867
12911
|
const d = join53(repoDir, "node_modules", "@playwright", "test");
|
|
12868
|
-
return
|
|
12912
|
+
return existsSync32(join53(d, "package.json")) ? d : null;
|
|
12869
12913
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
12870
12914
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
12871
12915
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
@@ -12896,7 +12940,7 @@ var init_visual_evidence = __esm(() => {
|
|
|
12896
12940
|
});
|
|
12897
12941
|
|
|
12898
12942
|
// ../../loki-ts/src/council/role_agents.ts
|
|
12899
|
-
import { existsSync as
|
|
12943
|
+
import { existsSync as existsSync33, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
|
|
12900
12944
|
import { homedir as homedir9 } from "os";
|
|
12901
12945
|
import { join as join54 } from "path";
|
|
12902
12946
|
function agentDirs(repoDir, env = process.env) {
|
|
@@ -12917,7 +12961,7 @@ function parseAgent(file, stem2) {
|
|
|
12917
12961
|
}
|
|
12918
12962
|
function loadRoleAgent(repoDir, role, env = process.env) {
|
|
12919
12963
|
for (const d of agentDirs(repoDir, env)) {
|
|
12920
|
-
if (!
|
|
12964
|
+
if (!existsSync33(d))
|
|
12921
12965
|
continue;
|
|
12922
12966
|
let names;
|
|
12923
12967
|
try {
|
|
@@ -12945,7 +12989,7 @@ var init_role_agents = __esm(() => {
|
|
|
12945
12989
|
});
|
|
12946
12990
|
|
|
12947
12991
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
12948
|
-
import { existsSync as
|
|
12992
|
+
import { existsSync as existsSync34, readFileSync as readFileSync40 } from "fs";
|
|
12949
12993
|
import { join as join55 } from "path";
|
|
12950
12994
|
function minVerdict(verdict, review) {
|
|
12951
12995
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
@@ -12960,7 +13004,7 @@ function explicitReview(repoDir, env) {
|
|
|
12960
13004
|
return "off";
|
|
12961
13005
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
12962
13006
|
const p = join55(repoDir, f);
|
|
12963
|
-
if (!
|
|
13007
|
+
if (!existsSync34(p))
|
|
12964
13008
|
continue;
|
|
12965
13009
|
try {
|
|
12966
13010
|
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync40(p, "utf8"));
|
|
@@ -13181,9 +13225,9 @@ var init_cost_preview = __esm(() => {
|
|
|
13181
13225
|
});
|
|
13182
13226
|
|
|
13183
13227
|
// ../../loki-ts/src/features/contract.ts
|
|
13184
|
-
import { existsSync as
|
|
13228
|
+
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync41, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
13185
13229
|
import { dirname as dirname24, join as join56 } from "path";
|
|
13186
|
-
import { createHash as
|
|
13230
|
+
import { createHash as createHash13 } from "crypto";
|
|
13187
13231
|
function contractEnabled(env = process.env) {
|
|
13188
13232
|
return env["LOKI_CONTRACT"] !== "0";
|
|
13189
13233
|
}
|
|
@@ -13234,7 +13278,7 @@ function readContract(repoDir) {
|
|
|
13234
13278
|
} catch {
|
|
13235
13279
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
13236
13280
|
}
|
|
13237
|
-
const sha2563 =
|
|
13281
|
+
const sha2563 = createHash13("sha256").update(raw2).digest("hex");
|
|
13238
13282
|
let j;
|
|
13239
13283
|
try {
|
|
13240
13284
|
j = JSON.parse(raw2.toString("utf8"));
|
|
@@ -13304,12 +13348,12 @@ var init_contract = __esm(() => {
|
|
|
13304
13348
|
});
|
|
13305
13349
|
|
|
13306
13350
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
13307
|
-
import { createHash as
|
|
13308
|
-
import { existsSync as
|
|
13309
|
-
import { dirname as dirname25, join as join57 } from "path";
|
|
13351
|
+
import { createHash as createHash14 } from "crypto";
|
|
13352
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync17, readFileSync as readFileSync42, renameSync as renameSync8, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
|
|
13353
|
+
import { basename as basename19, dirname as dirname25, join as join57 } from "path";
|
|
13310
13354
|
function sealGroup(runDir, rh) {
|
|
13311
13355
|
const manifestPath = join57(runDir, "group", "manifest.json");
|
|
13312
|
-
if (!
|
|
13356
|
+
if (!existsSync36(manifestPath))
|
|
13313
13357
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
13314
13358
|
const notProven = [];
|
|
13315
13359
|
let problems = 0;
|
|
@@ -13353,7 +13397,7 @@ function sealGroup(runDir, rh) {
|
|
|
13353
13397
|
unit_id: id,
|
|
13354
13398
|
sub_receipt_sha256: sha2(bytes),
|
|
13355
13399
|
receipt_sha256: sub["receipt_sha256"],
|
|
13356
|
-
events_file_sha256:
|
|
13400
|
+
events_file_sha256: existsSync36(ep) ? sha2(readFileSync42(ep)) : null,
|
|
13357
13401
|
verdict,
|
|
13358
13402
|
goal: text2(u["goal"], 200),
|
|
13359
13403
|
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
@@ -13378,7 +13422,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13378
13422
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
13379
13423
|
const g = receipt["group"];
|
|
13380
13424
|
if (g === undefined)
|
|
13381
|
-
return
|
|
13425
|
+
return existsSync36(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
13382
13426
|
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
13383
13427
|
return bad("group section malformed");
|
|
13384
13428
|
const entries = g["units"];
|
|
@@ -13403,7 +13447,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13403
13447
|
}
|
|
13404
13448
|
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
13405
13449
|
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
13406
|
-
const evSha =
|
|
13450
|
+
const evSha = existsSync36(ep) ? sha2(readFileSync42(ep)) : null;
|
|
13407
13451
|
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
13408
13452
|
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
13409
13453
|
const sub = readJson5(rp);
|
|
@@ -13421,7 +13465,83 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13421
13465
|
}
|
|
13422
13466
|
return unchecked ? { verdict: "UNCHECKED", reason: `group: ${unchecked}` } : null;
|
|
13423
13467
|
}
|
|
13424
|
-
|
|
13468
|
+
function compositeVerdict(childVerdicts) {
|
|
13469
|
+
const base = childVerdicts.includes("SPEC_CONFLICT") ? "SPEC_CONFLICT" : childVerdicts.length > 0 && childVerdicts.every((v) => v === "ALREADY_SATISFIED") ? "ALREADY_SATISFIED" : "VERIFIED";
|
|
13470
|
+
const failed = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
|
|
13471
|
+
return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
|
|
13472
|
+
}
|
|
13473
|
+
async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
13474
|
+
const runDir = dirname25(receiptPath);
|
|
13475
|
+
const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
|
|
13476
|
+
const bad = (reason) => ({ problem: { verdict: "TAMPERED", reason: `composite: ${reason}` }, verdict: "FAILED" });
|
|
13477
|
+
const kids = receipt["children"];
|
|
13478
|
+
if (!Array.isArray(kids) || kids.length === 0 || !ID_RE.test(String(receipt["run_id"] ?? "")))
|
|
13479
|
+
return bad("body malformed");
|
|
13480
|
+
let ibytes;
|
|
13481
|
+
try {
|
|
13482
|
+
ibytes = readFileSync42(join57(runDir, "integration.json"));
|
|
13483
|
+
} catch {
|
|
13484
|
+
return bad("integration.json missing");
|
|
13485
|
+
}
|
|
13486
|
+
if (sha2(ibytes) !== receipt["integration_sha256"])
|
|
13487
|
+
return bad("integration.json does not match its recorded hash");
|
|
13488
|
+
const integ = readJson5(join57(runDir, "integration.json"));
|
|
13489
|
+
if (!isObj10(integ) || !isObj10(integ["outcomes"]))
|
|
13490
|
+
return bad("integration.json has no outcomes");
|
|
13491
|
+
const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
|
|
13492
|
+
const order2 = Array.isArray(integ["order"]) ? integ["order"].map(String) : Object.keys(outcomes).sort();
|
|
13493
|
+
const kidRepos = kids.map((k) => isObj10(k) ? String(k["repo"] ?? "") : "");
|
|
13494
|
+
if (new Set(kidRepos).size !== kidRepos.length)
|
|
13495
|
+
return bad("children list a repo more than once");
|
|
13496
|
+
if (kidRepos.length !== order2.length || kidRepos.some((r, i) => r !== order2[i]))
|
|
13497
|
+
return bad("children do not cover the integrated repos in order (a repo is missing or extra)");
|
|
13498
|
+
let unchecked = null;
|
|
13499
|
+
const verdicts = [];
|
|
13500
|
+
for (const raw2 of kids) {
|
|
13501
|
+
const c = isObj10(raw2) ? raw2 : {}, repo = String(c["repo"] ?? ""), rel = String(c["receipt_path"] ?? "");
|
|
13502
|
+
if (!repo || !relOk(rel) || !rel.startsWith(`worktrees/${slugOf(repo)}/.loki/runs/`))
|
|
13503
|
+
return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
|
|
13504
|
+
if (outcomes[repo] !== "ok")
|
|
13505
|
+
return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
|
|
13506
|
+
const rp = join57(runDir, rel);
|
|
13507
|
+
let bytes;
|
|
13508
|
+
try {
|
|
13509
|
+
bytes = readFileSync42(rp);
|
|
13510
|
+
} catch {
|
|
13511
|
+
return bad(`child ${repo} receipt missing`);
|
|
13512
|
+
}
|
|
13513
|
+
if (sha2(bytes) !== c["receipt_file_sha256"])
|
|
13514
|
+
return bad(`child ${repo} receipt does not match its recorded hash (swapped or edited)`);
|
|
13515
|
+
const sub = readJson5(rp);
|
|
13516
|
+
if (!isObj10(sub) || sub["receipt_sha256"] !== c["receipt_sha256"] || rh(sub) !== c["receipt_sha256"])
|
|
13517
|
+
return bad(`child ${repo} receipt hash does not recompute`);
|
|
13518
|
+
if (String(sub["verdict"] ?? "") !== c["verdict"])
|
|
13519
|
+
return bad(`child ${repo} verdict differs from the composite`);
|
|
13520
|
+
if (String(sub["head_sha"] ?? "") !== c["head_sha"] || !c["head_sha"])
|
|
13521
|
+
return bad(`child ${repo} head_sha differs from the composite`);
|
|
13522
|
+
verdicts.push(String(c["verdict"]));
|
|
13523
|
+
if (typeof heads[repo] !== "string" || heads[repo] === "")
|
|
13524
|
+
unchecked ??= `child ${repo} has no integrated head to compare (freshness not checked)`;
|
|
13525
|
+
else if (heads[repo] !== c["head_sha"])
|
|
13526
|
+
unchecked ??= `child ${repo} head ${String(c["head_sha"])} is not the integrated head ${String(heads[repo])}`;
|
|
13527
|
+
const r = await verifyOne(rp, deps);
|
|
13528
|
+
if (r.verdict === "TAMPERED")
|
|
13529
|
+
return bad(`child ${repo} failed verify: ${r.reasons.join("; ")}`);
|
|
13530
|
+
if (r.verdict === "UNSIGNED" && signed)
|
|
13531
|
+
unchecked ??= `child ${repo} receipt is unsigned under a signed composite`;
|
|
13532
|
+
if (r.verdict === "UNCHECKED")
|
|
13533
|
+
unchecked ??= `child ${repo} not checked: ${r.reasons.join("; ")}`;
|
|
13534
|
+
}
|
|
13535
|
+
const expect = compositeVerdict(verdicts);
|
|
13536
|
+
if (receipt["verdict"] !== expect)
|
|
13537
|
+
return bad(`recorded verdict ${String(receipt["verdict"])} differs from the children (${expect})`);
|
|
13538
|
+
return { problem: unchecked ? { verdict: "UNCHECKED", reason: `composite: ${unchecked}` } : null, verdict: expect };
|
|
13539
|
+
}
|
|
13540
|
+
function compositeHash(r) {
|
|
13541
|
+
const { composite_sha256: _c, verification: _v, ...body } = r;
|
|
13542
|
+
return sha2(canonicalJson(body));
|
|
13543
|
+
}
|
|
13544
|
+
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) => join57(runDir, "group", "units"), readJson5 = (p) => {
|
|
13425
13545
|
try {
|
|
13426
13546
|
return JSON.parse(readFileSync42(p, "utf8"));
|
|
13427
13547
|
} catch {
|
|
@@ -13433,15 +13553,16 @@ var ID_RE, PASSING, sha2 = (b) => createHash13("sha256").update(b).digest("hex")
|
|
|
13433
13553
|
} catch {
|
|
13434
13554
|
return null;
|
|
13435
13555
|
}
|
|
13436
|
-
};
|
|
13556
|
+
}, COMPOSITE_SCHEMA = "loki.v10.composite/1", slugOf = (repo) => repo.replace(/\//g, "__"), isComposite = (r) => r["schema"] === COMPOSITE_SCHEMA, relOk = (p) => p !== "" && !p.startsWith("/") && !p.split("/").includes("..");
|
|
13437
13557
|
var init_seal_group = __esm(() => {
|
|
13558
|
+
init_receipt_signer();
|
|
13438
13559
|
ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
13439
13560
|
PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
13440
13561
|
});
|
|
13441
13562
|
|
|
13442
13563
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
13443
|
-
import { existsSync as
|
|
13444
|
-
import { basename as
|
|
13564
|
+
import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
|
|
13565
|
+
import { basename as basename20, join as join58 } from "path";
|
|
13445
13566
|
function supplyEnabled(env) {
|
|
13446
13567
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
13447
13568
|
}
|
|
@@ -13453,11 +13574,11 @@ function supplyVerdict(verdict, r) {
|
|
|
13453
13574
|
return r.blocked && verdict === "VERIFIED" ? "FAILED" : verdict;
|
|
13454
13575
|
}
|
|
13455
13576
|
function isManifestPath(path) {
|
|
13456
|
-
return MANIFESTS.test(
|
|
13577
|
+
return MANIFESTS.test(basename20(path));
|
|
13457
13578
|
}
|
|
13458
13579
|
function readDeclared(repoDir) {
|
|
13459
13580
|
const p = join58(repoDir, ".loki", DECLARED_FILE);
|
|
13460
|
-
if (!
|
|
13581
|
+
if (!existsSync37(p))
|
|
13461
13582
|
return { deps: [], problem: null };
|
|
13462
13583
|
try {
|
|
13463
13584
|
if (statSync18(p).size > MAX_FILE_BYTES)
|
|
@@ -13483,7 +13604,7 @@ function readDeclared(repoDir) {
|
|
|
13483
13604
|
}
|
|
13484
13605
|
function readAllowlist(repoDir) {
|
|
13485
13606
|
const p = join58(repoDir, ".loki", "supply-allowlist");
|
|
13486
|
-
if (!
|
|
13607
|
+
if (!existsSync37(p))
|
|
13487
13608
|
return new Set;
|
|
13488
13609
|
try {
|
|
13489
13610
|
return new Set(readFileSync43(p, "utf8").split(`
|
|
@@ -13684,9 +13805,9 @@ var init_supply_guard = __esm(() => {
|
|
|
13684
13805
|
});
|
|
13685
13806
|
|
|
13686
13807
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
13687
|
-
import { createHash as
|
|
13688
|
-
import { existsSync as
|
|
13689
|
-
import { basename as
|
|
13808
|
+
import { createHash as createHash15 } from "crypto";
|
|
13809
|
+
import { existsSync as existsSync38, readdirSync as readdirSync18, mkdirSync as mkdirSync24, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
|
|
13810
|
+
import { basename as basename21, isAbsolute as isAbsolute9, join as join59, relative as relative9 } from "path";
|
|
13690
13811
|
function sanitizeReason(s) {
|
|
13691
13812
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
13692
13813
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
@@ -13760,7 +13881,7 @@ function renderReceiptMd(r) {
|
|
|
13760
13881
|
].join(`
|
|
13761
13882
|
`);
|
|
13762
13883
|
}
|
|
13763
|
-
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) =>
|
|
13884
|
+
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) => {
|
|
13764
13885
|
const r = relative9(ctx.repoDir, join59(ctx.runDir, "receipt.json"));
|
|
13765
13886
|
return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
13766
13887
|
}, commitStage, sealStage;
|
|
@@ -13863,7 +13984,7 @@ var init_seal = __esm(() => {
|
|
|
13863
13984
|
notProven.add(`Wall test not executed: ${wallNotRun} file(s), no reason recorded`);
|
|
13864
13985
|
if (wallNotRun > 0) {
|
|
13865
13986
|
try {
|
|
13866
|
-
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) =>
|
|
13987
|
+
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
|
|
13867
13988
|
for (const n2 of readdirSync18(join59(ctx.runDir, "wall")).sort())
|
|
13868
13989
|
if (!kept.has(n2))
|
|
13869
13990
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
@@ -13979,7 +14100,7 @@ var init_seal = __esm(() => {
|
|
|
13979
14100
|
provider: ctx.provider,
|
|
13980
14101
|
model: ctx.model,
|
|
13981
14102
|
resumed: o.intake?.resumed === true,
|
|
13982
|
-
events_sha256: sha2562(
|
|
14103
|
+
events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync44(eventsPath) : ""),
|
|
13983
14104
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
13984
14105
|
log_seal: true,
|
|
13985
14106
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -13999,17 +14120,22 @@ var init_seal = __esm(() => {
|
|
|
13999
14120
|
hash = receiptSha256(body);
|
|
14000
14121
|
}
|
|
14001
14122
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
14002
|
-
|
|
14123
|
+
mkdirSync24(ctx.runDir, { recursive: true });
|
|
14003
14124
|
const path = join59(ctx.runDir, "receipt.json");
|
|
14004
|
-
|
|
14125
|
+
writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
|
|
14005
14126
|
`);
|
|
14006
|
-
|
|
14127
|
+
writeFileSync26(join59(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
14007
14128
|
recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
|
|
14008
14129
|
const signed = sig.jwt !== null;
|
|
14009
14130
|
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) } : {} };
|
|
14010
14131
|
try {
|
|
14011
14132
|
recordRunVerdict(ctx.repoDir, ctx.runId, verdict);
|
|
14012
14133
|
} catch {}
|
|
14134
|
+
if (process.env["LOKI_AUTO_LEARN_PR"] !== "0") {
|
|
14135
|
+
try {
|
|
14136
|
+
appendReceiptFact(ctx.repoDir, { runId: ctx.runId, verdict, receiptSha256: signed ? hash : null, title: str3(ctx.outputs().intake?.title) ?? `run ${ctx.runId}`, headSha: receipt.head_sha });
|
|
14137
|
+
} catch {}
|
|
14138
|
+
}
|
|
14013
14139
|
ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} });
|
|
14014
14140
|
return { status: "completed", data: { ...data, summary: `${verdict} receipt ${hash.slice(0, 12)} ${signed ? `SIGNED kid ${sig.kid}` : "UNSIGNED"}` } };
|
|
14015
14141
|
}
|
|
@@ -14109,9 +14235,9 @@ var init_keys_cmd = __esm(() => {
|
|
|
14109
14235
|
});
|
|
14110
14236
|
|
|
14111
14237
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
14112
|
-
import { existsSync as
|
|
14113
|
-
import { dirname as dirname27, join as join60 } from "path";
|
|
14114
|
-
import { createHash as
|
|
14238
|
+
import { existsSync as existsSync39, readFileSync as readFileSync46, renameSync as renameSync9, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
|
|
14239
|
+
import { basename as basename22, dirname as dirname27, join as join60 } from "path";
|
|
14240
|
+
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
|
|
14115
14241
|
function pubFor(kid, given) {
|
|
14116
14242
|
if (given)
|
|
14117
14243
|
return kidOf2(given) === kid ? given : undefined;
|
|
@@ -14148,10 +14274,10 @@ function checkAttestation(jwt, expectedHash, given) {
|
|
|
14148
14274
|
}
|
|
14149
14275
|
function checkEventLog(receiptPath, receipt) {
|
|
14150
14276
|
const bound = receipt["events_sha256"];
|
|
14151
|
-
if (typeof bound !== "string" || bound ===
|
|
14277
|
+
if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
|
|
14152
14278
|
return null;
|
|
14153
14279
|
const path = join60(dirname27(receiptPath), "events.jsonl");
|
|
14154
|
-
if (!
|
|
14280
|
+
if (!existsSync39(path))
|
|
14155
14281
|
return "events.jsonl is missing";
|
|
14156
14282
|
const raw2 = readFileSync46(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
|
|
14157
14283
|
`).filter((l) => l.trim() !== "");
|
|
@@ -14159,7 +14285,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14159
14285
|
return "events.jsonl has a forged, edited or missing line";
|
|
14160
14286
|
if (events2.some((e) => e.type === "tamper.detected"))
|
|
14161
14287
|
return "the supervisor detected events.jsonl being modified during the run";
|
|
14162
|
-
const h =
|
|
14288
|
+
const h = createHash16("sha256");
|
|
14163
14289
|
for (const l of lines) {
|
|
14164
14290
|
if (h.copy().digest("hex") === bound)
|
|
14165
14291
|
return null;
|
|
@@ -14178,7 +14304,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14178
14304
|
if (!d)
|
|
14179
14305
|
return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
|
|
14180
14306
|
const lines = readFileSync46(path, "utf8").split(`
|
|
14181
|
-
`).filter((l) => l.trim() !== ""), sha3 =
|
|
14307
|
+
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines.slice(0, done + 1).join(`
|
|
14182
14308
|
`) + `
|
|
14183
14309
|
`).digest("hex");
|
|
14184
14310
|
const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
|
|
@@ -14187,7 +14313,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14187
14313
|
return null;
|
|
14188
14314
|
}
|
|
14189
14315
|
async function verifyReceipt(receiptPath, deps = {}) {
|
|
14190
|
-
if (!
|
|
14316
|
+
if (!existsSync39(receiptPath))
|
|
14191
14317
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
14192
14318
|
let receipt;
|
|
14193
14319
|
try {
|
|
@@ -14197,11 +14323,12 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14197
14323
|
}
|
|
14198
14324
|
if (isEnvelope(receipt))
|
|
14199
14325
|
return verifyDsseReceipt(receipt, { pubFor: (kid) => pubFor(kid, deps.pubkey), hash: computeReceiptHash, attest: (j, h) => checkAttestation(j, h, deps.pubkey) });
|
|
14326
|
+
if (isComposite(receipt))
|
|
14327
|
+
return verifyCompositeFile(receiptPath, receipt, deps);
|
|
14200
14328
|
const recorded = receipt["receipt_sha256"];
|
|
14201
14329
|
const computed = computeReceiptHash(receipt);
|
|
14202
|
-
if (typeof recorded !== "string" || recorded !== computed)
|
|
14330
|
+
if (typeof recorded !== "string" || recorded !== computed)
|
|
14203
14331
|
return { verdict: "TAMPERED", reasons: [`receipt_sha256 mismatch: recorded ${JSON.stringify(recorded)}, computed ${computed}`] };
|
|
14204
|
-
}
|
|
14205
14332
|
const logProblem = checkEventLog(receiptPath, receipt);
|
|
14206
14333
|
if (logProblem)
|
|
14207
14334
|
return { verdict: "TAMPERED", reasons: [logProblem] };
|
|
@@ -14234,9 +14361,34 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14234
14361
|
return { verdict: gp.verdict, reasons: [gp.reason] };
|
|
14235
14362
|
return { verdict: "VERIFIED", reasons: [], receiptSha256: computed };
|
|
14236
14363
|
}
|
|
14364
|
+
async function verifyCompositeFile(receiptPath, receipt, deps) {
|
|
14365
|
+
const computed = compositeHash(receipt);
|
|
14366
|
+
if (receipt["composite_sha256"] !== computed)
|
|
14367
|
+
return { verdict: "TAMPERED", reasons: [`composite_sha256 mismatch: recorded ${JSON.stringify(receipt["composite_sha256"])}, computed ${computed}`] };
|
|
14368
|
+
const c = await verifyComposite(receiptPath, receipt, computeReceiptHash, verifyReceipt, deps);
|
|
14369
|
+
if (c.problem?.verdict === "TAMPERED")
|
|
14370
|
+
return { verdict: "TAMPERED", reasons: [c.problem.reason] };
|
|
14371
|
+
const jwt = (receipt["verification"] ?? {}).jwt ?? null;
|
|
14372
|
+
if (jwt !== null && typeof jwt !== "string")
|
|
14373
|
+
return { verdict: "UNCHECKED", reasons: ["verification.jwt is not a string"] };
|
|
14374
|
+
if (!jwt)
|
|
14375
|
+
return c.problem ? { verdict: c.problem.verdict, reasons: [c.problem.reason] } : { verdict: "UNSIGNED", reasons: [], receiptSha256: computed };
|
|
14376
|
+
const outcome = checkAttestation(jwt, computed, deps.pubkey);
|
|
14377
|
+
if (outcome.status === "unchecked")
|
|
14378
|
+
return { verdict: "UNCHECKED", reasons: [outcome.reason ?? "attestation not checked"] };
|
|
14379
|
+
if (outcome.status === "tampered")
|
|
14380
|
+
return { verdict: "TAMPERED", reasons: [outcome.reason ?? "attestation invalid"] };
|
|
14381
|
+
const shown = receipt["verification"].kid, signed = JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid;
|
|
14382
|
+
if (shown !== undefined && shown !== signed)
|
|
14383
|
+
return { verdict: "TAMPERED", reasons: ["verification.kid differs from the signing kid in the attestation header"] };
|
|
14384
|
+
if (c.problem)
|
|
14385
|
+
return { verdict: c.problem.verdict, reasons: [c.problem.reason] };
|
|
14386
|
+
return { verdict: "VERIFIED", reasons: [], receiptSha256: computed };
|
|
14387
|
+
}
|
|
14237
14388
|
var computeReceiptHash = (receipt) => receiptSha256(receipt);
|
|
14238
14389
|
var init_verify_cmd = __esm(() => {
|
|
14239
14390
|
init_paths();
|
|
14391
|
+
init_receipt_signer();
|
|
14240
14392
|
init_run_status();
|
|
14241
14393
|
init_events();
|
|
14242
14394
|
init_seal();
|
|
@@ -14247,7 +14399,7 @@ var init_verify_cmd = __esm(() => {
|
|
|
14247
14399
|
});
|
|
14248
14400
|
|
|
14249
14401
|
// src/server/app.ts
|
|
14250
|
-
import { existsSync as
|
|
14402
|
+
import { existsSync as existsSync41, readFileSync as readFileSync49, statSync as statSync21 } from "fs";
|
|
14251
14403
|
import { join as join63, resolve as resolve15, sep as sep14 } from "path";
|
|
14252
14404
|
|
|
14253
14405
|
// node_modules/hono/dist/request/constants.js
|
|
@@ -22455,7 +22607,7 @@ var loki_yaml_schema_default = {
|
|
|
22455
22607
|
items: { type: "string", minLength: 1 }
|
|
22456
22608
|
},
|
|
22457
22609
|
workspaces: {
|
|
22458
|
-
description: "Named groups of repos for cross-repo runs (`loki workspace run`).
|
|
22610
|
+
description: "Named groups of repos for cross-repo runs (`loki workspace run`). On by default; LOKI_WORKSPACES=0 disables. Unknown or mistyped keys fail validation.",
|
|
22459
22611
|
type: "object",
|
|
22460
22612
|
additionalProperties: {
|
|
22461
22613
|
type: "object",
|
|
@@ -22479,9 +22631,12 @@ var loki_yaml_schema_default = {
|
|
|
22479
22631
|
additionalProperties: false,
|
|
22480
22632
|
properties: {
|
|
22481
22633
|
command: { type: "string", minLength: 1 },
|
|
22482
|
-
timeout_s: { type: "integer", minimum: 1 }
|
|
22634
|
+
timeout_s: { type: "integer", minimum: 1 },
|
|
22635
|
+
per_stage: { type: "boolean" }
|
|
22483
22636
|
}
|
|
22484
22637
|
},
|
|
22638
|
+
concurrency: { type: "integer", minimum: 1 },
|
|
22639
|
+
plan: { type: "boolean" },
|
|
22485
22640
|
integration_files: { type: "array", items: { type: "string", minLength: 1 } }
|
|
22486
22641
|
}
|
|
22487
22642
|
}
|
|
@@ -24609,7 +24764,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
24609
24764
|
}
|
|
24610
24765
|
|
|
24611
24766
|
// src/server/legacy/shim.ts
|
|
24612
|
-
import { existsSync as
|
|
24767
|
+
import { existsSync as existsSync40, readFileSync as readFileSync48 } from "fs";
|
|
24613
24768
|
import { join as join62 } from "path";
|
|
24614
24769
|
|
|
24615
24770
|
// src/server/legacy/routes.ts
|
|
@@ -24984,7 +25139,7 @@ var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
|
24984
25139
|
var repoVersion = () => {
|
|
24985
25140
|
for (const f of [join62(import.meta.dir, "../../../../../VERSION")]) {
|
|
24986
25141
|
try {
|
|
24987
|
-
if (
|
|
25142
|
+
if (existsSync40(f))
|
|
24988
25143
|
return readFileSync48(f, "utf8").trim();
|
|
24989
25144
|
} catch {}
|
|
24990
25145
|
}
|
|
@@ -25167,7 +25322,7 @@ function legacyShim(opts) {
|
|
|
25167
25322
|
|
|
25168
25323
|
// src/server/app.ts
|
|
25169
25324
|
var MAX_BODY2 = 1e6;
|
|
25170
|
-
var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) =>
|
|
25325
|
+
var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join63(d, "index.html")));
|
|
25171
25326
|
function createApp(opts) {
|
|
25172
25327
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
25173
25328
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -25310,7 +25465,7 @@ function createApp(opts) {
|
|
|
25310
25465
|
return c.json({ error: "not found" }, 404);
|
|
25311
25466
|
const root = resolve15(uiDir);
|
|
25312
25467
|
const f = resolve15(root, `.${p}`);
|
|
25313
|
-
if (f.startsWith(root + sep14) &&
|
|
25468
|
+
if (f.startsWith(root + sep14) && existsSync41(f) && statSync21(f).isFile())
|
|
25314
25469
|
return new Response(Bun.file(f));
|
|
25315
25470
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
25316
25471
|
return c.json({ error: "not found" }, 404);
|