loki-mode 11.8.1 → 11.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/backlog.py +4 -3
- package/autonomy/lib/workspace.py +95 -4
- package/autonomy/loki +185 -106
- package/autonomy/trigger-server.py +282 -4
- package/bin/loki +1 -1
- package/completions/_loki +34 -1
- package/completions/loki.bash +10 -3
- package/dashboard/__init__.py +1 -1
- package/docs/ARCHITECTURE.md +0 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/COMPONENTS.md +1 -1
- package/docs/DEPLOY-PLAN.md +13 -3
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +1 -1
- package/docs/dev/architecture-reference.md +1 -1
- package/docs/sentry-intake.md +15 -1
- package/docs/v10/D61-SPEED.md +2 -0
- package/docs/v10/PROGRESS.md +13 -1
- package/loki-ts/dist/loki.js +947 -881
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/ask-tools-server.js +13 -4
- package/packages/control-plane/dist/server.js +428 -276
- package/packages/control-plane/src/server/diffstat.ts +10 -2
- package/packages/control-plane/src/server/legacy/routes-control.ts +8 -4
- package/packages/control-plane/src/server/routes/checkpoints.ts +11 -2
- package/packages/control-plane/ui/dist/assets/index-u8cd6TRQ.js +190 -0
- package/packages/control-plane/ui/dist/index.html +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-BnIg9ilA.js → AdminPage-BdH9iL2I.js} +1 -1
- package/web-app/dist/assets/{Avatar-0BTVsQ2A.js → Avatar-C-WixxR6.js} +1 -1
- package/web-app/dist/assets/{Badge-Bbq8CXgn.js → Badge-BFWpRd_L.js} +1 -1
- package/web-app/dist/assets/{Button-CKD4n0ld.js → Button-CE5vG66Y.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DJhLNLAz.js → CockpitPage-BAVn10bY.js} +1 -1
- package/web-app/dist/assets/{ComparePage-Di2akuII.js → ComparePage-DOecIGfM.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-9lhsYG-z.js → ErrorBoundary-1rNA5v7f.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CQDGXvDr.js → EvidenceReceiptPanel-DjOaWfdH.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Y_w2k9Ej.js → GitHubIssuesPanel-NNPBuRPD.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-pMSQedCv.js → GitHubPRsPanel-CL1b--tV.js} +1 -1
- package/web-app/dist/assets/{HomePage-CIAkiGdt.js → HomePage-BsLDN6MW.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BES4Pk6k.js → LoginPage-CVIv5bCS.js} +1 -1
- package/web-app/dist/assets/{MagicPage-B154fMhi.js → MagicPage-BgMBB9lK.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BcNyaiUO.js → MetricsPage-BHeJlqPC.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D28FqYxG.js → NotFoundPage-DDzwfyDK.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-C_O3u8Jx.js → ProjectPage-CuQWEYBF.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-uqdahuPA.js → ProjectsPage-OoD0RFDZ.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DLvVA9OZ.js → SettingsPage-tMFMKkK5.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-BAZP8vsm.js → ShowcasePage-DVfLoWdz.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DPYZkLWh.js → SystemSettingsPage-CThV9N4l.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BgSyb5Pd.js → TeamsPage-CV0nvXI8.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-D_Jh5VU1.js → TemplatesPage-D81EI1L8.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-4v8poDKb.js → TerminalOutput-Bj7uKRIe.js} +1 -1
- package/web-app/dist/assets/{activity-CE2b6kdw.js → activity-DoNfNkOL.js} +1 -1
- package/web-app/dist/assets/{bell-CxJDlLSR.js → bell-BWO3OU50.js} +1 -1
- package/web-app/dist/assets/{bot-C2WuK4zw.js → bot-DcvWv_zD.js} +1 -1
- package/web-app/dist/assets/{check-81epVldw.js → check-CfG09Z18.js} +1 -1
- package/web-app/dist/assets/{chevron-left-B6EXh_3C.js → chevron-left-z0fh4x2h.js} +1 -1
- package/web-app/dist/assets/{circle-alert-0iqoo-iy.js → circle-alert-C_7GlYul.js} +1 -1
- package/web-app/dist/assets/{clock-CFrBpNUp.js → clock-BsYUp070.js} +1 -1
- package/web-app/dist/assets/{cloud-BBSFhUT_.js → cloud-BVWAJ7Rv.js} +1 -1
- package/web-app/dist/assets/{code-xml-BCU2V1rq.js → code-xml-CdZT-6zu.js} +1 -1
- package/web-app/dist/assets/{database-D2hvO52K.js → database-Dbkm9t2D.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CXFA9b6T.js → dollar-sign-CfIUM5Pd.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CqTCEEfS.js → file-code-corner-CrUKhnj-.js} +1 -1
- package/web-app/dist/assets/{file-plus-DYFbkGbs.js → file-plus-Cf6u0xHY.js} +1 -1
- package/web-app/dist/assets/{globe-DpYHLobk.js → globe-DsIqaI7w.js} +1 -1
- package/web-app/dist/assets/{hammer-CZ97SEYo.js → hammer-CA14BeMp.js} +1 -1
- package/web-app/dist/assets/{index-DzswgpRo.js → index-DpJ_GJuZ.js} +3 -3
- package/web-app/dist/assets/{layers-2Tq0edx4.js → layers-Dh5Oqhrj.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BFP4kUX-.js → loader-circle-B0WHIGqt.js} +1 -1
- package/web-app/dist/assets/{lock-DkKdZH4d.js → lock-DkgfEGYF.js} +1 -1
- package/web-app/dist/assets/{package-ChOGD8yf.js → package-LzjmwD1S.js} +1 -1
- package/web-app/dist/assets/{plus-hggB1o6b.js → plus-BNjA0KSV.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DYwVLOeF.js → refresh-cw-k3IacuDX.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CUdY506q.js → rotate-ccw-BFON_xv-.js} +1 -1
- package/web-app/dist/assets/{scroll-text-Wvidz5uN.js → scroll-text-CaIlSPZB.js} +1 -1
- package/web-app/dist/assets/{server-O11QmkCN.js → server-CKFUccCG.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DXXx27uV.js → shield-alert-Q4-rPo5r.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DF8ivs3A.js → thumbs-up-BYMeh8oV.js} +1 -1
- package/web-app/dist/assets/{trash-2-LA6PW0Nf.js → trash-2-BHSUEIHw.js} +1 -1
- package/web-app/dist/assets/{trending-down-Bw_vLKHT.js → trending-down-BC_FR2VT.js} +1 -1
- package/web-app/dist/assets/{trending-up-tX8motha.js → trending-up-CmA-WmLW.js} +1 -1
- package/web-app/dist/assets/{upload-CxtTWJd2.js → upload-BxLgBa1B.js} +1 -1
- package/web-app/dist/assets/{usePolling-D-P5Iofq.js → usePolling-DKNgwt5n.js} +1 -1
- package/web-app/dist/assets/{user-dKTcuUFq.js → user-CA2DlmLc.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/memory/rag_injector.py +0 -161
- package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +0 -190
|
@@ -248,7 +248,7 @@ async function backfill(o) {
|
|
|
248
248
|
}
|
|
249
249
|
const source = sourceId(o.repoDir);
|
|
250
250
|
let runs2 = 0, sent = 0;
|
|
251
|
-
const
|
|
251
|
+
const failed2 = [];
|
|
252
252
|
for (const id of ids) {
|
|
253
253
|
const runDir = join7(root, id);
|
|
254
254
|
if (!hasSpool(runDir))
|
|
@@ -264,9 +264,9 @@ async function backfill(o) {
|
|
|
264
264
|
await sleep(backoffMs(a));
|
|
265
265
|
}
|
|
266
266
|
if (!res.ok)
|
|
267
|
-
|
|
267
|
+
failed2.push(id);
|
|
268
268
|
}
|
|
269
|
-
return { runs: runs2, sent, failed };
|
|
269
|
+
return { runs: runs2, sent, failed: failed2 };
|
|
270
270
|
}
|
|
271
271
|
var init_backfill = __esm(() => {
|
|
272
272
|
init_ship();
|
|
@@ -7326,8 +7326,8 @@ var init_paths = __esm(() => {
|
|
|
7326
7326
|
});
|
|
7327
7327
|
|
|
7328
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";
|
|
7329
|
+
import { createHash as createHash6, randomBytes as randomBytes2, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign, verify as verify2 } from "crypto";
|
|
7330
|
+
import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
7331
7331
|
import { homedir as homedir8 } from "os";
|
|
7332
7332
|
import { dirname as dirname7, join as join26 } from "path";
|
|
7333
7333
|
function canonicalJson(x) {
|
|
@@ -9185,7 +9185,7 @@ function routerMode(env = process.env) {
|
|
|
9185
9185
|
var routerEnabled = (env = process.env) => routerMode(env) === "on";
|
|
9186
9186
|
|
|
9187
9187
|
// ../../loki-ts/src/engine10/cost.ts
|
|
9188
|
-
import { readdirSync as
|
|
9188
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync22 } from "fs";
|
|
9189
9189
|
import { join as join30 } from "path";
|
|
9190
9190
|
function routerTelemetryOn(env = process.env) {
|
|
9191
9191
|
return routerEnabled(env);
|
|
@@ -9342,7 +9342,7 @@ import {
|
|
|
9342
9342
|
mkdirSync as mkdirSync9,
|
|
9343
9343
|
openSync as openSync6,
|
|
9344
9344
|
readSync as readSync5,
|
|
9345
|
-
renameSync as
|
|
9345
|
+
renameSync as renameSync5,
|
|
9346
9346
|
rmSync as rmSync6,
|
|
9347
9347
|
statSync as statSync14,
|
|
9348
9348
|
unlinkSync as unlinkSync4,
|
|
@@ -9360,7 +9360,7 @@ function durableWriteThenRename(target, body) {
|
|
|
9360
9360
|
} finally {
|
|
9361
9361
|
closeSync6(fd);
|
|
9362
9362
|
}
|
|
9363
|
-
|
|
9363
|
+
renameSync5(tmp, target);
|
|
9364
9364
|
fsyncParentDir(target);
|
|
9365
9365
|
}
|
|
9366
9366
|
function fsyncParentDir(target) {
|
|
@@ -9389,7 +9389,7 @@ var init_atomic = __esm(() => {
|
|
|
9389
9389
|
});
|
|
9390
9390
|
|
|
9391
9391
|
// ../../loki-ts/src/runner/budget.ts
|
|
9392
|
-
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as
|
|
9392
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync11, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
|
|
9393
9393
|
import { dirname as dirname11, join as join31, resolve as resolve10 } from "path";
|
|
9394
9394
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9395
9395
|
function loadDefaultMaxCostUsd() {
|
|
@@ -9979,11 +9979,99 @@ var init_scope = __esm(() => {
|
|
|
9979
9979
|
dropped = new WeakMap;
|
|
9980
9980
|
});
|
|
9981
9981
|
|
|
9982
|
+
// ../../loki-ts/src/features/runner_config.ts
|
|
9983
|
+
import { readFileSync as readFileSync27 } from "fs";
|
|
9984
|
+
import { join as join37 } from "path";
|
|
9985
|
+
function relevant(shared, text2) {
|
|
9986
|
+
if (shared === "go") {
|
|
9987
|
+
let blk = false;
|
|
9988
|
+
return lines(text2).filter((l) => {
|
|
9989
|
+
if (blk) {
|
|
9990
|
+
if (l === ")")
|
|
9991
|
+
blk = false;
|
|
9992
|
+
return true;
|
|
9993
|
+
}
|
|
9994
|
+
if (/^replace\s*\($/.test(l)) {
|
|
9995
|
+
blk = true;
|
|
9996
|
+
return true;
|
|
9997
|
+
}
|
|
9998
|
+
return /^replace\s/.test(l);
|
|
9999
|
+
}).join(`
|
|
10000
|
+
`);
|
|
10001
|
+
}
|
|
10002
|
+
if (shared === "tsconfig") {
|
|
10003
|
+
const j = jsonc(text2);
|
|
10004
|
+
const o = j.compilerOptions ?? {};
|
|
10005
|
+
return JSON.stringify(["extends", "baseUrl", "paths", "rootDirs", "moduleSuffixes", "customConditions"].map((k) => k === "extends" ? j["extends"] : o[k]));
|
|
10006
|
+
}
|
|
10007
|
+
if (Array.isArray(shared)) {
|
|
10008
|
+
const j = JSON.parse(text2);
|
|
10009
|
+
return JSON.stringify([shared.map((k) => j[k]), depGuard(j)]);
|
|
10010
|
+
}
|
|
10011
|
+
let on = false;
|
|
10012
|
+
return lines(text2).filter((l) => {
|
|
10013
|
+
if (/^\[[^\]]+\]$/.test(l))
|
|
10014
|
+
on = shared.test(l);
|
|
10015
|
+
return on || KW.test(l);
|
|
10016
|
+
}).join(`
|
|
10017
|
+
`);
|
|
10018
|
+
}
|
|
10019
|
+
function testConfigChanged(repoDir, baseSha, changed) {
|
|
10020
|
+
return changed.filter((f) => {
|
|
10021
|
+
const spec = RUNNER_CONFIGS.find((s) => s.re.test(f));
|
|
10022
|
+
if (!spec)
|
|
10023
|
+
return false;
|
|
10024
|
+
if (!spec.shared)
|
|
10025
|
+
return true;
|
|
10026
|
+
const b = baseText(repoDir, baseSha, f), h = headText(repoDir, f);
|
|
10027
|
+
if (b === h)
|
|
10028
|
+
return false;
|
|
10029
|
+
try {
|
|
10030
|
+
return relevant(spec.shared, b || "{}") !== relevant(spec.shared, h || "{}");
|
|
10031
|
+
} catch {
|
|
10032
|
+
return true;
|
|
10033
|
+
}
|
|
10034
|
+
});
|
|
10035
|
+
}
|
|
10036
|
+
var PKG_KEYS, RUNNER_CONFIGS, DEP_MAPS, RUNNER_DEP, LOCAL_SPEC, depGuard = (j) => DEP_MAPS.map((m) => Object.entries(j[m] ?? {}).filter(([k, v]) => RUNNER_DEP.test(k) || LOCAL_SPEC.test(String(v)))), KW, lines = (t) => t.split(`
|
|
10037
|
+
`).map((l) => l.trim()).filter((l) => l && !l.startsWith("#")), jsonc = (t) => JSON.parse(t.replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, "$1").replace(/,(\s*[}\]])/g, "$1")), baseText = (dir, base, f) => {
|
|
10038
|
+
try {
|
|
10039
|
+
return safeGit(dir, ["show", `${base}:./${f}`], { timeout: 20000, maxBuffer: 64 << 20 });
|
|
10040
|
+
} catch {
|
|
10041
|
+
return "";
|
|
10042
|
+
}
|
|
10043
|
+
}, headText = (dir, f) => {
|
|
10044
|
+
try {
|
|
10045
|
+
return readFileSync27(join37(dir, f), "utf8");
|
|
10046
|
+
} catch {
|
|
10047
|
+
return "";
|
|
10048
|
+
}
|
|
10049
|
+
};
|
|
10050
|
+
var init_runner_config = __esm(() => {
|
|
10051
|
+
init_safe_git();
|
|
10052
|
+
PKG_KEYS = ["scripts", "jest", "overrides", "resolutions", "imports", "pnpm", "mocha", "vitest", "ava", "c8", "nyc", "babel", "bun"];
|
|
10053
|
+
RUNNER_CONFIGS = [
|
|
10054
|
+
{ re: /(^|\/)(conftest\.py|tox\.ini|bunfig\.toml|\.?pytest\.(ini|toml)|\.mocharc[\w.]*)$/ },
|
|
10055
|
+
{ re: /(^|\/)(vitest|vite|jest)\.(config|workspace)\.[\w.]+$/ },
|
|
10056
|
+
{ re: /(^|\/)\.cargo\/config(\.toml)?$/ },
|
|
10057
|
+
{ re: /(^|\/)package\.json$/, shared: PKG_KEYS },
|
|
10058
|
+
{ re: /(^|\/)pyproject\.toml$/, shared: /^\[tool\.pytest/ },
|
|
10059
|
+
{ re: /(^|\/)setup\.cfg$/, shared: /^\[tool:pytest\]/ },
|
|
10060
|
+
{ re: /(^|\/)go\.mod$/, shared: "go" },
|
|
10061
|
+
{ re: /(^|\/)Cargo\.toml$/, shared: /^\[(patch|replace)\b/ },
|
|
10062
|
+
{ re: /(^|\/)[jt]sconfig(\.[\w-]+)?\.json$/, shared: "tsconfig" }
|
|
10063
|
+
];
|
|
10064
|
+
DEP_MAPS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
|
10065
|
+
RUNNER_DEP = /^(jest|vitest|mocha|ava|c8|nyc|pytest[\w-]*|@jest\/.*|ts-jest|babel-jest)$/;
|
|
10066
|
+
LOCAL_SPEC = /^(file|link|npm|workspace):/;
|
|
10067
|
+
KW = /pytest|jest|mocha|vitest|addopts|testpaths/i;
|
|
10068
|
+
});
|
|
10069
|
+
|
|
9982
10070
|
// ../../loki-ts/src/e10ext/assert_delta.ts
|
|
9983
10071
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
9984
|
-
import { mkdtempSync as mkdtempSync4, readFileSync as
|
|
10072
|
+
import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync28, rmSync as rmSync8 } from "fs";
|
|
9985
10073
|
import { tmpdir as tmpdir6 } from "os";
|
|
9986
|
-
import { join as
|
|
10074
|
+
import { join as join38 } from "path";
|
|
9987
10075
|
import { basename as basename11 } from "path";
|
|
9988
10076
|
function classifyAssertDelta(inp) {
|
|
9989
10077
|
const f = basename11(inp.path);
|
|
@@ -9993,7 +10081,7 @@ function classifyAssertDelta(inp) {
|
|
|
9993
10081
|
return WEAKENED;
|
|
9994
10082
|
let cwd = "";
|
|
9995
10083
|
try {
|
|
9996
|
-
cwd = mkdtempSync4(
|
|
10084
|
+
cwd = mkdtempSync4(join38(tmpdir6(), "loki-ad-"));
|
|
9997
10085
|
const raw2 = execFileSync5("python3", ["-I", "-c", PY], {
|
|
9998
10086
|
input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
|
|
9999
10087
|
cwd,
|
|
@@ -10024,7 +10112,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
|
|
|
10024
10112
|
return null;
|
|
10025
10113
|
try {
|
|
10026
10114
|
const show = (ref) => safeGit(repoDir, ["show", `${ref}:${path}`]);
|
|
10027
|
-
const head = headRef ? show(headRef) :
|
|
10115
|
+
const head = headRef ? show(headRef) : readFileSync28(join38(repoDir, path), "utf8");
|
|
10028
10116
|
const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
|
|
10029
10117
|
return r[0]?.kind === "literal-only" ? r.map((e) => `assertion value changed (not shown to be required by the task): ${e.file}:${e.line} ${e.old} -> ${e.new}`) : null;
|
|
10030
10118
|
} catch {
|
|
@@ -10214,9 +10302,9 @@ function hasRealTestFailure(out2) {
|
|
|
10214
10302
|
return /^\s*Tests:?\s+.*\b[1-9]\d* failed\b/m.test(out2) || /^(?:=+ )?.*\b[1-9]\d* failed\b.* in [\d.]+s/m.test(out2) || /^FAILED \S+::/m.test(out2) || /^(?:#|\u2139) fail [1-9]/m.test(out2) || /^\s*[1-9]\d*\s+fail\s*$/m.test(out2) || /^\s*(?:FAIL|\u00D7)\s+\S.*\s>\s/m.test(out2) || /\bAssertionError\b/.test(out2);
|
|
10215
10303
|
}
|
|
10216
10304
|
function firstLoadLine(out2) {
|
|
10217
|
-
const
|
|
10305
|
+
const lines2 = out2.split(`
|
|
10218
10306
|
`).map((l2) => l2.trim()).filter(Boolean);
|
|
10219
|
-
const l =
|
|
10307
|
+
const l = lines2.find((x) => /^(?:E\s+)?(?:Error|\w*Error|ERROR|Failed|FAIL)\b|Cannot find|Interrupted:|No test files found/.test(x)) ?? lines2[0] ?? "";
|
|
10220
10308
|
return l.replace(/\s+/g, " ").slice(0, 160);
|
|
10221
10309
|
}
|
|
10222
10310
|
function classifyRunnerOutput(out2) {
|
|
@@ -10248,7 +10336,7 @@ var init_runner_errors = __esm(() => {
|
|
|
10248
10336
|
});
|
|
10249
10337
|
|
|
10250
10338
|
// ../../loki-ts/src/engine10/testmap.ts
|
|
10251
|
-
import { basename as basename12, extname, join as
|
|
10339
|
+
import { basename as basename12, extname, join as join39, relative as relative3, sep as sep10 } from "path";
|
|
10252
10340
|
function isTestFile(rel) {
|
|
10253
10341
|
const name = basename12(rel);
|
|
10254
10342
|
return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
|
|
@@ -10278,8 +10366,8 @@ var init_testmap = __esm(() => {
|
|
|
10278
10366
|
});
|
|
10279
10367
|
|
|
10280
10368
|
// ../../loki-ts/src/util/check_result.ts
|
|
10281
|
-
import { mkdirSync as mkdirSync12, readFileSync as
|
|
10282
|
-
import { basename as basename13, join as
|
|
10369
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync29, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "fs";
|
|
10370
|
+
import { basename as basename13, join as join40 } from "path";
|
|
10283
10371
|
function plainTestEnv(base = process.env) {
|
|
10284
10372
|
const e = {};
|
|
10285
10373
|
for (const [k, v] of Object.entries(tokenFreeEnv(base)))
|
|
@@ -10355,12 +10443,12 @@ function unittestCount(out2, ok) {
|
|
|
10355
10443
|
return Math.max(0, +r[1] - +(v.match(/skipped=(\d+)/)?.[1] ?? 0) - +(v.match(/expected failures=(\d+)/)?.[1] ?? 0));
|
|
10356
10444
|
}
|
|
10357
10445
|
function goCount(out2, ok) {
|
|
10358
|
-
const
|
|
10446
|
+
const lines2 = out2.split(`
|
|
10359
10447
|
`);
|
|
10360
|
-
if (!
|
|
10448
|
+
if (!lines2.some((x) => GO_PKG_RE.test(x)))
|
|
10361
10449
|
return;
|
|
10362
|
-
const last = new Map, seen = new Set, skip = new Set, failPkg = ok !== true &&
|
|
10363
|
-
for (const x of
|
|
10450
|
+
const last = new Map, seen = new Set, skip = new Set, failPkg = ok !== true && lines2.some((x) => /^FAIL\s+\S+\s+[\d.]+s\b/.test(x));
|
|
10451
|
+
for (const x of lines2) {
|
|
10364
10452
|
const r = x.match(/^=== RUN\s+(\S+)/), m = x.match(/^--- (PASS|FAIL|SKIP): (\S+)/);
|
|
10365
10453
|
if (r)
|
|
10366
10454
|
seen.add(r[1]);
|
|
@@ -10377,9 +10465,9 @@ function goCount(out2, ok) {
|
|
|
10377
10465
|
const t = [...last.values()].filter((v) => v !== "SKIP").length;
|
|
10378
10466
|
if (t > 0)
|
|
10379
10467
|
return t;
|
|
10380
|
-
if (
|
|
10468
|
+
if (lines2.some((x) => /^(?:=== RUN|PASS$|FAIL$|testing: warning: no tests to run)/.test(x)))
|
|
10381
10469
|
return 0;
|
|
10382
|
-
return
|
|
10470
|
+
return lines2.filter((x) => GO_PKG_RE.test(x)).every((x) => /\[no tests? (?:files|to run)\]/.test(x)) ? 0 : null;
|
|
10383
10471
|
}
|
|
10384
10472
|
function goRunner(cmd, args) {
|
|
10385
10473
|
const segs = args ? [[cmd, ...args]] : cmd.split(/&&|\|\||[;|\n]/).map((x) => x.trim().split(/\s+/).filter(Boolean));
|
|
@@ -10403,7 +10491,7 @@ function goRunner(cmd, args) {
|
|
|
10403
10491
|
return {};
|
|
10404
10492
|
}
|
|
10405
10493
|
function goJson(out2) {
|
|
10406
|
-
let any = false,
|
|
10494
|
+
let any = false, failed2 = false;
|
|
10407
10495
|
for (const x of out2.split(`
|
|
10408
10496
|
`)) {
|
|
10409
10497
|
if (!x.trim() || /^go: /.test(x))
|
|
@@ -10418,9 +10506,9 @@ function goJson(out2) {
|
|
|
10418
10506
|
return null;
|
|
10419
10507
|
any = true;
|
|
10420
10508
|
if (e.Action === "fail")
|
|
10421
|
-
|
|
10509
|
+
failed2 = true;
|
|
10422
10510
|
}
|
|
10423
|
-
return any ? { failed } : null;
|
|
10511
|
+
return any ? { failed: failed2 } : null;
|
|
10424
10512
|
}
|
|
10425
10513
|
function skipped(raw2) {
|
|
10426
10514
|
return stripAnsi(raw2).split(`
|
|
@@ -10488,7 +10576,7 @@ function markExecuted(a, executed) {
|
|
|
10488
10576
|
return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) };
|
|
10489
10577
|
}
|
|
10490
10578
|
function classifyRisk(files, env = process.env) {
|
|
10491
|
-
const
|
|
10579
|
+
const lines2 = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
|
|
10492
10580
|
const sensitive = files.flatMap((f) => {
|
|
10493
10581
|
const hit = SENSITIVE.find(([, re]) => re.test(f.path));
|
|
10494
10582
|
return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
|
|
@@ -10506,14 +10594,14 @@ function classifyRisk(files, env = process.env) {
|
|
|
10506
10594
|
high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
|
|
10507
10595
|
if (files.length >= FILES_HIGH)
|
|
10508
10596
|
high.push(`${files.length} files >= ${FILES_HIGH}`);
|
|
10509
|
-
if (
|
|
10510
|
-
high.push(`${
|
|
10597
|
+
if (lines2 >= LINES_HIGH)
|
|
10598
|
+
high.push(`${lines2} changed lines >= ${LINES_HIGH}`);
|
|
10511
10599
|
if (testsModified.length)
|
|
10512
10600
|
med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
|
|
10513
10601
|
if (files.length >= FILES_MEDIUM)
|
|
10514
10602
|
med.push(`${files.length} files >= ${FILES_MEDIUM}`);
|
|
10515
|
-
if (
|
|
10516
|
-
med.push(`${
|
|
10603
|
+
if (lines2 >= LINES_MEDIUM)
|
|
10604
|
+
med.push(`${lines2} changed lines >= ${LINES_MEDIUM}`);
|
|
10517
10605
|
const computed = high.length ? "high" : med.length ? "medium" : "low";
|
|
10518
10606
|
const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
|
|
10519
10607
|
let tier = computed, override, ignored;
|
|
@@ -10536,7 +10624,7 @@ function classifyRisk(files, env = process.env) {
|
|
|
10536
10624
|
tier,
|
|
10537
10625
|
computed,
|
|
10538
10626
|
reasons,
|
|
10539
|
-
stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10627
|
+
stats: { files: files.length, lines: lines2, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10540
10628
|
checks_selected: plan.selected,
|
|
10541
10629
|
checks_deferred,
|
|
10542
10630
|
...override ? { override } : {},
|
|
@@ -10571,7 +10659,7 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10571
10659
|
return f;
|
|
10572
10660
|
let n2 = 0;
|
|
10573
10661
|
try {
|
|
10574
|
-
n2 =
|
|
10662
|
+
n2 = readFileSync29(join40(repoDir, path), "utf8").split(`
|
|
10575
10663
|
`).length;
|
|
10576
10664
|
} catch {}
|
|
10577
10665
|
return { path, added: n2, deleted: 0, status: "added" };
|
|
@@ -10579,11 +10667,11 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10579
10667
|
}
|
|
10580
10668
|
function recordRisk(repoDir, a) {
|
|
10581
10669
|
try {
|
|
10582
|
-
const dir =
|
|
10670
|
+
const dir = join40(repoDir, ".loki", "state"), p = join40(dir, "risk-tier.json");
|
|
10583
10671
|
mkdirSync12(dir, { recursive: true });
|
|
10584
10672
|
writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
|
|
10585
10673
|
`);
|
|
10586
|
-
|
|
10674
|
+
renameSync6(`${p}.tmp`, p);
|
|
10587
10675
|
return p;
|
|
10588
10676
|
} catch {
|
|
10589
10677
|
return null;
|
|
@@ -10624,10 +10712,10 @@ var init_check_result = __esm(() => {
|
|
|
10624
10712
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10625
10713
|
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
|
|
10626
10714
|
import { tmpdir as tmpdir7 } from "os";
|
|
10627
|
-
import { basename as basename14, extname as extname2, join as
|
|
10715
|
+
import { basename as basename14, extname as extname2, join as join41 } from "path";
|
|
10628
10716
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
10629
10717
|
return changed.filter((f) => {
|
|
10630
|
-
if (out2.includes(f) || out2.includes(
|
|
10718
|
+
if (out2.includes(f) || out2.includes(join41(repoDir, f)))
|
|
10631
10719
|
return true;
|
|
10632
10720
|
const segs = f.split("/");
|
|
10633
10721
|
if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
|
|
@@ -10738,7 +10826,7 @@ async function loadErrorIsHarnessOwned(i) {
|
|
|
10738
10826
|
return false;
|
|
10739
10827
|
if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
|
|
10740
10828
|
return false;
|
|
10741
|
-
const dir = mkdtempSync5(
|
|
10829
|
+
const dir = mkdtempSync5(join41(tmpdir7(), "e10-loadown-"));
|
|
10742
10830
|
const git3 = (args) => {
|
|
10743
10831
|
safeGit(i.repoDir, args, { stdio: "ignore" });
|
|
10744
10832
|
};
|
|
@@ -10770,7 +10858,7 @@ var init_load_owner = __esm(() => {
|
|
|
10770
10858
|
});
|
|
10771
10859
|
|
|
10772
10860
|
// ../../loki-ts/src/util/wall_owned.ts
|
|
10773
|
-
import { isAbsolute as isAbsolute4, join as
|
|
10861
|
+
import { isAbsolute as isAbsolute4, join as join42, relative as relative4 } from "path";
|
|
10774
10862
|
function failingLocations(raw2, cwd, repoDir) {
|
|
10775
10863
|
const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
|
|
10776
10864
|
`), files = new Set;
|
|
@@ -10802,7 +10890,7 @@ function failingLocations(raw2, cwd, repoDir) {
|
|
|
10802
10890
|
}
|
|
10803
10891
|
if (errors2 === 0 || located !== errors2)
|
|
10804
10892
|
return null;
|
|
10805
|
-
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f :
|
|
10893
|
+
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join42(cwd, f)));
|
|
10806
10894
|
}
|
|
10807
10895
|
function wallOwnedFailure(raw2, cwd, repoDir, wall) {
|
|
10808
10896
|
const locs = failingLocations(raw2, cwd, repoDir);
|
|
@@ -10831,11 +10919,11 @@ var init_state = __esm(() => {
|
|
|
10831
10919
|
});
|
|
10832
10920
|
|
|
10833
10921
|
// ../../loki-ts/src/runner/repo_profile.ts
|
|
10834
|
-
import { existsSync as existsSync22, readFileSync as
|
|
10835
|
-
import { basename as basename15, join as
|
|
10922
|
+
import { existsSync as existsSync22, readFileSync as readFileSync30, mkdirSync as mkdirSync13, readdirSync as readdirSync12, renameSync as renameSync7, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
|
|
10923
|
+
import { basename as basename15, join as join43, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
|
|
10836
10924
|
function loadProfile(repoDir) {
|
|
10837
10925
|
try {
|
|
10838
|
-
const p = JSON.parse(
|
|
10926
|
+
const p = JSON.parse(readFileSync30(commandProfilePath(repoDir), "utf8"));
|
|
10839
10927
|
return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
|
|
10840
10928
|
} catch {
|
|
10841
10929
|
return null;
|
|
@@ -10866,7 +10954,7 @@ function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
|
|
|
10866
10954
|
const block = new Set(blocked);
|
|
10867
10955
|
return attribute(repoDir, baseSha, bad.map((c) => ({ name: c.name, ...c.ids ? { ids: c.ids } : {}, eligible: (c.ids ?? []).every((i) => !block.has(i) && !changed.some((f) => i.includes(f))) })));
|
|
10868
10956
|
}
|
|
10869
|
-
var commandProfilePath = (repoDir) =>
|
|
10957
|
+
var commandProfilePath = (repoDir) => join43(repoDir, ".loki", "state", "repo-profile.json");
|
|
10870
10958
|
var init_repo_profile = __esm(() => {
|
|
10871
10959
|
init_failures();
|
|
10872
10960
|
init_check_result();
|
|
@@ -10879,20 +10967,20 @@ var init_repo_profile = __esm(() => {
|
|
|
10879
10967
|
import { createHash as createHash10 } from "crypto";
|
|
10880
10968
|
import {
|
|
10881
10969
|
existsSync as existsSync23,
|
|
10882
|
-
readdirSync as
|
|
10883
|
-
readFileSync as
|
|
10970
|
+
readdirSync as readdirSync13,
|
|
10971
|
+
readFileSync as readFileSync31,
|
|
10884
10972
|
statSync as statSync16,
|
|
10885
10973
|
writeFileSync as writeFileSync17,
|
|
10886
10974
|
mkdirSync as mkdirSync14
|
|
10887
10975
|
} from "fs";
|
|
10888
|
-
import { basename as basename16, dirname as dirname13, join as
|
|
10976
|
+
import { basename as basename16, dirname as dirname13, join as join44, resolve as resolve12 } from "path";
|
|
10889
10977
|
function readManifest(path) {
|
|
10890
10978
|
if (!existsSync23(path)) {
|
|
10891
10979
|
return null;
|
|
10892
10980
|
}
|
|
10893
10981
|
let raw2;
|
|
10894
10982
|
try {
|
|
10895
|
-
raw2 =
|
|
10983
|
+
raw2 = readFileSync31(path, "utf8");
|
|
10896
10984
|
} catch {
|
|
10897
10985
|
return null;
|
|
10898
10986
|
}
|
|
@@ -10929,7 +11017,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10929
11017
|
const seen = new Set;
|
|
10930
11018
|
let children;
|
|
10931
11019
|
try {
|
|
10932
|
-
children =
|
|
11020
|
+
children = readdirSync13(rootDir);
|
|
10933
11021
|
} catch {
|
|
10934
11022
|
children = [];
|
|
10935
11023
|
}
|
|
@@ -10944,7 +11032,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10944
11032
|
for (const child of children) {
|
|
10945
11033
|
if (!matchPattern(name, child))
|
|
10946
11034
|
continue;
|
|
10947
|
-
const full =
|
|
11035
|
+
const full = join44(rootDir, child);
|
|
10948
11036
|
if (seen.has(full))
|
|
10949
11037
|
continue;
|
|
10950
11038
|
let st;
|
|
@@ -10961,7 +11049,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10961
11049
|
}
|
|
10962
11050
|
if (declared.length === 0) {
|
|
10963
11051
|
for (const child of children) {
|
|
10964
|
-
const full =
|
|
11052
|
+
const full = join44(rootDir, child);
|
|
10965
11053
|
if (seen.has(full))
|
|
10966
11054
|
continue;
|
|
10967
11055
|
let st;
|
|
@@ -10972,7 +11060,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10972
11060
|
}
|
|
10973
11061
|
if (!st.isDirectory())
|
|
10974
11062
|
continue;
|
|
10975
|
-
if (!existsSync23(
|
|
11063
|
+
if (!existsSync23(join44(full, ".loki", "app.json")))
|
|
10976
11064
|
continue;
|
|
10977
11065
|
seen.add(full);
|
|
10978
11066
|
out2.push(full);
|
|
@@ -10993,17 +11081,17 @@ function cacheKey(manifestPaths) {
|
|
|
10993
11081
|
}
|
|
10994
11082
|
function writeCache(targetDir, key, result) {
|
|
10995
11083
|
try {
|
|
10996
|
-
const cacheDir =
|
|
11084
|
+
const cacheDir = join44(targetDir, ".loki", "state");
|
|
10997
11085
|
mkdirSync14(cacheDir, { recursive: true });
|
|
10998
|
-
const cachePath =
|
|
11086
|
+
const cachePath = join44(cacheDir, "project-graph.json");
|
|
10999
11087
|
writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
|
|
11000
11088
|
} catch {}
|
|
11001
11089
|
}
|
|
11002
11090
|
function discoverProjectGraph(targetDir) {
|
|
11003
11091
|
const absTarget = resolve12(targetDir);
|
|
11004
|
-
const targetManifest =
|
|
11092
|
+
const targetManifest = join44(absTarget, ".loki", "app.json");
|
|
11005
11093
|
const parentDir = dirname13(absTarget);
|
|
11006
|
-
const parentManifest =
|
|
11094
|
+
const parentManifest = join44(parentDir, ".loki", "app.json");
|
|
11007
11095
|
const examined = [];
|
|
11008
11096
|
let chosenManifest = null;
|
|
11009
11097
|
let chosenRoot = "";
|
|
@@ -11023,7 +11111,7 @@ function discoverProjectGraph(targetDir) {
|
|
|
11023
11111
|
const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
|
|
11024
11112
|
const filtered = [];
|
|
11025
11113
|
for (const m of members) {
|
|
11026
|
-
const sibManifestPath =
|
|
11114
|
+
const sibManifestPath = join44(m, ".loki", "app.json");
|
|
11027
11115
|
if (existsSync23(sibManifestPath)) {
|
|
11028
11116
|
examined.push(sibManifestPath);
|
|
11029
11117
|
const sibManifest = readManifest(sibManifestPath);
|
|
@@ -11096,15 +11184,15 @@ var init_steer = __esm(() => {
|
|
|
11096
11184
|
});
|
|
11097
11185
|
|
|
11098
11186
|
// ../../loki-ts/src/util/pr_lessons.ts
|
|
11099
|
-
import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as
|
|
11100
|
-
import { dirname as dirname14, join as
|
|
11187
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
|
|
11188
|
+
import { dirname as dirname14, join as join45 } from "path";
|
|
11101
11189
|
import { createHash as createHash11 } from "crypto";
|
|
11102
11190
|
function loadLessons(repoDir) {
|
|
11103
11191
|
const p = lessonsPath(repoDir);
|
|
11104
11192
|
if (!existsSync24(p))
|
|
11105
11193
|
return [];
|
|
11106
11194
|
try {
|
|
11107
|
-
const j = JSON.parse(
|
|
11195
|
+
const j = JSON.parse(readFileSync32(p, "utf-8"));
|
|
11108
11196
|
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
11109
11197
|
} catch {
|
|
11110
11198
|
return [];
|
|
@@ -11146,7 +11234,7 @@ function recordRunVerdict(repoDir, runId, verdict) {
|
|
|
11146
11234
|
if (dirty)
|
|
11147
11235
|
save(repoDir, lessons);
|
|
11148
11236
|
}
|
|
11149
|
-
var lessonsPath = (repoDir) =>
|
|
11237
|
+
var lessonsPath = (repoDir) => join45(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
|
|
11150
11238
|
var init_pr_lessons = __esm(() => {
|
|
11151
11239
|
init_atomic();
|
|
11152
11240
|
KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
|
|
@@ -11162,7 +11250,7 @@ var init_model_downgrades = __esm(() => {
|
|
|
11162
11250
|
var init_mcp_config = () => {};
|
|
11163
11251
|
|
|
11164
11252
|
// ../../loki-ts/src/providers/claude_flags.ts
|
|
11165
|
-
import { existsSync as existsSync25, readFileSync as
|
|
11253
|
+
import { existsSync as existsSync25, readFileSync as readFileSync33 } from "fs";
|
|
11166
11254
|
import { resolve as resolvePath, dirname as dirname15 } from "path";
|
|
11167
11255
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11168
11256
|
function designArchetypesText() {
|
|
@@ -11174,7 +11262,7 @@ function designArchetypesText() {
|
|
|
11174
11262
|
]) {
|
|
11175
11263
|
const p = resolvePath(moduleDir, ...rel);
|
|
11176
11264
|
if (existsSync25(p))
|
|
11177
|
-
return
|
|
11265
|
+
return readFileSync33(p, "utf8");
|
|
11178
11266
|
}
|
|
11179
11267
|
} catch {}
|
|
11180
11268
|
return "";
|
|
@@ -11252,14 +11340,14 @@ var init_providers = __esm(() => {
|
|
|
11252
11340
|
});
|
|
11253
11341
|
|
|
11254
11342
|
// ../../loki-ts/src/runner/router/route_record.ts
|
|
11255
|
-
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as
|
|
11256
|
-
import { join as
|
|
11343
|
+
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
|
|
11344
|
+
import { join as join46 } from "path";
|
|
11257
11345
|
function loadRouteRecord(runDir) {
|
|
11258
11346
|
try {
|
|
11259
|
-
const p =
|
|
11347
|
+
const p = join46(runDir, ROUTE_RECORD_FILE);
|
|
11260
11348
|
if (!existsSync26(p) || !lstatSync9(p).isFile())
|
|
11261
11349
|
return;
|
|
11262
|
-
const v = JSON.parse(
|
|
11350
|
+
const v = JSON.parse(readFileSync34(p, "utf8"));
|
|
11263
11351
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
11264
11352
|
} catch {
|
|
11265
11353
|
return;
|
|
@@ -11269,7 +11357,7 @@ var ROUTE_RECORD_FILE = "route.json";
|
|
|
11269
11357
|
var init_route_record = () => {};
|
|
11270
11358
|
|
|
11271
11359
|
// ../../loki-ts/src/runner/router/history_store.ts
|
|
11272
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as
|
|
11360
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync35, renameSync as renameSync8, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
|
|
11273
11361
|
import { resolve as resolve13 } from "path";
|
|
11274
11362
|
function validRun(v) {
|
|
11275
11363
|
if (typeof v !== "object" || v === null)
|
|
@@ -11285,7 +11373,7 @@ function readRunHistory(key, cacheRoot) {
|
|
|
11285
11373
|
if (!existsSync27(path))
|
|
11286
11374
|
return [];
|
|
11287
11375
|
try {
|
|
11288
|
-
const parsed = JSON.parse(
|
|
11376
|
+
const parsed = JSON.parse(readFileSync35(path, "utf8"));
|
|
11289
11377
|
const runs2 = parsed?.runs;
|
|
11290
11378
|
return Array.isArray(runs2) ? runs2.filter(validRun) : [];
|
|
11291
11379
|
} catch {
|
|
@@ -11300,7 +11388,7 @@ function appendRunOutcome(key, outcome, cacheRoot) {
|
|
|
11300
11388
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
11301
11389
|
try {
|
|
11302
11390
|
writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
|
|
11303
|
-
|
|
11391
|
+
renameSync8(tmp, target);
|
|
11304
11392
|
} catch (e) {
|
|
11305
11393
|
rmSync11(tmp, { force: true, recursive: false });
|
|
11306
11394
|
throw e;
|
|
@@ -11319,7 +11407,7 @@ var init_history_store = __esm(() => {
|
|
|
11319
11407
|
});
|
|
11320
11408
|
|
|
11321
11409
|
// ../../loki-ts/src/runner/router/history.ts
|
|
11322
|
-
import { dirname as dirname16, join as
|
|
11410
|
+
import { dirname as dirname16, join as join47, resolve as resolve14 } from "path";
|
|
11323
11411
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11324
11412
|
function shapeKey(model) {
|
|
11325
11413
|
if (!model || model.status !== "ok")
|
|
@@ -11479,7 +11567,7 @@ var init_worker = __esm(() => {
|
|
|
11479
11567
|
});
|
|
11480
11568
|
|
|
11481
11569
|
// ../../loki-ts/src/project_model/package_suite.ts
|
|
11482
|
-
import { join as
|
|
11570
|
+
import { join as join48 } from "path";
|
|
11483
11571
|
async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
11484
11572
|
const api = loadProjectApi(repoDir);
|
|
11485
11573
|
if (!isMultiRoot(api))
|
|
@@ -11490,7 +11578,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11490
11578
|
for (const pkg of withTests) {
|
|
11491
11579
|
const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
|
|
11492
11580
|
const started = Date.now();
|
|
11493
|
-
const r = await run(["bash", "-c", tc.cmd], { cwd:
|
|
11581
|
+
const r = await run(["bash", "-c", tc.cmd], { cwd: join48(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11494
11582
|
const duration_s = (Date.now() - started) / 1000;
|
|
11495
11583
|
if (signal.aborted) {
|
|
11496
11584
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
@@ -11499,7 +11587,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11499
11587
|
}
|
|
11500
11588
|
const out2 = `${r.stdout}
|
|
11501
11589
|
${r.stderr}`;
|
|
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:
|
|
11590
|
+
const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join48(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11503
11591
|
if (lr) {
|
|
11504
11592
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
11505
11593
|
notProven.add(`${lr} (${name}; harness-owned)`);
|
|
@@ -11533,7 +11621,7 @@ __export(exports_deep, {
|
|
|
11533
11621
|
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
|
|
11534
11622
|
});
|
|
11535
11623
|
import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
|
|
11536
|
-
import { join as
|
|
11624
|
+
import { join as join49 } from "path";
|
|
11537
11625
|
async function runFullSuite(ctx, signal, opts, checks, notProven) {
|
|
11538
11626
|
if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
|
|
11539
11627
|
return;
|
|
@@ -11630,7 +11718,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
|
11630
11718
|
}
|
|
11631
11719
|
const inline = 'script="$1"; shift; . "$script" || exit 3; hit=0; for f in "$@"; do if _commit_scan_secret_file "$f" || _commit_path_looks_secret "$f"; then printf "%s\\n" "$f"; hit=1; fi; done; exit "$hit"';
|
|
11632
11720
|
const started = Date.now();
|
|
11633
|
-
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) =>
|
|
11721
|
+
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join49(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
|
|
11634
11722
|
const durationS = (Date.now() - started) / 1000;
|
|
11635
11723
|
if (r.exitCode === 3) {
|
|
11636
11724
|
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
|
|
@@ -11651,10 +11739,10 @@ function commentArgv(prNumber, bodyFile) {
|
|
|
11651
11739
|
return ["comment", prNumber, bodyFile];
|
|
11652
11740
|
}
|
|
11653
11741
|
function buildCommentBody(checks, notProven, statusState) {
|
|
11654
|
-
const
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
return `${
|
|
11742
|
+
const lines2 = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
|
|
11743
|
+
lines2.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
|
|
11744
|
+
lines2.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
|
|
11745
|
+
return `${lines2.join(`
|
|
11658
11746
|
`)}
|
|
11659
11747
|
`;
|
|
11660
11748
|
}
|
|
@@ -11671,7 +11759,7 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
|
|
|
11671
11759
|
const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
|
|
11672
11760
|
if (prNumber) {
|
|
11673
11761
|
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11674
|
-
const bodyFile =
|
|
11762
|
+
const bodyFile = join49(ctx.runDir, "deep-comment.md");
|
|
11675
11763
|
writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
|
|
11676
11764
|
const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
11677
11765
|
if (commentResult.exitCode !== 0)
|
|
@@ -11725,7 +11813,7 @@ async function runDeep(ctx, signal, opts = {}) {
|
|
|
11725
11813
|
const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
|
|
11726
11814
|
const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
11727
11815
|
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11728
|
-
writeFileSync20(
|
|
11816
|
+
writeFileSync20(join49(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
|
|
11729
11817
|
`);
|
|
11730
11818
|
const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
|
|
11731
11819
|
const allNotProven = [...notProvenList, ...pushNotProven];
|
|
@@ -11770,7 +11858,7 @@ var init_deep = __esm(() => {
|
|
|
11770
11858
|
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
11771
11859
|
import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
|
|
11772
11860
|
import { tmpdir as tmpdir8 } from "os";
|
|
11773
|
-
import { dirname as dirname17, isAbsolute as isAbsolute6, join as
|
|
11861
|
+
import { dirname as dirname17, isAbsolute as isAbsolute6, join as join50, relative as relative5 } from "path";
|
|
11774
11862
|
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
11775
11863
|
const inRepo = (p) => {
|
|
11776
11864
|
try {
|
|
@@ -11781,7 +11869,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
|
11781
11869
|
}
|
|
11782
11870
|
};
|
|
11783
11871
|
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
11784
|
-
const project = [
|
|
11872
|
+
const project = [join50(repoDir, ".venv", "bin", bin), join50(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join50(venvEnv, "bin", bin)] : []].find(existsSync29);
|
|
11785
11873
|
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
11786
11874
|
}
|
|
11787
11875
|
function runnerCmd(t, repoDir) {
|
|
@@ -11811,13 +11899,6 @@ function commandFor(t, repoDir, api) {
|
|
|
11811
11899
|
const [cmd, args, interpreter] = runnerCmd(site ? { runner: t.runner, path: site.file } : t, repoDir);
|
|
11812
11900
|
return { cwd: site?.cwd ?? repoDir, cmd, args, argv: [cmd, ...args], ...interpreter ? { interpreter } : {}, pkgRoot: site?.root ?? "." };
|
|
11813
11901
|
}
|
|
11814
|
-
function testConfigChanged(repoDir, baseSha, changed) {
|
|
11815
|
-
const diff = (f) => safeGit(repoDir, ["diff", "-U0", baseSha, "--", f]);
|
|
11816
|
-
return changed.filter((f) => CFG_ALWAYS.test(f) || CFG_SHARED.test(f) && (() => {
|
|
11817
|
-
const d = diff(f);
|
|
11818
|
-
return !d || CFG_LINE.test(d);
|
|
11819
|
-
})());
|
|
11820
|
-
}
|
|
11821
11902
|
async function runOnce(cmd, args, cwd, signal, opts) {
|
|
11822
11903
|
if (!Bun.which(cmd, { PATH: opts.path ?? process.env["PATH"] ?? "" }))
|
|
11823
11904
|
return { ok: false, missing: true, cut: false, out: "" };
|
|
@@ -11856,8 +11937,8 @@ function firstError(out2) {
|
|
|
11856
11937
|
return l2;
|
|
11857
11938
|
}
|
|
11858
11939
|
};
|
|
11859
|
-
const
|
|
11860
|
-
`).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l =
|
|
11940
|
+
const lines2 = out2.slice(-65536).split(`
|
|
11941
|
+
`).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l = lines2.find((x) => /^(FAILED\s|\u25cf\s.*\u203a|not ok\s|_{3,}\s.+\s_{3,}$)/.test(x)) ?? lines2.find((x) => /^[\w./-]+\.go:\d+:\d+: \S/.test(x)) ?? lines2.find((x) => /fail|error/i.test(x) && !/^(=|\u2713|ok\b|PASS)/.test(x)) ?? "";
|
|
11861
11942
|
return l.replace(/\d{4}-\d\d-\d\dT[\d:.]+Z?/g, "").replace(/(^|\s)\/(?:[\w.@-]+\/)*[\w.@-]+/g, "$1<path>").replace(/:\d+(?::\d+)?/g, "").replace(/\[?\d+(?:\.\d+)?m?s\]?/g, "").replace(/\s+/g, " ").slice(0, 160);
|
|
11862
11943
|
}
|
|
11863
11944
|
async function runCheck(ctx, name, cmd, args, signal, checks, opts = {}) {
|
|
@@ -11899,7 +11980,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11899
11980
|
const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
|
|
11900
11981
|
if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
|
|
11901
11982
|
return out2;
|
|
11902
|
-
const dir = mkdtempSync6(
|
|
11983
|
+
const dir = mkdtempSync6(join50(tmpdir8(), "e10-base-"));
|
|
11903
11984
|
const git3 = (args) => {
|
|
11904
11985
|
safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
|
|
11905
11986
|
};
|
|
@@ -11934,7 +12015,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11934
12015
|
return out2;
|
|
11935
12016
|
}
|
|
11936
12017
|
function isLokiModeRepo(repoDir) {
|
|
11937
|
-
return existsSync29(
|
|
12018
|
+
return existsSync29(join50(repoDir, "scripts", "select-tests.sh"));
|
|
11938
12019
|
}
|
|
11939
12020
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
11940
12021
|
const { api, ...runOpts } = opts;
|
|
@@ -11951,15 +12032,16 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
11951
12032
|
}
|
|
11952
12033
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
11953
12034
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
11954
|
-
if (existsSync29(
|
|
12035
|
+
if (existsSync29(join50(g.cwd, "tsconfig.json")))
|
|
11955
12036
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
11956
|
-
if (ESLINT_CONFIGS.some((f) => existsSync29(
|
|
12037
|
+
if (ESLINT_CONFIGS.some((f) => existsSync29(join50(g.cwd, f))))
|
|
11957
12038
|
await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
11958
12039
|
}
|
|
11959
12040
|
}
|
|
11960
|
-
var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT,
|
|
12041
|
+
var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT, ESLINT_CONFIGS, verifyStage;
|
|
11961
12042
|
var init_verify = __esm(() => {
|
|
11962
12043
|
init_scope();
|
|
12044
|
+
init_runner_config();
|
|
11963
12045
|
init_assert_delta();
|
|
11964
12046
|
init_failures();
|
|
11965
12047
|
init_load_owner();
|
|
@@ -11973,10 +12055,8 @@ var init_verify = __esm(() => {
|
|
|
11973
12055
|
init_repo_profile();
|
|
11974
12056
|
init_check_result();
|
|
11975
12057
|
init_check_result();
|
|
12058
|
+
init_runner_config();
|
|
11976
12059
|
CODE_EXT = /\.(?:[cm]?[jt]sx?|py|go)$/;
|
|
11977
|
-
CFG_ALWAYS = /(^|\/)(conftest\.py|\.?pytest\.(?:ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+|\.mocharc[\w.]*)$/;
|
|
11978
|
-
CFG_SHARED = /(^|\/)(setup\.cfg|pyproject\.toml|package\.json)$/;
|
|
11979
|
-
CFG_LINE = /^[+-].*(pytest|jest|mocha|vitest|"test"\s*:|addopts|testpaths)/im;
|
|
11980
12060
|
ESLINT_CONFIGS = [".eslintrc", ".eslintrc.json", ".eslintrc.js", ".eslintrc.cjs", "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"];
|
|
11981
12061
|
verifyStage = {
|
|
11982
12062
|
name: "verify",
|
|
@@ -12013,8 +12093,8 @@ var init_verify = __esm(() => {
|
|
|
12013
12093
|
const wall = ctx.outputs().wall ?? {};
|
|
12014
12094
|
const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute6(f.path) ? relative5(ctx.repoDir, f.path) : f.path));
|
|
12015
12095
|
const wallTests = map.tests.filter((t) => wallPaths.has(t.path));
|
|
12016
|
-
const intake = ctx.outputs().intake,
|
|
12017
|
-
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...
|
|
12096
|
+
const intake = ctx.outputs().intake, relevant2 = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
|
|
12097
|
+
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant2]);
|
|
12018
12098
|
const scopedOut = scopedOutOf(map);
|
|
12019
12099
|
if (scopedOut.length)
|
|
12020
12100
|
ctx.emit("test.scoped_out", "verify", { tests: scopedOut });
|
|
@@ -12022,10 +12102,10 @@ var init_verify = __esm(() => {
|
|
|
12022
12102
|
if (signal.aborted)
|
|
12023
12103
|
break;
|
|
12024
12104
|
const tc = commandFor(t, ctx.repoDir, api);
|
|
12025
|
-
await runCheck(ctx, `${t.runner}:${t.path}`, tc.cmd, tc.args, signal, checks, { ...tc.interpreter ? { interpreter: tc.interpreter } : {}, protect: [...wallTests, ...
|
|
12105
|
+
await runCheck(ctx, `${t.runner}:${t.path}`, tc.cmd, tc.args, signal, checks, { ...tc.interpreter ? { interpreter: tc.interpreter } : {}, protect: [...wallTests, ...relevant2].includes(t), ...tc.pkgRoot !== "." ? { cwd: tc.cwd } : {} });
|
|
12026
12106
|
}
|
|
12027
|
-
const targetNames = new Set([...wallTests, ...
|
|
12028
|
-
const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(
|
|
12107
|
+
const targetNames = new Set([...wallTests, ...relevant2].map((t) => `${t.runner}:${t.path}`));
|
|
12108
|
+
const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(relevant2.map((t) => `${t.runner}:${t.path}`)), signal);
|
|
12029
12109
|
const attr = attribute(ctx.repoDir, ctx.baseSha, checks.filter((c) => c.result === "fail" && !preRed.names.includes(c.name)).map((c) => ({ name: c.name, ids: c.ids, eligible: !targetNames.has(c.name) && !changed.includes(c.name.slice(c.name.indexOf(":") + 1)) })));
|
|
12030
12110
|
preRed.names.push(...attr.preExistingChecks);
|
|
12031
12111
|
preRed.ids.push(...attr.preExistingIds);
|
|
@@ -12071,7 +12151,7 @@ var init_verify = __esm(() => {
|
|
|
12071
12151
|
}
|
|
12072
12152
|
};
|
|
12073
12153
|
const testCounts = {};
|
|
12074
|
-
const modifiedRel =
|
|
12154
|
+
const modifiedRel = relevant2.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
|
|
12075
12155
|
const k = preRed.cnt[`${t.runner}:${t.path}`];
|
|
12076
12156
|
if (k)
|
|
12077
12157
|
testCounts[t.path] = k;
|
|
@@ -12085,8 +12165,8 @@ var init_verify = __esm(() => {
|
|
|
12085
12165
|
});
|
|
12086
12166
|
|
|
12087
12167
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
12088
|
-
import { existsSync as existsSync30, readdirSync as
|
|
12089
|
-
import { dirname as dirname18, join as
|
|
12168
|
+
import { existsSync as existsSync30, readdirSync as readdirSync14, readFileSync as readFileSync36 } from "fs";
|
|
12169
|
+
import { dirname as dirname18, join as join51 } from "path";
|
|
12090
12170
|
function parseJsonc(text2) {
|
|
12091
12171
|
let out2 = "", i = 0;
|
|
12092
12172
|
while (i < text2.length) {
|
|
@@ -12121,25 +12201,25 @@ function moduleOf(file, depth = 0) {
|
|
|
12121
12201
|
const ext = typeof j.extends === "string" ? j.extends : null;
|
|
12122
12202
|
if (!ext || !ext.startsWith("."))
|
|
12123
12203
|
return null;
|
|
12124
|
-
const next =
|
|
12204
|
+
const next = join51(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
12125
12205
|
return existsSync30(next) ? moduleOf(next, depth + 1) : null;
|
|
12126
12206
|
}
|
|
12127
12207
|
function readPackageConventions(pkgDir, runner = null) {
|
|
12128
12208
|
let names = [];
|
|
12129
12209
|
try {
|
|
12130
|
-
names =
|
|
12210
|
+
names = readdirSync14(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
|
|
12131
12211
|
} catch {
|
|
12132
12212
|
return null;
|
|
12133
12213
|
}
|
|
12134
12214
|
let packageType = null, tsModule = null;
|
|
12135
12215
|
for (const n2 of names) {
|
|
12136
|
-
const j = readJson4(
|
|
12216
|
+
const j = readJson4(join51(pkgDir, n2));
|
|
12137
12217
|
if (!j)
|
|
12138
12218
|
continue;
|
|
12139
12219
|
if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
|
|
12140
12220
|
packageType = j.type;
|
|
12141
12221
|
if (tsModule === null && j.compilerOptions !== undefined)
|
|
12142
|
-
tsModule = moduleOf(
|
|
12222
|
+
tsModule = moduleOf(join51(pkgDir, n2));
|
|
12143
12223
|
}
|
|
12144
12224
|
if (packageType === null && tsModule === null)
|
|
12145
12225
|
return null;
|
|
@@ -12161,7 +12241,7 @@ function conventionViolation(c, fileName, content) {
|
|
|
12161
12241
|
}
|
|
12162
12242
|
var readJson4 = (p) => {
|
|
12163
12243
|
try {
|
|
12164
|
-
const v = parseJsonc(
|
|
12244
|
+
const v = parseJsonc(readFileSync36(p, "utf8"));
|
|
12165
12245
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12166
12246
|
} catch {
|
|
12167
12247
|
return null;
|
|
@@ -12171,15 +12251,15 @@ var init_conventions = () => {};
|
|
|
12171
12251
|
|
|
12172
12252
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
12173
12253
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12174
|
-
import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as
|
|
12254
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync37, readdirSync as readdirSync15, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync21 } from "fs";
|
|
12175
12255
|
import { tmpdir as tmpdir9 } from "os";
|
|
12176
|
-
import { dirname as dirname19, isAbsolute as isAbsolute7, join as
|
|
12256
|
+
import { dirname as dirname19, isAbsolute as isAbsolute7, join as join52, relative as relative6 } from "path";
|
|
12177
12257
|
function pytestCollectionIsRed(output, repoDir) {
|
|
12178
12258
|
if (/ModuleNotFoundError/.test(output))
|
|
12179
12259
|
return false;
|
|
12180
12260
|
const real2 = (p) => {
|
|
12181
12261
|
try {
|
|
12182
|
-
return realpathSync18(isAbsolute7(p) ? p :
|
|
12262
|
+
return realpathSync18(isAbsolute7(p) ? p : join52(repoDir, p));
|
|
12183
12263
|
} catch {
|
|
12184
12264
|
return null;
|
|
12185
12265
|
}
|
|
@@ -12216,7 +12296,7 @@ function pytestCollectionIsRed(output, repoDir) {
|
|
|
12216
12296
|
}
|
|
12217
12297
|
function moduleUnderRepo(repoDir, name) {
|
|
12218
12298
|
const rel = name.replace(/\./g, "/");
|
|
12219
|
-
return existsSync31(
|
|
12299
|
+
return existsSync31(join52(repoDir, `${rel}.py`)) || existsSync31(join52(repoDir, rel, "__init__.py"));
|
|
12220
12300
|
}
|
|
12221
12301
|
function pytestExit1IsRed(output) {
|
|
12222
12302
|
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
@@ -12314,10 +12394,10 @@ ${acceptance.map((a) => `Acceptance: ${a}`).join(`
|
|
|
12314
12394
|
function loadTaskText(ctx, fromPrior) {
|
|
12315
12395
|
if (fromPrior)
|
|
12316
12396
|
return fromPrior;
|
|
12317
|
-
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ??
|
|
12397
|
+
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join52(ctx.runDir, "issue.json");
|
|
12318
12398
|
if (existsSync31(issueJsonPath)) {
|
|
12319
12399
|
try {
|
|
12320
|
-
const issue = JSON.parse(
|
|
12400
|
+
const issue = JSON.parse(readFileSync37(issueJsonPath, "utf8"));
|
|
12321
12401
|
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
12322
12402
|
|
|
12323
12403
|
`);
|
|
@@ -12329,7 +12409,7 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
12329
12409
|
}
|
|
12330
12410
|
function wallTargetDir(repoDir, existingTests) {
|
|
12331
12411
|
const first = existingTests[0];
|
|
12332
|
-
return first ?
|
|
12412
|
+
return first ? join52(repoDir, dirname19(first.path)) : join52(repoDir, "tests");
|
|
12333
12413
|
}
|
|
12334
12414
|
function guessRunner(fileName, runners) {
|
|
12335
12415
|
if (fileName.endsWith(".py"))
|
|
@@ -12360,11 +12440,11 @@ async function wallAuthor(ctx, signal) {
|
|
|
12360
12440
|
const tree = prior.intake?.tree;
|
|
12361
12441
|
const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
|
|
12362
12442
|
const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
|
|
12363
|
-
const conv2 = readPackageConventions(
|
|
12364
|
-
const cwd = mkdtempSync7(
|
|
12443
|
+
const conv2 = readPackageConventions(join52(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
|
|
12444
|
+
const cwd = mkdtempSync7(join52(tmpdir9(), "loki-e15-wall-"));
|
|
12365
12445
|
const wm = wallManifestFor(ctx.repoDir, tree, task);
|
|
12366
12446
|
for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
|
|
12367
|
-
writeFileSync21(
|
|
12447
|
+
writeFileSync21(join52(cwd, n2), c, "utf8");
|
|
12368
12448
|
const session = await ctx.sessions.run({
|
|
12369
12449
|
stage: "wall",
|
|
12370
12450
|
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
|
|
@@ -12379,15 +12459,15 @@ async function wallAuthor(ctx, signal) {
|
|
|
12379
12459
|
rmSync13(cwd, { recursive: true, force: true });
|
|
12380
12460
|
return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
|
|
12381
12461
|
}
|
|
12382
|
-
const generated =
|
|
12383
|
-
const sealedDir =
|
|
12462
|
+
const generated = readdirSync15(cwd).filter((f) => f.startsWith(WALL_PREFIX));
|
|
12463
|
+
const sealedDir = join52(ctx.runDir, "wall");
|
|
12384
12464
|
if (generated.length > 0)
|
|
12385
12465
|
mkdirSync19(sealedDir, { recursive: true });
|
|
12386
12466
|
const contents = new Map;
|
|
12387
12467
|
const discarded = [];
|
|
12388
12468
|
for (const name of generated) {
|
|
12389
|
-
const content =
|
|
12390
|
-
writeFileSync21(
|
|
12469
|
+
const content = readFileSync37(join52(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
|
|
12470
|
+
writeFileSync21(join52(sealedDir, name), content, "utf8");
|
|
12391
12471
|
if (bad)
|
|
12392
12472
|
discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
|
|
12393
12473
|
else
|
|
@@ -12407,7 +12487,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12407
12487
|
const content = contents.get(name);
|
|
12408
12488
|
if (content === undefined)
|
|
12409
12489
|
continue;
|
|
12410
|
-
const dest =
|
|
12490
|
+
const dest = join52(targetDir, name), runner = guessRunner(name, runners);
|
|
12411
12491
|
writeFileSync21(dest, content, "utf8");
|
|
12412
12492
|
sealedFiles.push({ path: dest, sha256: sha2562(content) });
|
|
12413
12493
|
readOnlyFiles.push({ path: dest, content });
|
|
@@ -12417,7 +12497,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12417
12497
|
ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
|
|
12418
12498
|
const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
|
|
12419
12499
|
for (const t of wallTests) {
|
|
12420
|
-
const r = baseRunner.run(baseDir, [t]), abs =
|
|
12500
|
+
const r = baseRunner.run(baseDir, [t]), abs = join52(ctx.repoDir, t.path);
|
|
12421
12501
|
baseRun.pass += r.pass;
|
|
12422
12502
|
baseRun.fail += r.fail;
|
|
12423
12503
|
baseRun.not_run += r.not_run ?? 0;
|
|
@@ -12469,15 +12549,15 @@ var init_wall = __esm(() => {
|
|
|
12469
12549
|
});
|
|
12470
12550
|
|
|
12471
12551
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
12472
|
-
import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as
|
|
12473
|
-
import { dirname as dirname20, join as
|
|
12552
|
+
import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as readFileSync38, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
|
|
12553
|
+
import { dirname as dirname20, join as join53, relative as relative7, sep as sep11 } from "path";
|
|
12474
12554
|
function safeRestore(repoDir, f, want, mode) {
|
|
12475
|
-
const root = realpathSync19(repoDir), fp =
|
|
12555
|
+
const root = realpathSync19(repoDir), fp = join53(root, f), rel = relative7(root, dirname20(fp));
|
|
12476
12556
|
if (rel.startsWith(".."))
|
|
12477
12557
|
throw new Error("outside repo");
|
|
12478
12558
|
let cur = root;
|
|
12479
12559
|
for (const part of rel === "" ? [] : rel.split(sep11)) {
|
|
12480
|
-
cur =
|
|
12560
|
+
cur = join53(cur, part);
|
|
12481
12561
|
let st2;
|
|
12482
12562
|
try {
|
|
12483
12563
|
st2 = lstatSync10(cur);
|
|
@@ -12494,7 +12574,7 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
12494
12574
|
} catch {
|
|
12495
12575
|
st = null;
|
|
12496
12576
|
}
|
|
12497
|
-
if (st?.isFile() &&
|
|
12577
|
+
if (st?.isFile() && readFileSync38(fp).equals(want)) {
|
|
12498
12578
|
chmodSync5(fp, mode);
|
|
12499
12579
|
return;
|
|
12500
12580
|
}
|
|
@@ -12532,7 +12612,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12532
12612
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
12533
12613
|
const notProven = [];
|
|
12534
12614
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
12535
|
-
const fp =
|
|
12615
|
+
const fp = join53(repoDir, f);
|
|
12536
12616
|
try {
|
|
12537
12617
|
if (v.startsWith("!")) {
|
|
12538
12618
|
const st = lstatSync10(fp);
|
|
@@ -12551,7 +12631,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12551
12631
|
try {
|
|
12552
12632
|
let m = 420;
|
|
12553
12633
|
try {
|
|
12554
|
-
const l = lstatSync10(
|
|
12634
|
+
const l = lstatSync10(join53(repoDir, f));
|
|
12555
12635
|
if (l.isFile())
|
|
12556
12636
|
m = l.mode & 511;
|
|
12557
12637
|
} catch {}
|
|
@@ -12627,9 +12707,9 @@ var init_scope2 = __esm(() => {
|
|
|
12627
12707
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
12628
12708
|
import { spawn as spawn7 } from "child_process";
|
|
12629
12709
|
import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
|
|
12630
|
-
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as
|
|
12710
|
+
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync16, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
|
|
12631
12711
|
import { createServer } from "net";
|
|
12632
|
-
import { dirname as dirname23, isAbsolute as isAbsolute8, join as
|
|
12712
|
+
import { dirname as dirname23, isAbsolute as isAbsolute8, join as join54, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
|
|
12633
12713
|
function routeFor(file) {
|
|
12634
12714
|
let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
|
|
12635
12715
|
r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
|
|
@@ -12639,7 +12719,7 @@ function hashScreens(runDir, rels) {
|
|
|
12639
12719
|
const out2 = [];
|
|
12640
12720
|
for (const rel of rels) {
|
|
12641
12721
|
try {
|
|
12642
|
-
out2.push({ path: rel, sha256: sha256File(
|
|
12722
|
+
out2.push({ path: rel, sha256: sha256File(join54(runDir, rel)) });
|
|
12643
12723
|
} catch {}
|
|
12644
12724
|
}
|
|
12645
12725
|
return out2;
|
|
@@ -12652,10 +12732,10 @@ function checkScreens(runDir, screens) {
|
|
|
12652
12732
|
return "evidence_screens entry is malformed";
|
|
12653
12733
|
if (isAbsolute8(s.path) || normalize2(s.path).startsWith(".."))
|
|
12654
12734
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
12655
|
-
const file =
|
|
12735
|
+
const file = join54(runDir, s.path);
|
|
12656
12736
|
let cur = runDir;
|
|
12657
12737
|
for (const seg of normalize2(s.path).split(sep12)) {
|
|
12658
|
-
cur =
|
|
12738
|
+
cur = join54(cur, seg);
|
|
12659
12739
|
if (isSymlink(cur))
|
|
12660
12740
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
12661
12741
|
}
|
|
@@ -12702,11 +12782,11 @@ async function waitUp(url, ms, signal) {
|
|
|
12702
12782
|
}
|
|
12703
12783
|
function openapiPaths(repoDir) {
|
|
12704
12784
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
12705
|
-
const p =
|
|
12785
|
+
const p = join54(repoDir, f);
|
|
12706
12786
|
if (!existsSync32(p))
|
|
12707
12787
|
continue;
|
|
12708
12788
|
try {
|
|
12709
|
-
const txt =
|
|
12789
|
+
const txt = readFileSync39(p, "utf8");
|
|
12710
12790
|
const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
|
|
12711
12791
|
return paths.filter((x) => !x.includes("{")).slice(0, 10);
|
|
12712
12792
|
} catch {
|
|
@@ -12720,24 +12800,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
12720
12800
|
if (!pw || !pkg)
|
|
12721
12801
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
12722
12802
|
try {
|
|
12723
|
-
const dir =
|
|
12803
|
+
const dir = join54(runDir, relDir, "e2e");
|
|
12724
12804
|
mkdirSync21(dir, { recursive: true });
|
|
12725
|
-
writeFileSync23(
|
|
12726
|
-
writeFileSync23(
|
|
12805
|
+
writeFileSync23(join54(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
12806
|
+
writeFileSync23(join54(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
12727
12807
|
`);
|
|
12728
|
-
if (!await shoot(pw, ["test", "--config",
|
|
12808
|
+
if (!await shoot(pw, ["test", "--config", join54(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
12729
12809
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
12730
12810
|
const rels = [];
|
|
12731
12811
|
const walk = (d) => {
|
|
12732
|
-
for (const e of
|
|
12733
|
-
const full =
|
|
12812
|
+
for (const e of readdirSync16(d, { withFileTypes: true })) {
|
|
12813
|
+
const full = join54(d, e.name);
|
|
12734
12814
|
if (e.isDirectory())
|
|
12735
12815
|
walk(full);
|
|
12736
12816
|
else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
|
|
12737
|
-
rels.push(
|
|
12817
|
+
rels.push(join54(relDir, "e2e", relative8(dir, full)));
|
|
12738
12818
|
}
|
|
12739
12819
|
};
|
|
12740
|
-
walk(
|
|
12820
|
+
walk(join54(dir, "out"));
|
|
12741
12821
|
return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
|
|
12742
12822
|
} catch (e) {
|
|
12743
12823
|
return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
|
|
@@ -12797,6 +12877,16 @@ async function stopServer(c, graceMs, signal) {
|
|
|
12797
12877
|
for (let i = 0;i < 10 && groupAlive(pid); i++)
|
|
12798
12878
|
await new Promise((r) => setTimeout(r, 50));
|
|
12799
12879
|
}
|
|
12880
|
+
function noRootReason(subs, lacking) {
|
|
12881
|
+
const list = (xs) => xs.map((d) => `${d}/`).join(" or ");
|
|
12882
|
+
if (subs.length === 0)
|
|
12883
|
+
return "no package.json";
|
|
12884
|
+
if (lacking.length === subs.length)
|
|
12885
|
+
return `no dev, preview or start script in ${list(lacking)} (no root package.json)`;
|
|
12886
|
+
if (lacking.length === 0)
|
|
12887
|
+
return `no root package.json (${list(subs)} have a dev, preview or start script but no UI boot was resolved)`;
|
|
12888
|
+
return `no root package.json; no dev, preview or start script in ${list(lacking)} (${list(subs.filter((d) => !lacking.includes(d)))} has one)`;
|
|
12889
|
+
}
|
|
12800
12890
|
async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
12801
12891
|
const skip = (why) => ({ screens: [], http: false, skipped: why });
|
|
12802
12892
|
const signal = opts.signal;
|
|
@@ -12806,13 +12896,22 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12806
12896
|
if (!visualEvidenceEnabled(opts.env))
|
|
12807
12897
|
return skip("LOKI_VISUAL_EVIDENCE is 0");
|
|
12808
12898
|
const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
|
|
12809
|
-
const appDir = boot ?
|
|
12810
|
-
const pkgPath =
|
|
12899
|
+
const appDir = boot ? join54(repoDir, boot.boot.cwd) : repoDir;
|
|
12900
|
+
const pkgPath = join54(appDir, "package.json");
|
|
12811
12901
|
let script = null;
|
|
12812
12902
|
if (!boot) {
|
|
12813
|
-
if (!existsSync32(pkgPath))
|
|
12814
|
-
|
|
12815
|
-
|
|
12903
|
+
if (!existsSync32(pkgPath)) {
|
|
12904
|
+
const subs = ["backend", "frontend"].filter((d) => existsSync32(join54(repoDir, d, "package.json")));
|
|
12905
|
+
const has = (d) => {
|
|
12906
|
+
try {
|
|
12907
|
+
return pickScript(JSON.parse(readFileSync39(join54(repoDir, d, "package.json"), "utf8"))) !== null;
|
|
12908
|
+
} catch {
|
|
12909
|
+
return false;
|
|
12910
|
+
}
|
|
12911
|
+
};
|
|
12912
|
+
return skip(noRootReason(subs, subs.filter((d) => !has(d))));
|
|
12913
|
+
}
|
|
12914
|
+
script = pickScript(JSON.parse(readFileSync39(pkgPath, "utf8")));
|
|
12816
12915
|
if (!script)
|
|
12817
12916
|
return skip("no dev, preview or start script");
|
|
12818
12917
|
}
|
|
@@ -12839,20 +12938,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12839
12938
|
}
|
|
12840
12939
|
if (!await waitUp(base, left(), signal))
|
|
12841
12940
|
return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
|
|
12842
|
-
const evRoot =
|
|
12941
|
+
const evRoot = join54(runDir, "evidence");
|
|
12843
12942
|
if (isSymlink(evRoot))
|
|
12844
12943
|
return skip("evidence dir is a symlink");
|
|
12845
12944
|
mkdirSync21(evRoot, { recursive: true });
|
|
12846
12945
|
if (pages.length > 0) {
|
|
12847
|
-
const rel0 =
|
|
12848
|
-
mkdirSync21(
|
|
12946
|
+
const rel0 = join54("evidence", randomBytes4(6).toString("hex"));
|
|
12947
|
+
mkdirSync21(join54(runDir, rel0));
|
|
12849
12948
|
const rels = [];
|
|
12850
12949
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
12851
12950
|
if (aborted() || left() <= 0)
|
|
12852
12951
|
break;
|
|
12853
|
-
const rel =
|
|
12854
|
-
const ok = await shoot(pw, ["screenshot", `${base}${route}`,
|
|
12855
|
-
if (ok && !aborted() && !isSymlink(
|
|
12952
|
+
const rel = join54(rel0, `${screenName(route)}.png`);
|
|
12953
|
+
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join54(runDir, rel)], appDir, left(), signal);
|
|
12954
|
+
if (ok && !aborted() && !isSymlink(join54(runDir, rel)) && existsSync32(join54(runDir, rel)))
|
|
12856
12955
|
rels.push(rel);
|
|
12857
12956
|
}
|
|
12858
12957
|
if (aborted())
|
|
@@ -12877,9 +12976,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12877
12976
|
}
|
|
12878
12977
|
if (aborted())
|
|
12879
12978
|
return skip("aborted");
|
|
12880
|
-
const hdir =
|
|
12979
|
+
const hdir = join54(evRoot, randomBytes4(6).toString("hex"));
|
|
12881
12980
|
mkdirSync21(hdir);
|
|
12882
|
-
writeFileSync23(
|
|
12981
|
+
writeFileSync23(join54(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
12883
12982
|
`);
|
|
12884
12983
|
return { screens: [], http: true, skipped: null };
|
|
12885
12984
|
} catch (e) {
|
|
@@ -12904,12 +13003,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
12904
13003
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
12905
13004
|
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
|
|
12906
13005
|
}
|
|
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(
|
|
12908
|
-
const bin =
|
|
13006
|
+
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(readFileSync39(p)).digest("hex"), playwrightBin = (repoDir) => {
|
|
13007
|
+
const bin = join54(repoDir, "node_modules", ".bin", "playwright");
|
|
12909
13008
|
return existsSync32(bin) ? bin : null;
|
|
12910
13009
|
}, playwrightTestPkg = (repoDir) => {
|
|
12911
|
-
const d =
|
|
12912
|
-
return existsSync32(
|
|
13010
|
+
const d = join54(repoDir, "node_modules", "@playwright", "test");
|
|
13011
|
+
return existsSync32(join54(d, "package.json")) ? d : null;
|
|
12913
13012
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
12914
13013
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
12915
13014
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
@@ -12940,14 +13039,14 @@ var init_visual_evidence = __esm(() => {
|
|
|
12940
13039
|
});
|
|
12941
13040
|
|
|
12942
13041
|
// ../../loki-ts/src/council/role_agents.ts
|
|
12943
|
-
import { existsSync as existsSync33, readdirSync as
|
|
13042
|
+
import { existsSync as existsSync33, readdirSync as readdirSync17, readFileSync as readFileSync40 } from "fs";
|
|
12944
13043
|
import { homedir as homedir9 } from "os";
|
|
12945
|
-
import { join as
|
|
13044
|
+
import { join as join55 } from "path";
|
|
12946
13045
|
function agentDirs(repoDir, env = process.env) {
|
|
12947
|
-
return [env["LOKI_AGENTS_PROJECT_DIR"] ||
|
|
13046
|
+
return [env["LOKI_AGENTS_PROJECT_DIR"] || join55(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join55(env["XDG_CONFIG_HOME"] || join55(homedir9(), ".config"), "loki", "agents")];
|
|
12948
13047
|
}
|
|
12949
13048
|
function parseAgent(file, stem2) {
|
|
12950
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(
|
|
13049
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync40(file, "utf8"));
|
|
12951
13050
|
if (!m)
|
|
12952
13051
|
return null;
|
|
12953
13052
|
const body = m[2].trim();
|
|
@@ -12965,14 +13064,14 @@ function loadRoleAgent(repoDir, role, env = process.env) {
|
|
|
12965
13064
|
continue;
|
|
12966
13065
|
let names;
|
|
12967
13066
|
try {
|
|
12968
|
-
names =
|
|
13067
|
+
names = readdirSync17(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
|
|
12969
13068
|
} catch {
|
|
12970
13069
|
continue;
|
|
12971
13070
|
}
|
|
12972
13071
|
const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
|
|
12973
13072
|
for (const n2 of ordered) {
|
|
12974
13073
|
try {
|
|
12975
|
-
const a = parseAgent(
|
|
13074
|
+
const a = parseAgent(join55(d, n2), n2.slice(0, -3));
|
|
12976
13075
|
if (a && a.role === role)
|
|
12977
13076
|
return a;
|
|
12978
13077
|
} catch {}
|
|
@@ -12989,8 +13088,8 @@ var init_role_agents = __esm(() => {
|
|
|
12989
13088
|
});
|
|
12990
13089
|
|
|
12991
13090
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
12992
|
-
import { existsSync as existsSync34, readFileSync as
|
|
12993
|
-
import { join as
|
|
13091
|
+
import { existsSync as existsSync34, readFileSync as readFileSync41 } from "fs";
|
|
13092
|
+
import { join as join56 } from "path";
|
|
12994
13093
|
function minVerdict(verdict, review) {
|
|
12995
13094
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
12996
13095
|
return RANK3[ceiling] < RANK3[verdict] ? ceiling : verdict;
|
|
@@ -13003,11 +13102,11 @@ function explicitReview(repoDir, env) {
|
|
|
13003
13102
|
if (flag && isOff(ev))
|
|
13004
13103
|
return "off";
|
|
13005
13104
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
13006
|
-
const p =
|
|
13105
|
+
const p = join56(repoDir, f);
|
|
13007
13106
|
if (!existsSync34(p))
|
|
13008
13107
|
continue;
|
|
13009
13108
|
try {
|
|
13010
|
-
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(
|
|
13109
|
+
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync41(p, "utf8"));
|
|
13011
13110
|
const v = asProvider(m?.[1]);
|
|
13012
13111
|
if (v)
|
|
13013
13112
|
return v;
|
|
@@ -13217,16 +13316,41 @@ function recordRun(env, repoDir, model, verdict, usd, unmetered, wallS, cacheRoo
|
|
|
13217
13316
|
return false;
|
|
13218
13317
|
return appendRunOutcome(repoKey(readOriginUrl(repoDir), repoDir), { shape, executor: /haiku/i.test(model) ? "haiku" : "sonnet", verdict: "pass", owner: null, escalated: false, usd, wallS, wallKind: WALL_KIND }, cacheRoot);
|
|
13219
13318
|
}
|
|
13220
|
-
var NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0";
|
|
13319
|
+
var MIN_RUNS = 3, NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0", ESTIMATE_HELP;
|
|
13221
13320
|
var init_cost_preview = __esm(() => {
|
|
13222
13321
|
init_cache();
|
|
13322
|
+
init_session();
|
|
13223
13323
|
init_history();
|
|
13224
13324
|
init_engine_origin();
|
|
13325
|
+
ESTIMATE_HELP = `loki estimate - Show what a run is likely to cost
|
|
13326
|
+
|
|
13327
|
+
Usage: loki estimate [--model M] [--provider P] [--json]
|
|
13328
|
+
|
|
13329
|
+
Prints the same estimate line "loki start" prints before a run: the dollar and time
|
|
13330
|
+
range of this repo's prior verified runs of the same project shape. With fewer than
|
|
13331
|
+
${MIN_RUNS} such runs it prints a rough per-model prior, or NOT AVAILABLE (reason).
|
|
13332
|
+
It never estimates from task text and never prints a number it did not measure.
|
|
13333
|
+
|
|
13334
|
+
Options:
|
|
13335
|
+
--model M Model whose rough prior to use when there is no history (default: the start model)
|
|
13336
|
+
--provider P Provider whose default model is used (default: LOKI_PROVIDER or claude)
|
|
13337
|
+
--json Machine-readable output (the estimate result object)
|
|
13338
|
+
--help, -h Show this help
|
|
13339
|
+
|
|
13340
|
+
Changed: --iterations is removed. The old iteration-based projection (tools/estimate-run.py)
|
|
13341
|
+
is replaced by this single estimator. LOKI_COST_PREVIEW=0 turns the estimate off.
|
|
13342
|
+
|
|
13343
|
+
Examples:
|
|
13344
|
+
loki estimate
|
|
13345
|
+
loki estimate --json
|
|
13346
|
+
|
|
13347
|
+
See also: loki cost --detail (what a run ALREADY spent)
|
|
13348
|
+
`;
|
|
13225
13349
|
});
|
|
13226
13350
|
|
|
13227
13351
|
// ../../loki-ts/src/features/contract.ts
|
|
13228
|
-
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as
|
|
13229
|
-
import { dirname as dirname24, join as
|
|
13352
|
+
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync42, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
13353
|
+
import { dirname as dirname24, join as join57 } from "path";
|
|
13230
13354
|
import { createHash as createHash13 } from "crypto";
|
|
13231
13355
|
function contractEnabled(env = process.env) {
|
|
13232
13356
|
return env["LOKI_CONTRACT"] !== "0";
|
|
@@ -13258,7 +13382,7 @@ function untracedLines(trace) {
|
|
|
13258
13382
|
return trace.criteria.filter((c) => c.status === "no_match").map((c) => `contract ${sanitizeCriterion(c.id)} untraced (no keyword match in changed files): ${sanitizeCriterion(c.text)}`);
|
|
13259
13383
|
}
|
|
13260
13384
|
function contractPath(repoDir) {
|
|
13261
|
-
return
|
|
13385
|
+
return join57(repoDir, ".loki", "contract.json");
|
|
13262
13386
|
}
|
|
13263
13387
|
function readContract(repoDir) {
|
|
13264
13388
|
const p = contractPath(repoDir);
|
|
@@ -13274,7 +13398,7 @@ function readContract(repoDir) {
|
|
|
13274
13398
|
return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
|
|
13275
13399
|
let raw2;
|
|
13276
13400
|
try {
|
|
13277
|
-
raw2 =
|
|
13401
|
+
raw2 = readFileSync42(p);
|
|
13278
13402
|
} catch {
|
|
13279
13403
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
13280
13404
|
}
|
|
@@ -13343,16 +13467,16 @@ var init_contract = __esm(() => {
|
|
|
13343
13467
|
init_safe_git();
|
|
13344
13468
|
STOP2 = new Set(["the", "and", "for", "with", "that", "this", "must", "should", "shall", "when", "then", "are", "was", "all", "any", "can", "not", "has", "have", "from", "into", "each", "user", "users"]);
|
|
13345
13469
|
MAX_CONTRACT_BYTES = 1024 * 1024;
|
|
13346
|
-
REQ_MAP_REL =
|
|
13470
|
+
REQ_MAP_REL = join57(".loki", "state", "requirements-map.json");
|
|
13347
13471
|
REQ_STOP = new Set(["with", "that", "this", "from", "have", "will", "should", "must", "when", "then", "into", "each", "every", "user", "users", "page", "able", "allow", "support", "ensure", "using", "their", "there", "which", "shall"]);
|
|
13348
13472
|
});
|
|
13349
13473
|
|
|
13350
13474
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
13351
13475
|
import { createHash as createHash14 } from "crypto";
|
|
13352
|
-
import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as
|
|
13353
|
-
import { basename as basename19, dirname as dirname25, join as
|
|
13476
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync18, readFileSync as readFileSync43, renameSync as renameSync9, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
|
|
13477
|
+
import { basename as basename19, dirname as dirname25, join as join58 } from "path";
|
|
13354
13478
|
function sealGroup(runDir, rh) {
|
|
13355
|
-
const manifestPath =
|
|
13479
|
+
const manifestPath = join58(runDir, "group", "manifest.json");
|
|
13356
13480
|
if (!existsSync36(manifestPath))
|
|
13357
13481
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
13358
13482
|
const notProven = [];
|
|
@@ -13377,10 +13501,10 @@ function sealGroup(runDir, rh) {
|
|
|
13377
13501
|
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
13378
13502
|
seen.add(id);
|
|
13379
13503
|
seenLower.add(id.toLowerCase());
|
|
13380
|
-
const dir =
|
|
13504
|
+
const dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
|
|
13381
13505
|
let bytes;
|
|
13382
13506
|
try {
|
|
13383
|
-
bytes =
|
|
13507
|
+
bytes = readFileSync43(rp);
|
|
13384
13508
|
} catch {
|
|
13385
13509
|
return fail(`unit ${id} sub-receipt missing`);
|
|
13386
13510
|
}
|
|
@@ -13397,7 +13521,7 @@ function sealGroup(runDir, rh) {
|
|
|
13397
13521
|
unit_id: id,
|
|
13398
13522
|
sub_receipt_sha256: sha2(bytes),
|
|
13399
13523
|
receipt_sha256: sub["receipt_sha256"],
|
|
13400
|
-
events_file_sha256: existsSync36(ep) ? sha2(
|
|
13524
|
+
events_file_sha256: existsSync36(ep) ? sha2(readFileSync43(ep)) : null,
|
|
13401
13525
|
verdict,
|
|
13402
13526
|
goal: text2(u["goal"], 200),
|
|
13403
13527
|
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
@@ -13422,7 +13546,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13422
13546
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
13423
13547
|
const g = receipt["group"];
|
|
13424
13548
|
if (g === undefined)
|
|
13425
|
-
return existsSync36(
|
|
13549
|
+
return existsSync36(join58(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
13426
13550
|
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
13427
13551
|
return bad("group section malformed");
|
|
13428
13552
|
const entries = g["units"];
|
|
@@ -13434,20 +13558,20 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13434
13558
|
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
13435
13559
|
let unchecked = null;
|
|
13436
13560
|
for (let i = 0;i < entries.length; i++) {
|
|
13437
|
-
const e = entries[i], id = ids[i], dir =
|
|
13561
|
+
const e = entries[i], id = ids[i], dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
|
|
13438
13562
|
if (e["index"] !== i)
|
|
13439
13563
|
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
13440
13564
|
let bytes;
|
|
13441
13565
|
try {
|
|
13442
13566
|
if (!statSync17(dir).isDirectory())
|
|
13443
13567
|
return bad(`unit ${id} is not a directory`);
|
|
13444
|
-
bytes =
|
|
13568
|
+
bytes = readFileSync43(rp);
|
|
13445
13569
|
} catch {
|
|
13446
13570
|
return bad(`unit ${id} sub-receipt missing`);
|
|
13447
13571
|
}
|
|
13448
13572
|
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
13449
13573
|
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
13450
|
-
const evSha = existsSync36(ep) ? sha2(
|
|
13574
|
+
const evSha = existsSync36(ep) ? sha2(readFileSync43(ep)) : null;
|
|
13451
13575
|
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
13452
13576
|
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
13453
13577
|
const sub = readJson5(rp);
|
|
@@ -13467,8 +13591,8 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13467
13591
|
}
|
|
13468
13592
|
function compositeVerdict(childVerdicts) {
|
|
13469
13593
|
const base = childVerdicts.includes("SPEC_CONFLICT") ? "SPEC_CONFLICT" : childVerdicts.length > 0 && childVerdicts.every((v) => v === "ALREADY_SATISFIED") ? "ALREADY_SATISFIED" : "VERIFIED";
|
|
13470
|
-
const
|
|
13471
|
-
return capGroupVerdict(base, { section: undefined, notProven: [], problems:
|
|
13594
|
+
const failed2 = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
|
|
13595
|
+
return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed2, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
|
|
13472
13596
|
}
|
|
13473
13597
|
async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
13474
13598
|
const runDir = dirname25(receiptPath);
|
|
@@ -13479,13 +13603,13 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13479
13603
|
return bad("body malformed");
|
|
13480
13604
|
let ibytes;
|
|
13481
13605
|
try {
|
|
13482
|
-
ibytes =
|
|
13606
|
+
ibytes = readFileSync43(join58(runDir, "integration.json"));
|
|
13483
13607
|
} catch {
|
|
13484
13608
|
return bad("integration.json missing");
|
|
13485
13609
|
}
|
|
13486
13610
|
if (sha2(ibytes) !== receipt["integration_sha256"])
|
|
13487
13611
|
return bad("integration.json does not match its recorded hash");
|
|
13488
|
-
const integ = readJson5(
|
|
13612
|
+
const integ = readJson5(join58(runDir, "integration.json"));
|
|
13489
13613
|
if (!isObj10(integ) || !isObj10(integ["outcomes"]))
|
|
13490
13614
|
return bad("integration.json has no outcomes");
|
|
13491
13615
|
const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
|
|
@@ -13503,10 +13627,10 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13503
13627
|
return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
|
|
13504
13628
|
if (outcomes[repo] !== "ok")
|
|
13505
13629
|
return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
|
|
13506
|
-
const rp =
|
|
13630
|
+
const rp = join58(runDir, rel);
|
|
13507
13631
|
let bytes;
|
|
13508
13632
|
try {
|
|
13509
|
-
bytes =
|
|
13633
|
+
bytes = readFileSync43(rp);
|
|
13510
13634
|
} catch {
|
|
13511
13635
|
return bad(`child ${repo} receipt missing`);
|
|
13512
13636
|
}
|
|
@@ -13541,15 +13665,15 @@ function compositeHash(r) {
|
|
|
13541
13665
|
const { composite_sha256: _c, verification: _v, ...body } = r;
|
|
13542
13666
|
return sha2(canonicalJson(body));
|
|
13543
13667
|
}
|
|
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) =>
|
|
13668
|
+
var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join58(runDir, "group", "units"), readJson5 = (p) => {
|
|
13545
13669
|
try {
|
|
13546
|
-
return JSON.parse(
|
|
13670
|
+
return JSON.parse(readFileSync43(p, "utf8"));
|
|
13547
13671
|
} catch {
|
|
13548
13672
|
return;
|
|
13549
13673
|
}
|
|
13550
13674
|
}, dirNames = (d) => {
|
|
13551
13675
|
try {
|
|
13552
|
-
return
|
|
13676
|
+
return readdirSync18(d).filter((n2) => !n2.startsWith(".")).sort();
|
|
13553
13677
|
} catch {
|
|
13554
13678
|
return null;
|
|
13555
13679
|
}
|
|
@@ -13561,8 +13685,8 @@ var init_seal_group = __esm(() => {
|
|
|
13561
13685
|
});
|
|
13562
13686
|
|
|
13563
13687
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
13564
|
-
import { existsSync as existsSync37, readFileSync as
|
|
13565
|
-
import { basename as basename20, join as
|
|
13688
|
+
import { existsSync as existsSync37, readFileSync as readFileSync44, statSync as statSync18 } from "fs";
|
|
13689
|
+
import { basename as basename20, join as join59 } from "path";
|
|
13566
13690
|
function supplyEnabled(env) {
|
|
13567
13691
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
13568
13692
|
}
|
|
@@ -13577,13 +13701,13 @@ function isManifestPath(path) {
|
|
|
13577
13701
|
return MANIFESTS.test(basename20(path));
|
|
13578
13702
|
}
|
|
13579
13703
|
function readDeclared(repoDir) {
|
|
13580
|
-
const p =
|
|
13704
|
+
const p = join59(repoDir, ".loki", DECLARED_FILE);
|
|
13581
13705
|
if (!existsSync37(p))
|
|
13582
13706
|
return { deps: [], problem: null };
|
|
13583
13707
|
try {
|
|
13584
13708
|
if (statSync18(p).size > MAX_FILE_BYTES)
|
|
13585
13709
|
return { deps: [], problem: "declaration file too large" };
|
|
13586
|
-
const j = JSON.parse(
|
|
13710
|
+
const j = JSON.parse(readFileSync44(p, "utf8"));
|
|
13587
13711
|
if (!Array.isArray(j))
|
|
13588
13712
|
return { deps: [], problem: "declaration is not a JSON array" };
|
|
13589
13713
|
const deps = [];
|
|
@@ -13603,11 +13727,11 @@ function readDeclared(repoDir) {
|
|
|
13603
13727
|
}
|
|
13604
13728
|
}
|
|
13605
13729
|
function readAllowlist(repoDir) {
|
|
13606
|
-
const p =
|
|
13730
|
+
const p = join59(repoDir, ".loki", "supply-allowlist");
|
|
13607
13731
|
if (!existsSync37(p))
|
|
13608
13732
|
return new Set;
|
|
13609
13733
|
try {
|
|
13610
|
-
return new Set(
|
|
13734
|
+
return new Set(readFileSync44(p, "utf8").split(`
|
|
13611
13735
|
`).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
|
|
13612
13736
|
} catch {
|
|
13613
13737
|
return new Set;
|
|
@@ -13648,7 +13772,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13648
13772
|
if (eco === "npm") {
|
|
13649
13773
|
if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
|
|
13650
13774
|
return true;
|
|
13651
|
-
return files(
|
|
13775
|
+
return files(join59(repoDir, ".npmrc"), home ? join59(home, ".npmrc") : "").some((t) => t.split(`
|
|
13652
13776
|
`).some((l) => {
|
|
13653
13777
|
const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
|
|
13654
13778
|
return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
|
|
@@ -13657,7 +13781,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13657
13781
|
if (eco === "pypi") {
|
|
13658
13782
|
if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
|
|
13659
13783
|
return true;
|
|
13660
|
-
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ?
|
|
13784
|
+
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join59(home, ".config", "pip", "pip.conf") : "", home ? join59(home, ".pip", "pip.conf") : "", join59(repoDir, "pip.conf")];
|
|
13661
13785
|
return files(...cfg).some((t) => t.split(`
|
|
13662
13786
|
`).some((l) => {
|
|
13663
13787
|
const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
|
|
@@ -13667,7 +13791,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13667
13791
|
if (eco === "cargo") {
|
|
13668
13792
|
if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
|
|
13669
13793
|
return true;
|
|
13670
|
-
return files(
|
|
13794
|
+
return files(join59(repoDir, ".cargo", "config.toml"), join59(repoDir, ".cargo", "config"), home ? join59(home, ".cargo", "config.toml") : "", home ? join59(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
|
|
13671
13795
|
}
|
|
13672
13796
|
if (eco === "go") {
|
|
13673
13797
|
const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
|
|
@@ -13790,7 +13914,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
|
|
|
13790
13914
|
}
|
|
13791
13915
|
}, PUBLIC_HOSTS, hostOf = (u) => u.trim().toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "").replace(/^www\./, "").split(/[/?#\s]/)[0].replace(/^[^@]*@/, "").replace(/:\d+$/, ""), privateUrl = (u) => /[a-z0-9]/i.test(u) && !PUBLIC_HOSTS.has(hostOf(u)), readSmall = (p) => {
|
|
13792
13916
|
try {
|
|
13793
|
-
return statSync18(p).size <= MAX_FILE_BYTES ?
|
|
13917
|
+
return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync44(p, "utf8") : "";
|
|
13794
13918
|
} catch {
|
|
13795
13919
|
return "";
|
|
13796
13920
|
}
|
|
@@ -13806,8 +13930,8 @@ var init_supply_guard = __esm(() => {
|
|
|
13806
13930
|
|
|
13807
13931
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
13808
13932
|
import { createHash as createHash15 } from "crypto";
|
|
13809
|
-
import { existsSync as existsSync38, readdirSync as
|
|
13810
|
-
import { basename as basename21, isAbsolute as isAbsolute9, join as
|
|
13933
|
+
import { existsSync as existsSync38, readdirSync as readdirSync19, mkdirSync as mkdirSync24, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
|
|
13934
|
+
import { basename as basename21, isAbsolute as isAbsolute9, join as join60, relative as relative9 } from "path";
|
|
13811
13935
|
function sanitizeReason(s) {
|
|
13812
13936
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
13813
13937
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
@@ -13882,7 +14006,7 @@ function renderReceiptMd(r) {
|
|
|
13882
14006
|
`);
|
|
13883
14007
|
}
|
|
13884
14008
|
var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash15("sha256").update(s).digest("hex"), str3 = (v) => typeof v === "string" && v !== "" ? v : null, strs2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : [], receiptRel = (ctx) => {
|
|
13885
|
-
const r = relative9(ctx.repoDir,
|
|
14009
|
+
const r = relative9(ctx.repoDir, join60(ctx.runDir, "receipt.json"));
|
|
13886
14010
|
return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
13887
14011
|
}, commitStage, sealStage;
|
|
13888
14012
|
var init_seal = __esm(() => {
|
|
@@ -13985,7 +14109,7 @@ var init_seal = __esm(() => {
|
|
|
13985
14109
|
if (wallNotRun > 0) {
|
|
13986
14110
|
try {
|
|
13987
14111
|
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
|
|
13988
|
-
for (const n2 of
|
|
14112
|
+
for (const n2 of readdirSync19(join60(ctx.runDir, "wall")).sort())
|
|
13989
14113
|
if (!kept.has(n2))
|
|
13990
14114
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
13991
14115
|
} catch {}
|
|
@@ -14046,19 +14170,19 @@ var init_seal = __esm(() => {
|
|
|
14046
14170
|
for (const [s, d] of Object.entries(o))
|
|
14047
14171
|
if (typeof d?.duration_s === "number")
|
|
14048
14172
|
stages[s] = d.duration_s;
|
|
14049
|
-
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(
|
|
14173
|
+
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join60(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
|
|
14050
14174
|
const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
|
|
14051
14175
|
if (iterIds.length === 0)
|
|
14052
14176
|
notProven.add("cost not measured (no iteration ids recorded)");
|
|
14053
14177
|
const cost = ctx.cost.read(ctx.repoDir, iterIds);
|
|
14054
14178
|
if (cost.unmetered)
|
|
14055
14179
|
notProven.add("cost unmetered (CLI invoker; recorded as 0)");
|
|
14056
|
-
const eventsPath =
|
|
14180
|
+
const eventsPath = join60(ctx.runDir, "events.jsonl");
|
|
14057
14181
|
const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
|
|
14058
14182
|
const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
|
|
14059
14183
|
if (wallFiles.length > 0 && wallPassed === null)
|
|
14060
14184
|
notProven.add("wall result not recorded by verify");
|
|
14061
|
-
const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(
|
|
14185
|
+
const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join60(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
|
|
14062
14186
|
if (routeBlock)
|
|
14063
14187
|
for (const l of routeNotProven(routeBlock))
|
|
14064
14188
|
notProven.add(l);
|
|
@@ -14100,7 +14224,7 @@ var init_seal = __esm(() => {
|
|
|
14100
14224
|
provider: ctx.provider,
|
|
14101
14225
|
model: ctx.model,
|
|
14102
14226
|
resumed: o.intake?.resumed === true,
|
|
14103
|
-
events_sha256: sha2562(existsSync38(eventsPath) ?
|
|
14227
|
+
events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync45(eventsPath) : ""),
|
|
14104
14228
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
14105
14229
|
log_seal: true,
|
|
14106
14230
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -14121,10 +14245,10 @@ var init_seal = __esm(() => {
|
|
|
14121
14245
|
}
|
|
14122
14246
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
14123
14247
|
mkdirSync24(ctx.runDir, { recursive: true });
|
|
14124
|
-
const path =
|
|
14248
|
+
const path = join60(ctx.runDir, "receipt.json");
|
|
14125
14249
|
writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
|
|
14126
14250
|
`);
|
|
14127
|
-
writeFileSync26(
|
|
14251
|
+
writeFileSync26(join60(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
14128
14252
|
recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
|
|
14129
14253
|
const signed = sig.jwt !== null;
|
|
14130
14254
|
const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...mp ? { mutation_line: mp.line } : {}, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} };
|
|
@@ -14143,8 +14267,8 @@ var init_seal = __esm(() => {
|
|
|
14143
14267
|
});
|
|
14144
14268
|
|
|
14145
14269
|
// ../../loki-ts/src/features/receipt_dsse.ts
|
|
14146
|
-
import { createPublicKey as createPublicKey3, sign as sign2, verify as
|
|
14147
|
-
import { readFileSync as
|
|
14270
|
+
import { createPublicKey as createPublicKey3, sign as sign2, verify as verify3 } from "crypto";
|
|
14271
|
+
import { readFileSync as readFileSync46 } from "fs";
|
|
14148
14272
|
function pae(payloadType, body) {
|
|
14149
14273
|
const t = Buffer.from(payloadType, "utf8");
|
|
14150
14274
|
return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
|
|
@@ -14163,7 +14287,7 @@ function verifyEnvelope(env, pubFor) {
|
|
|
14163
14287
|
continue;
|
|
14164
14288
|
}
|
|
14165
14289
|
known++;
|
|
14166
|
-
if (
|
|
14290
|
+
if (verify3(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
|
|
14167
14291
|
kid = s.keyid;
|
|
14168
14292
|
break;
|
|
14169
14293
|
}
|
|
@@ -14221,7 +14345,7 @@ function verifyDsseReceipt(env, d) {
|
|
|
14221
14345
|
}
|
|
14222
14346
|
function runIdGuard(receiptPath, runId) {
|
|
14223
14347
|
try {
|
|
14224
|
-
return envelopeRunIdProblem(JSON.parse(
|
|
14348
|
+
return envelopeRunIdProblem(JSON.parse(readFileSync46(receiptPath, "utf8")), runId);
|
|
14225
14349
|
} catch {
|
|
14226
14350
|
return null;
|
|
14227
14351
|
}
|
|
@@ -14235,16 +14359,16 @@ var init_keys_cmd = __esm(() => {
|
|
|
14235
14359
|
});
|
|
14236
14360
|
|
|
14237
14361
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
14238
|
-
import { existsSync as existsSync39, readFileSync as
|
|
14239
|
-
import { basename as basename22, dirname as dirname27, join as
|
|
14240
|
-
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as
|
|
14362
|
+
import { existsSync as existsSync39, readFileSync as readFileSync47, renameSync as renameSync10, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
|
|
14363
|
+
import { basename as basename22, dirname as dirname27, join as join61 } from "path";
|
|
14364
|
+
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify4 } from "crypto";
|
|
14241
14365
|
function pubFor(kid, given) {
|
|
14242
14366
|
if (given)
|
|
14243
14367
|
return kidOf2(given) === kid ? given : undefined;
|
|
14244
14368
|
const active = loadSigningKey(false);
|
|
14245
14369
|
const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
|
|
14246
14370
|
try {
|
|
14247
|
-
return [createPublicKey4(
|
|
14371
|
+
return [createPublicKey4(readFileSync47(f))];
|
|
14248
14372
|
} catch {
|
|
14249
14373
|
return [];
|
|
14250
14374
|
}
|
|
@@ -14268,7 +14392,7 @@ function checkAttestation(jwt, expectedHash, given) {
|
|
|
14268
14392
|
const pub = pubFor(header.kid, given);
|
|
14269
14393
|
if (!pub)
|
|
14270
14394
|
return { status: "unchecked", reason: `no key for kid ${String(header.kid)} on this machine (set LOKI_RECEIPT_SIGNING_KEY_FILE or LOKI_RECEIPT_RETIRED_PUBKEYS)` };
|
|
14271
|
-
if (!
|
|
14395
|
+
if (!verify4(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url")))
|
|
14272
14396
|
return bad("signature does not verify");
|
|
14273
14397
|
return payload?.receipt_sha256 === expectedHash ? { status: "verified", reason: null } : bad("attestation binds a different receipt hash");
|
|
14274
14398
|
}
|
|
@@ -14276,17 +14400,17 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14276
14400
|
const bound = receipt["events_sha256"];
|
|
14277
14401
|
if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
|
|
14278
14402
|
return null;
|
|
14279
|
-
const path =
|
|
14403
|
+
const path = join61(dirname27(receiptPath), "events.jsonl");
|
|
14280
14404
|
if (!existsSync39(path))
|
|
14281
14405
|
return "events.jsonl is missing";
|
|
14282
|
-
const raw2 =
|
|
14406
|
+
const raw2 = readFileSync47(path, "utf8"), events2 = readEvents(path), lines2 = raw2.split(`
|
|
14283
14407
|
`).filter((l) => l.trim() !== "");
|
|
14284
|
-
if (events2.length !==
|
|
14408
|
+
if (events2.length !== lines2.length || events2.some((e, i) => e.seq !== i))
|
|
14285
14409
|
return "events.jsonl has a forged, edited or missing line";
|
|
14286
14410
|
if (events2.some((e) => e.type === "tamper.detected"))
|
|
14287
14411
|
return "the supervisor detected events.jsonl being modified during the run";
|
|
14288
14412
|
const h = createHash16("sha256");
|
|
14289
|
-
for (const l of
|
|
14413
|
+
for (const l of lines2) {
|
|
14290
14414
|
if (h.copy().digest("hex") === bound)
|
|
14291
14415
|
return null;
|
|
14292
14416
|
h.update(l + `
|
|
@@ -14295,7 +14419,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14295
14419
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
14296
14420
|
}
|
|
14297
14421
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
14298
|
-
const path =
|
|
14422
|
+
const path = join61(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
14299
14423
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
14300
14424
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
14301
14425
|
if (done < 0)
|
|
@@ -14303,12 +14427,12 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14303
14427
|
const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
|
|
14304
14428
|
if (!d)
|
|
14305
14429
|
return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
|
|
14306
|
-
const
|
|
14307
|
-
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(
|
|
14430
|
+
const lines2 = readFileSync47(path, "utf8").split(`
|
|
14431
|
+
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines2.slice(0, done + 1).join(`
|
|
14308
14432
|
`) + `
|
|
14309
14433
|
`).digest("hex");
|
|
14310
14434
|
const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
|
|
14311
|
-
if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !
|
|
14435
|
+
if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !verify4(null, Buffer.from(`${sha3}:false`), pub, Buffer.from(d["sig"], "base64url")))
|
|
14312
14436
|
return { verdict: "TAMPERED", reason: "log.sealed line is invalid, forged, or records a tamper" };
|
|
14313
14437
|
return null;
|
|
14314
14438
|
}
|
|
@@ -14317,7 +14441,7 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14317
14441
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
14318
14442
|
let receipt;
|
|
14319
14443
|
try {
|
|
14320
|
-
receipt = JSON.parse((deps.read ?? ((p) =>
|
|
14444
|
+
receipt = JSON.parse((deps.read ?? ((p) => readFileSync47(p, "utf8")))(receiptPath));
|
|
14321
14445
|
} catch {
|
|
14322
14446
|
return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
|
|
14323
14447
|
}
|
|
@@ -14399,8 +14523,8 @@ var init_verify_cmd = __esm(() => {
|
|
|
14399
14523
|
});
|
|
14400
14524
|
|
|
14401
14525
|
// src/server/app.ts
|
|
14402
|
-
import { existsSync as existsSync41, readFileSync as
|
|
14403
|
-
import { join as
|
|
14526
|
+
import { existsSync as existsSync41, readFileSync as readFileSync50, statSync as statSync21 } from "fs";
|
|
14527
|
+
import { join as join64, resolve as resolve15, sep as sep14 } from "path";
|
|
14404
14528
|
|
|
14405
14529
|
// node_modules/hono/dist/request/constants.js
|
|
14406
14530
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -20172,6 +20296,9 @@ var SHA = /^[0-9a-f]{40,64}$/;
|
|
|
20172
20296
|
var MAX_RECEIPT_BYTES = 1024 * 1024;
|
|
20173
20297
|
var GIT_TIMEOUT_MS = 5000;
|
|
20174
20298
|
var cache = new Map;
|
|
20299
|
+
var FAIL_TTL_MS = 60000;
|
|
20300
|
+
var failed = new Map;
|
|
20301
|
+
var diffStatGitRuns = { n: 0 };
|
|
20175
20302
|
var gitEnv = () => ({ PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" });
|
|
20176
20303
|
function parseNumstat(out) {
|
|
20177
20304
|
const files = [];
|
|
@@ -20229,13 +20356,19 @@ function diffStatFor(db, source, run) {
|
|
|
20229
20356
|
const base = typeof r?.base_sha === "string" ? r.base_sha : "", head = typeof r?.head_sha === "string" ? r.head_sha : "";
|
|
20230
20357
|
if (!SHA.test(base) || !SHA.test(head))
|
|
20231
20358
|
return null;
|
|
20359
|
+
const failKey = `${key}:${base}:${head}`;
|
|
20360
|
+
const failedAt = failed.get(failKey);
|
|
20361
|
+
if (failedAt !== undefined && Date.now() - failedAt < FAIL_TTL_MS)
|
|
20362
|
+
return null;
|
|
20232
20363
|
try {
|
|
20364
|
+
diffStatGitRuns.n++;
|
|
20233
20365
|
const out = execFileSync("git", ["-C", repoReal, "-c", "core.quotePath=false", "diff", "--numstat", "--no-renames", `${base}..${head}`, "--"], { env: gitEnv(), timeout: GIT_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
20234
20366
|
const files = parseNumstat(out);
|
|
20235
20367
|
const stat = { base, head, files, added: files.reduce((n2, f) => n2 + (f.added ?? 0), 0), removed: files.reduce((n2, f) => n2 + (f.removed ?? 0), 0), source: "git" };
|
|
20236
20368
|
cache.set(key, stat);
|
|
20237
20369
|
return stat;
|
|
20238
20370
|
} catch {
|
|
20371
|
+
failed.set(failKey, Date.now());
|
|
20239
20372
|
return null;
|
|
20240
20373
|
}
|
|
20241
20374
|
}
|
|
@@ -20604,10 +20737,10 @@ function stopReason(evs, verdict) {
|
|
|
20604
20737
|
return { reason: /[.!?]$/.test(reason) ? reason : `${reason}.`, ownRules: OWN_RULES.test(text2) };
|
|
20605
20738
|
}
|
|
20606
20739
|
if (verdict === "FAILED") {
|
|
20607
|
-
const
|
|
20608
|
-
const why = typeof
|
|
20609
|
-
if (
|
|
20610
|
-
return { reason: `The run failed in the ${
|
|
20740
|
+
const failed2 = [...evs].reverse().find((e) => e.type === "stage.failed");
|
|
20741
|
+
const why = typeof failed2?.data?.reason === "string" ? oneLine(failed2.data.reason, 240) : "";
|
|
20742
|
+
if (failed2 && failed2.stage)
|
|
20743
|
+
return { reason: `The run failed in the ${failed2.stage} stage${why ? `: ${why}` : ""}.`, ownRules: false };
|
|
20611
20744
|
const bad = evs.filter((e) => e.type === "test.result" && e.data.result === "fail").length;
|
|
20612
20745
|
return { reason: bad ? `The run failed: ${bad} check${bad === 1 ? "" : "s"} did not pass.` : "The run failed and did not record a reason.", ownRules: false };
|
|
20613
20746
|
}
|
|
@@ -22197,6 +22330,7 @@ var isObj4 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
|
22197
22330
|
var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
|
|
22198
22331
|
var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
22199
22332
|
var MAX_CHECKPOINTS = 50;
|
|
22333
|
+
var ROLLBACK_ALLOW = new Set(["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json", "state", "queue"]);
|
|
22200
22334
|
var validCheckpointId = (id) => SAFE_ID.test(id);
|
|
22201
22335
|
var storeOf = (repoDir) => join17(repoDir, ".loki", "state", "checkpoints");
|
|
22202
22336
|
var lokiOf = (repoDir) => join17(repoDir, ".loki");
|
|
@@ -22336,16 +22470,33 @@ function rollbackCheckpoint(repoDir, id, now2 = new Date) {
|
|
|
22336
22470
|
`);
|
|
22337
22471
|
let restored = 0;
|
|
22338
22472
|
const errors = [];
|
|
22473
|
+
const skipped = (src) => src === join17(real2, "state", "checkpoints");
|
|
22339
22474
|
for (const e of readdirSync4(real2, { withFileTypes: true })) {
|
|
22340
22475
|
if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
|
|
22341
22476
|
continue;
|
|
22342
|
-
|
|
22477
|
+
if (!ROLLBACK_ALLOW.has(e.name)) {
|
|
22478
|
+
errors.push(`${e.name}: not in the rollback allowlist, skipped`);
|
|
22479
|
+
continue;
|
|
22480
|
+
}
|
|
22481
|
+
const src = join17(real2, e.name), dst = join17(loki, e.name);
|
|
22343
22482
|
try {
|
|
22344
22483
|
if (lstatSync2(src).isSymbolicLink()) {
|
|
22345
22484
|
errors.push(`${e.name}: symlink skipped`);
|
|
22346
22485
|
continue;
|
|
22347
22486
|
}
|
|
22348
|
-
|
|
22487
|
+
if (existsSync8(dst) && lstatSync2(dst).isSymbolicLink()) {
|
|
22488
|
+
errors.push(`${e.name}: destination is a symlink, skipped`);
|
|
22489
|
+
continue;
|
|
22490
|
+
}
|
|
22491
|
+
cpSync(src, dst, { recursive: true, filter: (s) => {
|
|
22492
|
+
if (skipped(s))
|
|
22493
|
+
return false;
|
|
22494
|
+
if (lstatSync2(s).isSymbolicLink()) {
|
|
22495
|
+
errors.push(`${s.slice(real2.length + 1)}: nested symlink skipped`);
|
|
22496
|
+
return false;
|
|
22497
|
+
}
|
|
22498
|
+
return true;
|
|
22499
|
+
} });
|
|
22349
22500
|
restored++;
|
|
22350
22501
|
} catch (err) {
|
|
22351
22502
|
errors.push(`${e.name}: ${err.message}`);
|
|
@@ -23567,8 +23718,8 @@ function tasksList(loki, status) {
|
|
|
23567
23718
|
const out2 = order2.map((o) => ("ref" in o) ? byId.get(o.ref) : o);
|
|
23568
23719
|
for (const t of out2) {
|
|
23569
23720
|
if (t.type === "iteration" && !t.description) {
|
|
23570
|
-
const ec = t.exitCode,
|
|
23571
|
-
const o = Number.isInteger(ec) && ec === 0 && !
|
|
23721
|
+
const ec = t.exitCode, failed2 = t._terminal_outcome === "failed";
|
|
23722
|
+
const o = Number.isInteger(ec) && ec === 0 && !failed2 ? "completed cleanly (exit 0)" : Number.isInteger(ec) && ec !== 0 ? `failed (exit ${ec})` : failed2 ? "failed" : t._terminal_outcome === "completed" ? "completed" : t.status === "done" ? "finished" : null;
|
|
23572
23723
|
if (o)
|
|
23573
23724
|
t.description = `Iteration ${o}${t.provider ? `, built by ${t.provider}` : ""}.`;
|
|
23574
23725
|
}
|
|
@@ -24575,7 +24726,7 @@ function mount19(ctx) {
|
|
|
24575
24726
|
function computeStats(db, since) {
|
|
24576
24727
|
const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
|
|
24577
24728
|
const by = {};
|
|
24578
|
-
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0,
|
|
24729
|
+
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed2 = 0, satisfied = 0, other = 0;
|
|
24579
24730
|
let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
|
|
24580
24731
|
for (const r of all) {
|
|
24581
24732
|
if (r.endedAt)
|
|
@@ -24593,7 +24744,7 @@ function computeStats(db, since) {
|
|
|
24593
24744
|
else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
|
|
24594
24745
|
satisfied++;
|
|
24595
24746
|
else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
|
|
24596
|
-
|
|
24747
|
+
failed2++;
|
|
24597
24748
|
else
|
|
24598
24749
|
other++;
|
|
24599
24750
|
}
|
|
@@ -24625,8 +24776,8 @@ function computeStats(db, since) {
|
|
|
24625
24776
|
runs_running: running,
|
|
24626
24777
|
by_verdict: by,
|
|
24627
24778
|
blocked_waiting: blocked,
|
|
24628
|
-
verified_rate: verified + unchecked +
|
|
24629
|
-
breakdown: { verified, failed, already_satisfied: satisfied, other },
|
|
24779
|
+
verified_rate: verified + unchecked + failed2 ? verified / (verified + unchecked + failed2) : null,
|
|
24780
|
+
breakdown: { verified, failed: failed2, already_satisfied: satisfied, other },
|
|
24630
24781
|
verified_unchecked: unchecked,
|
|
24631
24782
|
keys_configured: pubkeysFromEnv().configured === true,
|
|
24632
24783
|
...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
|
|
@@ -24650,9 +24801,9 @@ function mount20(ctx) {
|
|
|
24650
24801
|
}
|
|
24651
24802
|
|
|
24652
24803
|
// src/server/routes/verify.ts
|
|
24653
|
-
import { readFileSync as
|
|
24804
|
+
import { readFileSync as readFileSync48, realpathSync as realpathSync20 } from "fs";
|
|
24654
24805
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
24655
|
-
import { join as
|
|
24806
|
+
import { join as join62, sep as sep13 } from "path";
|
|
24656
24807
|
init_verify_cmd();
|
|
24657
24808
|
init_seal();
|
|
24658
24809
|
init_receipt_dsse();
|
|
@@ -24679,11 +24830,11 @@ function mount21(ctx) {
|
|
|
24679
24830
|
return done(404, "receipt not available", "not_found");
|
|
24680
24831
|
let runDir, file;
|
|
24681
24832
|
try {
|
|
24682
|
-
const root = realpathSync20(
|
|
24683
|
-
runDir = realpathSync20(
|
|
24684
|
-
if (runDir !==
|
|
24833
|
+
const root = realpathSync20(join62(repo.p, ".loki", "runs"));
|
|
24834
|
+
runDir = realpathSync20(join62(root, run2));
|
|
24835
|
+
if (runDir !== join62(root, run2))
|
|
24685
24836
|
return done(404, "receipt not found", "not_found");
|
|
24686
|
-
file = realpathSync20(
|
|
24837
|
+
file = realpathSync20(join62(runDir, "receipt.json"));
|
|
24687
24838
|
} catch {
|
|
24688
24839
|
return done(404, "receipt not found", "not_found");
|
|
24689
24840
|
}
|
|
@@ -24702,7 +24853,7 @@ function mount21(ctx) {
|
|
|
24702
24853
|
sha3 = r.receiptSha256 ?? null;
|
|
24703
24854
|
}
|
|
24704
24855
|
try {
|
|
24705
|
-
outcome = outcomeOf(JSON.parse(
|
|
24856
|
+
outcome = outcomeOf(JSON.parse(readFileSync48(file, "utf8")));
|
|
24706
24857
|
} catch {
|
|
24707
24858
|
outcome = "UNREADABLE";
|
|
24708
24859
|
}
|
|
@@ -24764,8 +24915,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
24764
24915
|
}
|
|
24765
24916
|
|
|
24766
24917
|
// src/server/legacy/shim.ts
|
|
24767
|
-
import { existsSync as existsSync40, readFileSync as
|
|
24768
|
-
import { join as
|
|
24918
|
+
import { existsSync as existsSync40, readFileSync as readFileSync49 } from "fs";
|
|
24919
|
+
import { join as join63 } from "path";
|
|
24769
24920
|
|
|
24770
24921
|
// src/server/legacy/routes.ts
|
|
24771
24922
|
var LEGACY_ROUTES = [
|
|
@@ -25108,11 +25259,12 @@ function metricsMapped(repoDir) {
|
|
|
25108
25259
|
// src/server/legacy/routes-control.ts
|
|
25109
25260
|
function controlMapped(db, repoDir) {
|
|
25110
25261
|
const loki = () => lokiDirOf(repoDir);
|
|
25262
|
+
const ctl = (action) => (c) => peerIsLoopback(c) && isLoopbackHost(c.req.header("host")) ? runControl(db, repoDir, action, c, "legacy") : c.json({ error: "loopback only" }, 403);
|
|
25111
25263
|
return {
|
|
25112
|
-
"POST /api/control/pause": (
|
|
25113
|
-
"POST /api/control/resume": (
|
|
25114
|
-
"POST /api/control/stop": (
|
|
25115
|
-
"POST /api/council/force-review": (
|
|
25264
|
+
"POST /api/control/pause": ctl("pause"),
|
|
25265
|
+
"POST /api/control/resume": ctl("resume"),
|
|
25266
|
+
"POST /api/control/stop": ctl("stop"),
|
|
25267
|
+
"POST /api/council/force-review": ctl("council-review"),
|
|
25116
25268
|
"GET /api/council/state": (c) => c.json(councilState(loki())),
|
|
25117
25269
|
"GET /api/council/verdicts": (c) => {
|
|
25118
25270
|
const n2 = intQ(c.req.query("limit"), 1000);
|
|
@@ -25137,10 +25289,10 @@ var logged = new Set;
|
|
|
25137
25289
|
var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
|
|
25138
25290
|
var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
25139
25291
|
var repoVersion = () => {
|
|
25140
|
-
for (const f of [
|
|
25292
|
+
for (const f of [join63(import.meta.dir, "../../../../../VERSION")]) {
|
|
25141
25293
|
try {
|
|
25142
25294
|
if (existsSync40(f))
|
|
25143
|
-
return
|
|
25295
|
+
return readFileSync49(f, "utf8").trim();
|
|
25144
25296
|
} catch {}
|
|
25145
25297
|
}
|
|
25146
25298
|
return "unknown";
|
|
@@ -25322,7 +25474,7 @@ function legacyShim(opts) {
|
|
|
25322
25474
|
|
|
25323
25475
|
// src/server/app.ts
|
|
25324
25476
|
var MAX_BODY2 = 1e6;
|
|
25325
|
-
var defaultUiDir = () => [
|
|
25477
|
+
var defaultUiDir = () => [join64(import.meta.dir, "../../ui/dist"), join64(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join64(d, "index.html")));
|
|
25326
25478
|
function createApp(opts) {
|
|
25327
25479
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
25328
25480
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -25351,10 +25503,10 @@ function createApp(opts) {
|
|
|
25351
25503
|
await next();
|
|
25352
25504
|
});
|
|
25353
25505
|
const readInstalled = () => {
|
|
25354
|
-
for (const f of [process.env.LOKI_VERSION_FILE,
|
|
25506
|
+
for (const f of [process.env.LOKI_VERSION_FILE, join64(import.meta.dir, "../../../../VERSION")]) {
|
|
25355
25507
|
try {
|
|
25356
25508
|
if (f)
|
|
25357
|
-
return
|
|
25509
|
+
return readFileSync50(f, "utf8").trim() || "unknown";
|
|
25358
25510
|
} catch {}
|
|
25359
25511
|
}
|
|
25360
25512
|
return "unknown";
|
|
@@ -25469,7 +25621,7 @@ function createApp(opts) {
|
|
|
25469
25621
|
return new Response(Bun.file(f));
|
|
25470
25622
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
25471
25623
|
return c.json({ error: "not found" }, 404);
|
|
25472
|
-
return new Response(Bun.file(
|
|
25624
|
+
return new Response(Bun.file(join64(root, "index.html")));
|
|
25473
25625
|
});
|
|
25474
25626
|
return { app, db, close: () => {
|
|
25475
25627
|
clearInterval(reconciler);
|