loki-mode 11.8.1 → 11.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/backlog.py +15 -3
- package/autonomy/lib/workspace.py +95 -4
- package/autonomy/loki +189 -108
- package/autonomy/trigger-server.py +302 -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 +25 -1
- package/loki-ts/dist/loki.js +1016 -946
- 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 +457 -279
- 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-D3PgG7SH.js} +1 -1
- package/web-app/dist/assets/{Avatar-0BTVsQ2A.js → Avatar-Ct77hvlG.js} +1 -1
- package/web-app/dist/assets/{Badge-Bbq8CXgn.js → Badge-Cg_gZaJF.js} +1 -1
- package/web-app/dist/assets/{Button-CKD4n0ld.js → Button-i9r2G84n.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DJhLNLAz.js → CockpitPage-C3UyLXyp.js} +1 -1
- package/web-app/dist/assets/{ComparePage-Di2akuII.js → ComparePage-B1dXHga_.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-9lhsYG-z.js → ErrorBoundary-CQ_xREw0.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CQDGXvDr.js → EvidenceReceiptPanel-DwUxd_Xi.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Y_w2k9Ej.js → GitHubIssuesPanel-C0jAnuAd.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-pMSQedCv.js → GitHubPRsPanel-B4pNXJMz.js} +1 -1
- package/web-app/dist/assets/{HomePage-CIAkiGdt.js → HomePage-BC9UTS1l.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BES4Pk6k.js → LoginPage-CnpdwK8F.js} +1 -1
- package/web-app/dist/assets/{MagicPage-B154fMhi.js → MagicPage-Bj0lZuNp.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BcNyaiUO.js → MetricsPage-yyw3VnfX.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D28FqYxG.js → NotFoundPage-LMxkJP15.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-C_O3u8Jx.js → ProjectPage-2x0MRkNu.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-uqdahuPA.js → ProjectsPage-A8J8o1gj.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DLvVA9OZ.js → SettingsPage-fwvOVcRb.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-BAZP8vsm.js → ShowcasePage-DqI4sUwk.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DPYZkLWh.js → SystemSettingsPage-CyytfNo_.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BgSyb5Pd.js → TeamsPage-BTTe76W1.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-D_Jh5VU1.js → TemplatesPage-CIp3OTlL.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-4v8poDKb.js → TerminalOutput-dX5L4ky-.js} +1 -1
- package/web-app/dist/assets/{activity-CE2b6kdw.js → activity-uHXr2AaF.js} +1 -1
- package/web-app/dist/assets/{bell-CxJDlLSR.js → bell-D6RbxRMo.js} +1 -1
- package/web-app/dist/assets/{bot-C2WuK4zw.js → bot-D31w2Mu6.js} +1 -1
- package/web-app/dist/assets/{check-81epVldw.js → check-CoKJksbr.js} +1 -1
- package/web-app/dist/assets/{chevron-left-B6EXh_3C.js → chevron-left-Bjx7oX7s.js} +1 -1
- package/web-app/dist/assets/{circle-alert-0iqoo-iy.js → circle-alert-DOAh8Jmi.js} +1 -1
- package/web-app/dist/assets/{clock-CFrBpNUp.js → clock-DgEXPNWH.js} +1 -1
- package/web-app/dist/assets/{cloud-BBSFhUT_.js → cloud-0dXJFzBv.js} +1 -1
- package/web-app/dist/assets/{code-xml-BCU2V1rq.js → code-xml-DytQ_bq7.js} +1 -1
- package/web-app/dist/assets/{database-D2hvO52K.js → database-Qyjryb72.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CXFA9b6T.js → dollar-sign-BzhTVy1e.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CqTCEEfS.js → file-code-corner-DTdSOALw.js} +1 -1
- package/web-app/dist/assets/{file-plus-DYFbkGbs.js → file-plus-VdjOG1RK.js} +1 -1
- package/web-app/dist/assets/{globe-DpYHLobk.js → globe-BhxNOfyM.js} +1 -1
- package/web-app/dist/assets/{hammer-CZ97SEYo.js → hammer-MQzrw8ee.js} +1 -1
- package/web-app/dist/assets/{index-DzswgpRo.js → index-DaxrMbHf.js} +3 -3
- package/web-app/dist/assets/{layers-2Tq0edx4.js → layers-C7nusTP-.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BFP4kUX-.js → loader-circle-B1qBkgOY.js} +1 -1
- package/web-app/dist/assets/{lock-DkKdZH4d.js → lock-BdkOxK2O.js} +1 -1
- package/web-app/dist/assets/{package-ChOGD8yf.js → package-RVyK96Zn.js} +1 -1
- package/web-app/dist/assets/{plus-hggB1o6b.js → plus-ntmwFRrp.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DYwVLOeF.js → refresh-cw-BJ39b_PP.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CUdY506q.js → rotate-ccw-uPx_-3vz.js} +1 -1
- package/web-app/dist/assets/{scroll-text-Wvidz5uN.js → scroll-text-D9qvXkj-.js} +1 -1
- package/web-app/dist/assets/{server-O11QmkCN.js → server-gs4xmD8Y.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DXXx27uV.js → shield-alert-O2_YffGI.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DF8ivs3A.js → thumbs-up-B3RVBhQx.js} +1 -1
- package/web-app/dist/assets/{trash-2-LA6PW0Nf.js → trash-2-DEUt7qhq.js} +1 -1
- package/web-app/dist/assets/{trending-down-Bw_vLKHT.js → trending-down-oyTns4UZ.js} +1 -1
- package/web-app/dist/assets/{trending-up-tX8motha.js → trending-up-CdpZK0Oc.js} +1 -1
- package/web-app/dist/assets/{upload-CxtTWJd2.js → upload-D9sbjjC8.js} +1 -1
- package/web-app/dist/assets/{usePolling-D-P5Iofq.js → usePolling-6ognrnyV.js} +1 -1
- package/web-app/dist/assets/{user-dKTcuUFq.js → user-CBKc8QoI.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,12 @@ 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;
|
|
10512
|
+
}
|
|
10513
|
+
function summaryPassFailCount(out2) {
|
|
10514
|
+
return ["pass", "fail"].reduce((t, k) => t + +([...out2.matchAll(new RegExp(`^(?:#|\\u2139) ${k} (\\d+)$`, "gm"))].pop()?.[1] ?? 0), 0);
|
|
10424
10515
|
}
|
|
10425
10516
|
function skipped(raw2) {
|
|
10426
10517
|
return stripAnsi(raw2).split(`
|
|
@@ -10488,7 +10579,7 @@ function markExecuted(a, executed) {
|
|
|
10488
10579
|
return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) };
|
|
10489
10580
|
}
|
|
10490
10581
|
function classifyRisk(files, env = process.env) {
|
|
10491
|
-
const
|
|
10582
|
+
const lines2 = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
|
|
10492
10583
|
const sensitive = files.flatMap((f) => {
|
|
10493
10584
|
const hit = SENSITIVE.find(([, re]) => re.test(f.path));
|
|
10494
10585
|
return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
|
|
@@ -10506,14 +10597,14 @@ function classifyRisk(files, env = process.env) {
|
|
|
10506
10597
|
high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
|
|
10507
10598
|
if (files.length >= FILES_HIGH)
|
|
10508
10599
|
high.push(`${files.length} files >= ${FILES_HIGH}`);
|
|
10509
|
-
if (
|
|
10510
|
-
high.push(`${
|
|
10600
|
+
if (lines2 >= LINES_HIGH)
|
|
10601
|
+
high.push(`${lines2} changed lines >= ${LINES_HIGH}`);
|
|
10511
10602
|
if (testsModified.length)
|
|
10512
10603
|
med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
|
|
10513
10604
|
if (files.length >= FILES_MEDIUM)
|
|
10514
10605
|
med.push(`${files.length} files >= ${FILES_MEDIUM}`);
|
|
10515
|
-
if (
|
|
10516
|
-
med.push(`${
|
|
10606
|
+
if (lines2 >= LINES_MEDIUM)
|
|
10607
|
+
med.push(`${lines2} changed lines >= ${LINES_MEDIUM}`);
|
|
10517
10608
|
const computed = high.length ? "high" : med.length ? "medium" : "low";
|
|
10518
10609
|
const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
|
|
10519
10610
|
let tier = computed, override, ignored;
|
|
@@ -10536,7 +10627,7 @@ function classifyRisk(files, env = process.env) {
|
|
|
10536
10627
|
tier,
|
|
10537
10628
|
computed,
|
|
10538
10629
|
reasons,
|
|
10539
|
-
stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10630
|
+
stats: { files: files.length, lines: lines2, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10540
10631
|
checks_selected: plan.selected,
|
|
10541
10632
|
checks_deferred,
|
|
10542
10633
|
...override ? { override } : {},
|
|
@@ -10571,7 +10662,7 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10571
10662
|
return f;
|
|
10572
10663
|
let n2 = 0;
|
|
10573
10664
|
try {
|
|
10574
|
-
n2 =
|
|
10665
|
+
n2 = readFileSync29(join40(repoDir, path), "utf8").split(`
|
|
10575
10666
|
`).length;
|
|
10576
10667
|
} catch {}
|
|
10577
10668
|
return { path, added: n2, deleted: 0, status: "added" };
|
|
@@ -10579,11 +10670,11 @@ function diffFiles(repoDir, baseSha) {
|
|
|
10579
10670
|
}
|
|
10580
10671
|
function recordRisk(repoDir, a) {
|
|
10581
10672
|
try {
|
|
10582
|
-
const dir =
|
|
10673
|
+
const dir = join40(repoDir, ".loki", "state"), p = join40(dir, "risk-tier.json");
|
|
10583
10674
|
mkdirSync12(dir, { recursive: true });
|
|
10584
10675
|
writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
|
|
10585
10676
|
`);
|
|
10586
|
-
|
|
10677
|
+
renameSync6(`${p}.tmp`, p);
|
|
10587
10678
|
return p;
|
|
10588
10679
|
} catch {
|
|
10589
10680
|
return null;
|
|
@@ -10624,10 +10715,10 @@ var init_check_result = __esm(() => {
|
|
|
10624
10715
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10625
10716
|
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
|
|
10626
10717
|
import { tmpdir as tmpdir7 } from "os";
|
|
10627
|
-
import { basename as basename14, extname as extname2, join as
|
|
10718
|
+
import { basename as basename14, extname as extname2, join as join41 } from "path";
|
|
10628
10719
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
10629
10720
|
return changed.filter((f) => {
|
|
10630
|
-
if (out2.includes(f) || out2.includes(
|
|
10721
|
+
if (out2.includes(f) || out2.includes(join41(repoDir, f)))
|
|
10631
10722
|
return true;
|
|
10632
10723
|
const segs = f.split("/");
|
|
10633
10724
|
if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
|
|
@@ -10738,7 +10829,7 @@ async function loadErrorIsHarnessOwned(i) {
|
|
|
10738
10829
|
return false;
|
|
10739
10830
|
if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
|
|
10740
10831
|
return false;
|
|
10741
|
-
const dir = mkdtempSync5(
|
|
10832
|
+
const dir = mkdtempSync5(join41(tmpdir7(), "e10-loadown-"));
|
|
10742
10833
|
const git3 = (args) => {
|
|
10743
10834
|
safeGit(i.repoDir, args, { stdio: "ignore" });
|
|
10744
10835
|
};
|
|
@@ -10770,7 +10861,7 @@ var init_load_owner = __esm(() => {
|
|
|
10770
10861
|
});
|
|
10771
10862
|
|
|
10772
10863
|
// ../../loki-ts/src/util/wall_owned.ts
|
|
10773
|
-
import { isAbsolute as isAbsolute4, join as
|
|
10864
|
+
import { isAbsolute as isAbsolute4, join as join42, relative as relative4 } from "path";
|
|
10774
10865
|
function failingLocations(raw2, cwd, repoDir) {
|
|
10775
10866
|
const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
|
|
10776
10867
|
`), files = new Set;
|
|
@@ -10802,7 +10893,7 @@ function failingLocations(raw2, cwd, repoDir) {
|
|
|
10802
10893
|
}
|
|
10803
10894
|
if (errors2 === 0 || located !== errors2)
|
|
10804
10895
|
return null;
|
|
10805
|
-
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f :
|
|
10896
|
+
return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join42(cwd, f)));
|
|
10806
10897
|
}
|
|
10807
10898
|
function wallOwnedFailure(raw2, cwd, repoDir, wall) {
|
|
10808
10899
|
const locs = failingLocations(raw2, cwd, repoDir);
|
|
@@ -10831,11 +10922,11 @@ var init_state = __esm(() => {
|
|
|
10831
10922
|
});
|
|
10832
10923
|
|
|
10833
10924
|
// ../../loki-ts/src/runner/repo_profile.ts
|
|
10834
|
-
import { existsSync as existsSync22, readFileSync as
|
|
10835
|
-
import { basename as basename15, join as
|
|
10925
|
+
import { existsSync as existsSync22, readFileSync as readFileSync30, mkdirSync as mkdirSync13, readdirSync as readdirSync12, renameSync as renameSync7, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
|
|
10926
|
+
import { basename as basename15, join as join43, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
|
|
10836
10927
|
function loadProfile(repoDir) {
|
|
10837
10928
|
try {
|
|
10838
|
-
const p = JSON.parse(
|
|
10929
|
+
const p = JSON.parse(readFileSync30(commandProfilePath(repoDir), "utf8"));
|
|
10839
10930
|
return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
|
|
10840
10931
|
} catch {
|
|
10841
10932
|
return null;
|
|
@@ -10866,7 +10957,7 @@ function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
|
|
|
10866
10957
|
const block = new Set(blocked);
|
|
10867
10958
|
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
10959
|
}
|
|
10869
|
-
var commandProfilePath = (repoDir) =>
|
|
10960
|
+
var commandProfilePath = (repoDir) => join43(repoDir, ".loki", "state", "repo-profile.json");
|
|
10870
10961
|
var init_repo_profile = __esm(() => {
|
|
10871
10962
|
init_failures();
|
|
10872
10963
|
init_check_result();
|
|
@@ -10879,20 +10970,20 @@ var init_repo_profile = __esm(() => {
|
|
|
10879
10970
|
import { createHash as createHash10 } from "crypto";
|
|
10880
10971
|
import {
|
|
10881
10972
|
existsSync as existsSync23,
|
|
10882
|
-
readdirSync as
|
|
10883
|
-
readFileSync as
|
|
10973
|
+
readdirSync as readdirSync13,
|
|
10974
|
+
readFileSync as readFileSync31,
|
|
10884
10975
|
statSync as statSync16,
|
|
10885
10976
|
writeFileSync as writeFileSync17,
|
|
10886
10977
|
mkdirSync as mkdirSync14
|
|
10887
10978
|
} from "fs";
|
|
10888
|
-
import { basename as basename16, dirname as dirname13, join as
|
|
10979
|
+
import { basename as basename16, dirname as dirname13, join as join44, resolve as resolve12 } from "path";
|
|
10889
10980
|
function readManifest(path) {
|
|
10890
10981
|
if (!existsSync23(path)) {
|
|
10891
10982
|
return null;
|
|
10892
10983
|
}
|
|
10893
10984
|
let raw2;
|
|
10894
10985
|
try {
|
|
10895
|
-
raw2 =
|
|
10986
|
+
raw2 = readFileSync31(path, "utf8");
|
|
10896
10987
|
} catch {
|
|
10897
10988
|
return null;
|
|
10898
10989
|
}
|
|
@@ -10929,7 +11020,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10929
11020
|
const seen = new Set;
|
|
10930
11021
|
let children;
|
|
10931
11022
|
try {
|
|
10932
|
-
children =
|
|
11023
|
+
children = readdirSync13(rootDir);
|
|
10933
11024
|
} catch {
|
|
10934
11025
|
children = [];
|
|
10935
11026
|
}
|
|
@@ -10944,7 +11035,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10944
11035
|
for (const child of children) {
|
|
10945
11036
|
if (!matchPattern(name, child))
|
|
10946
11037
|
continue;
|
|
10947
|
-
const full =
|
|
11038
|
+
const full = join44(rootDir, child);
|
|
10948
11039
|
if (seen.has(full))
|
|
10949
11040
|
continue;
|
|
10950
11041
|
let st;
|
|
@@ -10961,7 +11052,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10961
11052
|
}
|
|
10962
11053
|
if (declared.length === 0) {
|
|
10963
11054
|
for (const child of children) {
|
|
10964
|
-
const full =
|
|
11055
|
+
const full = join44(rootDir, child);
|
|
10965
11056
|
if (seen.has(full))
|
|
10966
11057
|
continue;
|
|
10967
11058
|
let st;
|
|
@@ -10972,7 +11063,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10972
11063
|
}
|
|
10973
11064
|
if (!st.isDirectory())
|
|
10974
11065
|
continue;
|
|
10975
|
-
if (!existsSync23(
|
|
11066
|
+
if (!existsSync23(join44(full, ".loki", "app.json")))
|
|
10976
11067
|
continue;
|
|
10977
11068
|
seen.add(full);
|
|
10978
11069
|
out2.push(full);
|
|
@@ -10993,17 +11084,17 @@ function cacheKey(manifestPaths) {
|
|
|
10993
11084
|
}
|
|
10994
11085
|
function writeCache(targetDir, key, result) {
|
|
10995
11086
|
try {
|
|
10996
|
-
const cacheDir =
|
|
11087
|
+
const cacheDir = join44(targetDir, ".loki", "state");
|
|
10997
11088
|
mkdirSync14(cacheDir, { recursive: true });
|
|
10998
|
-
const cachePath =
|
|
11089
|
+
const cachePath = join44(cacheDir, "project-graph.json");
|
|
10999
11090
|
writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
|
|
11000
11091
|
} catch {}
|
|
11001
11092
|
}
|
|
11002
11093
|
function discoverProjectGraph(targetDir) {
|
|
11003
11094
|
const absTarget = resolve12(targetDir);
|
|
11004
|
-
const targetManifest =
|
|
11095
|
+
const targetManifest = join44(absTarget, ".loki", "app.json");
|
|
11005
11096
|
const parentDir = dirname13(absTarget);
|
|
11006
|
-
const parentManifest =
|
|
11097
|
+
const parentManifest = join44(parentDir, ".loki", "app.json");
|
|
11007
11098
|
const examined = [];
|
|
11008
11099
|
let chosenManifest = null;
|
|
11009
11100
|
let chosenRoot = "";
|
|
@@ -11023,7 +11114,7 @@ function discoverProjectGraph(targetDir) {
|
|
|
11023
11114
|
const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
|
|
11024
11115
|
const filtered = [];
|
|
11025
11116
|
for (const m of members) {
|
|
11026
|
-
const sibManifestPath =
|
|
11117
|
+
const sibManifestPath = join44(m, ".loki", "app.json");
|
|
11027
11118
|
if (existsSync23(sibManifestPath)) {
|
|
11028
11119
|
examined.push(sibManifestPath);
|
|
11029
11120
|
const sibManifest = readManifest(sibManifestPath);
|
|
@@ -11096,15 +11187,15 @@ var init_steer = __esm(() => {
|
|
|
11096
11187
|
});
|
|
11097
11188
|
|
|
11098
11189
|
// ../../loki-ts/src/util/pr_lessons.ts
|
|
11099
|
-
import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as
|
|
11100
|
-
import { dirname as dirname14, join as
|
|
11190
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
|
|
11191
|
+
import { dirname as dirname14, join as join45 } from "path";
|
|
11101
11192
|
import { createHash as createHash11 } from "crypto";
|
|
11102
11193
|
function loadLessons(repoDir) {
|
|
11103
11194
|
const p = lessonsPath(repoDir);
|
|
11104
11195
|
if (!existsSync24(p))
|
|
11105
11196
|
return [];
|
|
11106
11197
|
try {
|
|
11107
|
-
const j = JSON.parse(
|
|
11198
|
+
const j = JSON.parse(readFileSync32(p, "utf-8"));
|
|
11108
11199
|
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
11109
11200
|
} catch {
|
|
11110
11201
|
return [];
|
|
@@ -11146,7 +11237,7 @@ function recordRunVerdict(repoDir, runId, verdict) {
|
|
|
11146
11237
|
if (dirty)
|
|
11147
11238
|
save(repoDir, lessons);
|
|
11148
11239
|
}
|
|
11149
|
-
var lessonsPath = (repoDir) =>
|
|
11240
|
+
var lessonsPath = (repoDir) => join45(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
|
|
11150
11241
|
var init_pr_lessons = __esm(() => {
|
|
11151
11242
|
init_atomic();
|
|
11152
11243
|
KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
|
|
@@ -11158,11 +11249,30 @@ var init_model_downgrades = __esm(() => {
|
|
|
11158
11249
|
init_decision();
|
|
11159
11250
|
});
|
|
11160
11251
|
|
|
11252
|
+
// ../../loki-ts/src/e10ext/select.ts
|
|
11253
|
+
var PYTEST_COLLECTION_EXITS;
|
|
11254
|
+
var init_select = __esm(() => {
|
|
11255
|
+
PYTEST_COLLECTION_EXITS = new Set([2, 3, 4, 5]);
|
|
11256
|
+
});
|
|
11257
|
+
|
|
11258
|
+
// ../../loki-ts/src/e10ext/treeswap.ts
|
|
11259
|
+
var init_treeswap = __esm(() => {
|
|
11260
|
+
init_safe_git();
|
|
11261
|
+
});
|
|
11262
|
+
|
|
11263
|
+
// ../../loki-ts/src/features/attempts.ts
|
|
11264
|
+
var init_attempts = __esm(() => {
|
|
11265
|
+
init_credential_env();
|
|
11266
|
+
init_safe_git();
|
|
11267
|
+
init_select();
|
|
11268
|
+
init_treeswap();
|
|
11269
|
+
});
|
|
11270
|
+
|
|
11161
11271
|
// ../../loki-ts/src/providers/mcp_config.ts
|
|
11162
11272
|
var init_mcp_config = () => {};
|
|
11163
11273
|
|
|
11164
11274
|
// ../../loki-ts/src/providers/claude_flags.ts
|
|
11165
|
-
import { existsSync as existsSync25, readFileSync as
|
|
11275
|
+
import { existsSync as existsSync25, readFileSync as readFileSync33 } from "fs";
|
|
11166
11276
|
import { resolve as resolvePath, dirname as dirname15 } from "path";
|
|
11167
11277
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11168
11278
|
function designArchetypesText() {
|
|
@@ -11174,7 +11284,7 @@ function designArchetypesText() {
|
|
|
11174
11284
|
]) {
|
|
11175
11285
|
const p = resolvePath(moduleDir, ...rel);
|
|
11176
11286
|
if (existsSync25(p))
|
|
11177
|
-
return
|
|
11287
|
+
return readFileSync33(p, "utf8");
|
|
11178
11288
|
}
|
|
11179
11289
|
} catch {}
|
|
11180
11290
|
return "";
|
|
@@ -11252,14 +11362,14 @@ var init_providers = __esm(() => {
|
|
|
11252
11362
|
});
|
|
11253
11363
|
|
|
11254
11364
|
// ../../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
|
|
11365
|
+
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
|
|
11366
|
+
import { join as join46 } from "path";
|
|
11257
11367
|
function loadRouteRecord(runDir) {
|
|
11258
11368
|
try {
|
|
11259
|
-
const p =
|
|
11369
|
+
const p = join46(runDir, ROUTE_RECORD_FILE);
|
|
11260
11370
|
if (!existsSync26(p) || !lstatSync9(p).isFile())
|
|
11261
11371
|
return;
|
|
11262
|
-
const v = JSON.parse(
|
|
11372
|
+
const v = JSON.parse(readFileSync34(p, "utf8"));
|
|
11263
11373
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
11264
11374
|
} catch {
|
|
11265
11375
|
return;
|
|
@@ -11269,7 +11379,7 @@ var ROUTE_RECORD_FILE = "route.json";
|
|
|
11269
11379
|
var init_route_record = () => {};
|
|
11270
11380
|
|
|
11271
11381
|
// ../../loki-ts/src/runner/router/history_store.ts
|
|
11272
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as
|
|
11382
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync35, renameSync as renameSync8, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
|
|
11273
11383
|
import { resolve as resolve13 } from "path";
|
|
11274
11384
|
function validRun(v) {
|
|
11275
11385
|
if (typeof v !== "object" || v === null)
|
|
@@ -11285,7 +11395,7 @@ function readRunHistory(key, cacheRoot) {
|
|
|
11285
11395
|
if (!existsSync27(path))
|
|
11286
11396
|
return [];
|
|
11287
11397
|
try {
|
|
11288
|
-
const parsed = JSON.parse(
|
|
11398
|
+
const parsed = JSON.parse(readFileSync35(path, "utf8"));
|
|
11289
11399
|
const runs2 = parsed?.runs;
|
|
11290
11400
|
return Array.isArray(runs2) ? runs2.filter(validRun) : [];
|
|
11291
11401
|
} catch {
|
|
@@ -11300,7 +11410,7 @@ function appendRunOutcome(key, outcome, cacheRoot) {
|
|
|
11300
11410
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
11301
11411
|
try {
|
|
11302
11412
|
writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
|
|
11303
|
-
|
|
11413
|
+
renameSync8(tmp, target);
|
|
11304
11414
|
} catch (e) {
|
|
11305
11415
|
rmSync11(tmp, { force: true, recursive: false });
|
|
11306
11416
|
throw e;
|
|
@@ -11319,7 +11429,7 @@ var init_history_store = __esm(() => {
|
|
|
11319
11429
|
});
|
|
11320
11430
|
|
|
11321
11431
|
// ../../loki-ts/src/runner/router/history.ts
|
|
11322
|
-
import { dirname as dirname16, join as
|
|
11432
|
+
import { dirname as dirname16, join as join47, resolve as resolve14 } from "path";
|
|
11323
11433
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11324
11434
|
function shapeKey(model) {
|
|
11325
11435
|
if (!model || model.status !== "ok")
|
|
@@ -11420,6 +11530,7 @@ var init_implement = __esm(() => {
|
|
|
11420
11530
|
init_context();
|
|
11421
11531
|
init_model_downgrades();
|
|
11422
11532
|
init_sizing();
|
|
11533
|
+
init_attempts();
|
|
11423
11534
|
init_plan();
|
|
11424
11535
|
init_verify();
|
|
11425
11536
|
init_resolve();
|
|
@@ -11479,7 +11590,7 @@ var init_worker = __esm(() => {
|
|
|
11479
11590
|
});
|
|
11480
11591
|
|
|
11481
11592
|
// ../../loki-ts/src/project_model/package_suite.ts
|
|
11482
|
-
import { join as
|
|
11593
|
+
import { join as join48 } from "path";
|
|
11483
11594
|
async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
11484
11595
|
const api = loadProjectApi(repoDir);
|
|
11485
11596
|
if (!isMultiRoot(api))
|
|
@@ -11490,7 +11601,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11490
11601
|
for (const pkg of withTests) {
|
|
11491
11602
|
const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
|
|
11492
11603
|
const started = Date.now();
|
|
11493
|
-
const r = await run(["bash", "-c", tc.cmd], { cwd:
|
|
11604
|
+
const r = await run(["bash", "-c", tc.cmd], { cwd: join48(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11494
11605
|
const duration_s = (Date.now() - started) / 1000;
|
|
11495
11606
|
if (signal.aborted) {
|
|
11496
11607
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
@@ -11499,7 +11610,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
|
11499
11610
|
}
|
|
11500
11611
|
const out2 = `${r.stdout}
|
|
11501
11612
|
${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:
|
|
11613
|
+
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
11614
|
if (lr) {
|
|
11504
11615
|
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
11505
11616
|
notProven.add(`${lr} (${name}; harness-owned)`);
|
|
@@ -11533,7 +11644,7 @@ __export(exports_deep, {
|
|
|
11533
11644
|
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
|
|
11534
11645
|
});
|
|
11535
11646
|
import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
|
|
11536
|
-
import { join as
|
|
11647
|
+
import { join as join49 } from "path";
|
|
11537
11648
|
async function runFullSuite(ctx, signal, opts, checks, notProven) {
|
|
11538
11649
|
if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
|
|
11539
11650
|
return;
|
|
@@ -11630,7 +11741,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
|
11630
11741
|
}
|
|
11631
11742
|
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
11743
|
const started = Date.now();
|
|
11633
|
-
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) =>
|
|
11744
|
+
const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join49(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
|
|
11634
11745
|
const durationS = (Date.now() - started) / 1000;
|
|
11635
11746
|
if (r.exitCode === 3) {
|
|
11636
11747
|
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
|
|
@@ -11651,10 +11762,10 @@ function commentArgv(prNumber, bodyFile) {
|
|
|
11651
11762
|
return ["comment", prNumber, bodyFile];
|
|
11652
11763
|
}
|
|
11653
11764
|
function buildCommentBody(checks, notProven, statusState) {
|
|
11654
|
-
const
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
return `${
|
|
11765
|
+
const lines2 = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
|
|
11766
|
+
lines2.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
|
|
11767
|
+
lines2.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
|
|
11768
|
+
return `${lines2.join(`
|
|
11658
11769
|
`)}
|
|
11659
11770
|
`;
|
|
11660
11771
|
}
|
|
@@ -11671,7 +11782,7 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
|
|
|
11671
11782
|
const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
|
|
11672
11783
|
if (prNumber) {
|
|
11673
11784
|
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11674
|
-
const bodyFile =
|
|
11785
|
+
const bodyFile = join49(ctx.runDir, "deep-comment.md");
|
|
11675
11786
|
writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
|
|
11676
11787
|
const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
11677
11788
|
if (commentResult.exitCode !== 0)
|
|
@@ -11725,7 +11836,7 @@ async function runDeep(ctx, signal, opts = {}) {
|
|
|
11725
11836
|
const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
|
|
11726
11837
|
const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
11727
11838
|
mkdirSync18(ctx.runDir, { recursive: true });
|
|
11728
|
-
writeFileSync20(
|
|
11839
|
+
writeFileSync20(join49(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
|
|
11729
11840
|
`);
|
|
11730
11841
|
const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
|
|
11731
11842
|
const allNotProven = [...notProvenList, ...pushNotProven];
|
|
@@ -11770,7 +11881,7 @@ var init_deep = __esm(() => {
|
|
|
11770
11881
|
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
11771
11882
|
import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
|
|
11772
11883
|
import { tmpdir as tmpdir8 } from "os";
|
|
11773
|
-
import { dirname as dirname17, isAbsolute as isAbsolute6, join as
|
|
11884
|
+
import { dirname as dirname17, isAbsolute as isAbsolute6, join as join50, relative as relative5 } from "path";
|
|
11774
11885
|
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
11775
11886
|
const inRepo = (p) => {
|
|
11776
11887
|
try {
|
|
@@ -11781,7 +11892,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
|
11781
11892
|
}
|
|
11782
11893
|
};
|
|
11783
11894
|
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
11784
|
-
const project = [
|
|
11895
|
+
const project = [join50(repoDir, ".venv", "bin", bin), join50(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join50(venvEnv, "bin", bin)] : []].find(existsSync29);
|
|
11785
11896
|
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
11786
11897
|
}
|
|
11787
11898
|
function runnerCmd(t, repoDir) {
|
|
@@ -11811,13 +11922,6 @@ function commandFor(t, repoDir, api) {
|
|
|
11811
11922
|
const [cmd, args, interpreter] = runnerCmd(site ? { runner: t.runner, path: site.file } : t, repoDir);
|
|
11812
11923
|
return { cwd: site?.cwd ?? repoDir, cmd, args, argv: [cmd, ...args], ...interpreter ? { interpreter } : {}, pkgRoot: site?.root ?? "." };
|
|
11813
11924
|
}
|
|
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
11925
|
async function runOnce(cmd, args, cwd, signal, opts) {
|
|
11822
11926
|
if (!Bun.which(cmd, { PATH: opts.path ?? process.env["PATH"] ?? "" }))
|
|
11823
11927
|
return { ok: false, missing: true, cut: false, out: "" };
|
|
@@ -11856,8 +11960,8 @@ function firstError(out2) {
|
|
|
11856
11960
|
return l2;
|
|
11857
11961
|
}
|
|
11858
11962
|
};
|
|
11859
|
-
const
|
|
11860
|
-
`).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l =
|
|
11963
|
+
const lines2 = out2.slice(-65536).split(`
|
|
11964
|
+
`).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
11965
|
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
11966
|
}
|
|
11863
11967
|
async function runCheck(ctx, name, cmd, args, signal, checks, opts = {}) {
|
|
@@ -11899,7 +12003,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11899
12003
|
const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
|
|
11900
12004
|
if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
|
|
11901
12005
|
return out2;
|
|
11902
|
-
const dir = mkdtempSync6(
|
|
12006
|
+
const dir = mkdtempSync6(join50(tmpdir8(), "e10-base-"));
|
|
11903
12007
|
const git3 = (args) => {
|
|
11904
12008
|
safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
|
|
11905
12009
|
};
|
|
@@ -11907,12 +12011,15 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11907
12011
|
git3(["-c", "core.hooksPath=/dev/null", "worktree", "add", "--detach", dir, ctx.baseSha]);
|
|
11908
12012
|
const base = new Map;
|
|
11909
12013
|
for (const { c, t } of new Set([...un, ...sus])) {
|
|
11910
|
-
const bc = commandFor(t, dir, api)
|
|
11911
|
-
|
|
12014
|
+
const bc = commandFor(t, dir, api);
|
|
12015
|
+
const b = await runOnce(bc.cmd, bc.args, bc.cwd, signal, {});
|
|
12016
|
+
const red = !b.ok && !b.cut ? failIds(b.out) : [];
|
|
12017
|
+
base.set(c, { red, n: ran(b.out) ?? summaryPassFailCount(b.out), sk: skipped(b.out) });
|
|
11912
12018
|
}
|
|
11913
12019
|
out2.weak = sus.filter(({ c }) => (c.sk ?? 0) > base.get(c).sk || (c.n ?? 0) < base.get(c).n).map(({ c }) => c.name);
|
|
11914
|
-
for (const { c } of sus)
|
|
12020
|
+
for (const { c } of sus) {
|
|
11915
12021
|
out2.cnt[c.name] = { b: { run: base.get(c).n, skipped: base.get(c).sk }, h: { run: c.n ?? 0, skipped: c.sk ?? 0 } };
|
|
12022
|
+
}
|
|
11916
12023
|
const progress = checks.some((c) => wall.has(c.name) && c.result === "pass") || sus.some(({ c }) => !out2.weak.includes(c.name) && base.get(c).red.length > 0 && (c.n ?? 0) >= base.get(c).n);
|
|
11917
12024
|
if (progress) {
|
|
11918
12025
|
for (const { c } of un) {
|
|
@@ -11934,7 +12041,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11934
12041
|
return out2;
|
|
11935
12042
|
}
|
|
11936
12043
|
function isLokiModeRepo(repoDir) {
|
|
11937
|
-
return existsSync29(
|
|
12044
|
+
return existsSync29(join50(repoDir, "scripts", "select-tests.sh"));
|
|
11938
12045
|
}
|
|
11939
12046
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
11940
12047
|
const { api, ...runOpts } = opts;
|
|
@@ -11951,15 +12058,16 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
11951
12058
|
}
|
|
11952
12059
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
11953
12060
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
11954
|
-
if (existsSync29(
|
|
12061
|
+
if (existsSync29(join50(g.cwd, "tsconfig.json")))
|
|
11955
12062
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
11956
|
-
if (ESLINT_CONFIGS.some((f) => existsSync29(
|
|
12063
|
+
if (ESLINT_CONFIGS.some((f) => existsSync29(join50(g.cwd, f))))
|
|
11957
12064
|
await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
11958
12065
|
}
|
|
11959
12066
|
}
|
|
11960
|
-
var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT,
|
|
12067
|
+
var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT, ESLINT_CONFIGS, verifyStage;
|
|
11961
12068
|
var init_verify = __esm(() => {
|
|
11962
12069
|
init_scope();
|
|
12070
|
+
init_runner_config();
|
|
11963
12071
|
init_assert_delta();
|
|
11964
12072
|
init_failures();
|
|
11965
12073
|
init_load_owner();
|
|
@@ -11973,10 +12081,8 @@ var init_verify = __esm(() => {
|
|
|
11973
12081
|
init_repo_profile();
|
|
11974
12082
|
init_check_result();
|
|
11975
12083
|
init_check_result();
|
|
12084
|
+
init_runner_config();
|
|
11976
12085
|
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
12086
|
ESLINT_CONFIGS = [".eslintrc", ".eslintrc.json", ".eslintrc.js", ".eslintrc.cjs", "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"];
|
|
11981
12087
|
verifyStage = {
|
|
11982
12088
|
name: "verify",
|
|
@@ -12013,8 +12119,8 @@ var init_verify = __esm(() => {
|
|
|
12013
12119
|
const wall = ctx.outputs().wall ?? {};
|
|
12014
12120
|
const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute6(f.path) ? relative5(ctx.repoDir, f.path) : f.path));
|
|
12015
12121
|
const wallTests = map.tests.filter((t) => wallPaths.has(t.path));
|
|
12016
|
-
const intake = ctx.outputs().intake,
|
|
12017
|
-
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...
|
|
12122
|
+
const intake = ctx.outputs().intake, relevant2 = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
|
|
12123
|
+
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant2]);
|
|
12018
12124
|
const scopedOut = scopedOutOf(map);
|
|
12019
12125
|
if (scopedOut.length)
|
|
12020
12126
|
ctx.emit("test.scoped_out", "verify", { tests: scopedOut });
|
|
@@ -12022,10 +12128,10 @@ var init_verify = __esm(() => {
|
|
|
12022
12128
|
if (signal.aborted)
|
|
12023
12129
|
break;
|
|
12024
12130
|
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, ...
|
|
12131
|
+
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
12132
|
}
|
|
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(
|
|
12133
|
+
const targetNames = new Set([...wallTests, ...relevant2].map((t) => `${t.runner}:${t.path}`));
|
|
12134
|
+
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
12135
|
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
12136
|
preRed.names.push(...attr.preExistingChecks);
|
|
12031
12137
|
preRed.ids.push(...attr.preExistingIds);
|
|
@@ -12071,7 +12177,7 @@ var init_verify = __esm(() => {
|
|
|
12071
12177
|
}
|
|
12072
12178
|
};
|
|
12073
12179
|
const testCounts = {};
|
|
12074
|
-
const modifiedRel =
|
|
12180
|
+
const modifiedRel = relevant2.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
|
|
12075
12181
|
const k = preRed.cnt[`${t.runner}:${t.path}`];
|
|
12076
12182
|
if (k)
|
|
12077
12183
|
testCounts[t.path] = k;
|
|
@@ -12085,8 +12191,8 @@ var init_verify = __esm(() => {
|
|
|
12085
12191
|
});
|
|
12086
12192
|
|
|
12087
12193
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
12088
|
-
import { existsSync as existsSync30, readdirSync as
|
|
12089
|
-
import { dirname as dirname18, join as
|
|
12194
|
+
import { existsSync as existsSync30, readdirSync as readdirSync14, readFileSync as readFileSync36 } from "fs";
|
|
12195
|
+
import { dirname as dirname18, join as join51 } from "path";
|
|
12090
12196
|
function parseJsonc(text2) {
|
|
12091
12197
|
let out2 = "", i = 0;
|
|
12092
12198
|
while (i < text2.length) {
|
|
@@ -12121,25 +12227,25 @@ function moduleOf(file, depth = 0) {
|
|
|
12121
12227
|
const ext = typeof j.extends === "string" ? j.extends : null;
|
|
12122
12228
|
if (!ext || !ext.startsWith("."))
|
|
12123
12229
|
return null;
|
|
12124
|
-
const next =
|
|
12230
|
+
const next = join51(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
12125
12231
|
return existsSync30(next) ? moduleOf(next, depth + 1) : null;
|
|
12126
12232
|
}
|
|
12127
12233
|
function readPackageConventions(pkgDir, runner = null) {
|
|
12128
12234
|
let names = [];
|
|
12129
12235
|
try {
|
|
12130
|
-
names =
|
|
12236
|
+
names = readdirSync14(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
|
|
12131
12237
|
} catch {
|
|
12132
12238
|
return null;
|
|
12133
12239
|
}
|
|
12134
12240
|
let packageType = null, tsModule = null;
|
|
12135
12241
|
for (const n2 of names) {
|
|
12136
|
-
const j = readJson4(
|
|
12242
|
+
const j = readJson4(join51(pkgDir, n2));
|
|
12137
12243
|
if (!j)
|
|
12138
12244
|
continue;
|
|
12139
12245
|
if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
|
|
12140
12246
|
packageType = j.type;
|
|
12141
12247
|
if (tsModule === null && j.compilerOptions !== undefined)
|
|
12142
|
-
tsModule = moduleOf(
|
|
12248
|
+
tsModule = moduleOf(join51(pkgDir, n2));
|
|
12143
12249
|
}
|
|
12144
12250
|
if (packageType === null && tsModule === null)
|
|
12145
12251
|
return null;
|
|
@@ -12161,7 +12267,7 @@ function conventionViolation(c, fileName, content) {
|
|
|
12161
12267
|
}
|
|
12162
12268
|
var readJson4 = (p) => {
|
|
12163
12269
|
try {
|
|
12164
|
-
const v = parseJsonc(
|
|
12270
|
+
const v = parseJsonc(readFileSync36(p, "utf8"));
|
|
12165
12271
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12166
12272
|
} catch {
|
|
12167
12273
|
return null;
|
|
@@ -12171,15 +12277,15 @@ var init_conventions = () => {};
|
|
|
12171
12277
|
|
|
12172
12278
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
12173
12279
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12174
|
-
import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as
|
|
12280
|
+
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
12281
|
import { tmpdir as tmpdir9 } from "os";
|
|
12176
|
-
import { dirname as dirname19, isAbsolute as isAbsolute7, join as
|
|
12282
|
+
import { dirname as dirname19, isAbsolute as isAbsolute7, join as join52, relative as relative6 } from "path";
|
|
12177
12283
|
function pytestCollectionIsRed(output, repoDir) {
|
|
12178
12284
|
if (/ModuleNotFoundError/.test(output))
|
|
12179
12285
|
return false;
|
|
12180
12286
|
const real2 = (p) => {
|
|
12181
12287
|
try {
|
|
12182
|
-
return realpathSync18(isAbsolute7(p) ? p :
|
|
12288
|
+
return realpathSync18(isAbsolute7(p) ? p : join52(repoDir, p));
|
|
12183
12289
|
} catch {
|
|
12184
12290
|
return null;
|
|
12185
12291
|
}
|
|
@@ -12216,7 +12322,7 @@ function pytestCollectionIsRed(output, repoDir) {
|
|
|
12216
12322
|
}
|
|
12217
12323
|
function moduleUnderRepo(repoDir, name) {
|
|
12218
12324
|
const rel = name.replace(/\./g, "/");
|
|
12219
|
-
return existsSync31(
|
|
12325
|
+
return existsSync31(join52(repoDir, `${rel}.py`)) || existsSync31(join52(repoDir, rel, "__init__.py"));
|
|
12220
12326
|
}
|
|
12221
12327
|
function pytestExit1IsRed(output) {
|
|
12222
12328
|
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
@@ -12314,10 +12420,10 @@ ${acceptance.map((a) => `Acceptance: ${a}`).join(`
|
|
|
12314
12420
|
function loadTaskText(ctx, fromPrior) {
|
|
12315
12421
|
if (fromPrior)
|
|
12316
12422
|
return fromPrior;
|
|
12317
|
-
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ??
|
|
12423
|
+
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join52(ctx.runDir, "issue.json");
|
|
12318
12424
|
if (existsSync31(issueJsonPath)) {
|
|
12319
12425
|
try {
|
|
12320
|
-
const issue = JSON.parse(
|
|
12426
|
+
const issue = JSON.parse(readFileSync37(issueJsonPath, "utf8"));
|
|
12321
12427
|
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
12322
12428
|
|
|
12323
12429
|
`);
|
|
@@ -12329,7 +12435,7 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
12329
12435
|
}
|
|
12330
12436
|
function wallTargetDir(repoDir, existingTests) {
|
|
12331
12437
|
const first = existingTests[0];
|
|
12332
|
-
return first ?
|
|
12438
|
+
return first ? join52(repoDir, dirname19(first.path)) : join52(repoDir, "tests");
|
|
12333
12439
|
}
|
|
12334
12440
|
function guessRunner(fileName, runners) {
|
|
12335
12441
|
if (fileName.endsWith(".py"))
|
|
@@ -12360,11 +12466,11 @@ async function wallAuthor(ctx, signal) {
|
|
|
12360
12466
|
const tree = prior.intake?.tree;
|
|
12361
12467
|
const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
|
|
12362
12468
|
const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
|
|
12363
|
-
const conv2 = readPackageConventions(
|
|
12364
|
-
const cwd = mkdtempSync7(
|
|
12469
|
+
const conv2 = readPackageConventions(join52(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
|
|
12470
|
+
const cwd = mkdtempSync7(join52(tmpdir9(), "loki-e15-wall-"));
|
|
12365
12471
|
const wm = wallManifestFor(ctx.repoDir, tree, task);
|
|
12366
12472
|
for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
|
|
12367
|
-
writeFileSync21(
|
|
12473
|
+
writeFileSync21(join52(cwd, n2), c, "utf8");
|
|
12368
12474
|
const session = await ctx.sessions.run({
|
|
12369
12475
|
stage: "wall",
|
|
12370
12476
|
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
|
|
@@ -12379,15 +12485,15 @@ async function wallAuthor(ctx, signal) {
|
|
|
12379
12485
|
rmSync13(cwd, { recursive: true, force: true });
|
|
12380
12486
|
return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
|
|
12381
12487
|
}
|
|
12382
|
-
const generated =
|
|
12383
|
-
const sealedDir =
|
|
12488
|
+
const generated = readdirSync15(cwd).filter((f) => f.startsWith(WALL_PREFIX));
|
|
12489
|
+
const sealedDir = join52(ctx.runDir, "wall");
|
|
12384
12490
|
if (generated.length > 0)
|
|
12385
12491
|
mkdirSync19(sealedDir, { recursive: true });
|
|
12386
12492
|
const contents = new Map;
|
|
12387
12493
|
const discarded = [];
|
|
12388
12494
|
for (const name of generated) {
|
|
12389
|
-
const content =
|
|
12390
|
-
writeFileSync21(
|
|
12495
|
+
const content = readFileSync37(join52(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
|
|
12496
|
+
writeFileSync21(join52(sealedDir, name), content, "utf8");
|
|
12391
12497
|
if (bad)
|
|
12392
12498
|
discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
|
|
12393
12499
|
else
|
|
@@ -12407,7 +12513,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12407
12513
|
const content = contents.get(name);
|
|
12408
12514
|
if (content === undefined)
|
|
12409
12515
|
continue;
|
|
12410
|
-
const dest =
|
|
12516
|
+
const dest = join52(targetDir, name), runner = guessRunner(name, runners);
|
|
12411
12517
|
writeFileSync21(dest, content, "utf8");
|
|
12412
12518
|
sealedFiles.push({ path: dest, sha256: sha2562(content) });
|
|
12413
12519
|
readOnlyFiles.push({ path: dest, content });
|
|
@@ -12417,7 +12523,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
12417
12523
|
ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
|
|
12418
12524
|
const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
|
|
12419
12525
|
for (const t of wallTests) {
|
|
12420
|
-
const r = baseRunner.run(baseDir, [t]), abs =
|
|
12526
|
+
const r = baseRunner.run(baseDir, [t]), abs = join52(ctx.repoDir, t.path);
|
|
12421
12527
|
baseRun.pass += r.pass;
|
|
12422
12528
|
baseRun.fail += r.fail;
|
|
12423
12529
|
baseRun.not_run += r.not_run ?? 0;
|
|
@@ -12469,15 +12575,15 @@ var init_wall = __esm(() => {
|
|
|
12469
12575
|
});
|
|
12470
12576
|
|
|
12471
12577
|
// ../../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
|
|
12578
|
+
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";
|
|
12579
|
+
import { dirname as dirname20, join as join53, relative as relative7, sep as sep11 } from "path";
|
|
12474
12580
|
function safeRestore(repoDir, f, want, mode) {
|
|
12475
|
-
const root = realpathSync19(repoDir), fp =
|
|
12581
|
+
const root = realpathSync19(repoDir), fp = join53(root, f), rel = relative7(root, dirname20(fp));
|
|
12476
12582
|
if (rel.startsWith(".."))
|
|
12477
12583
|
throw new Error("outside repo");
|
|
12478
12584
|
let cur = root;
|
|
12479
12585
|
for (const part of rel === "" ? [] : rel.split(sep11)) {
|
|
12480
|
-
cur =
|
|
12586
|
+
cur = join53(cur, part);
|
|
12481
12587
|
let st2;
|
|
12482
12588
|
try {
|
|
12483
12589
|
st2 = lstatSync10(cur);
|
|
@@ -12494,7 +12600,7 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
12494
12600
|
} catch {
|
|
12495
12601
|
st = null;
|
|
12496
12602
|
}
|
|
12497
|
-
if (st?.isFile() &&
|
|
12603
|
+
if (st?.isFile() && readFileSync38(fp).equals(want)) {
|
|
12498
12604
|
chmodSync5(fp, mode);
|
|
12499
12605
|
return;
|
|
12500
12606
|
}
|
|
@@ -12532,7 +12638,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12532
12638
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
12533
12639
|
const notProven = [];
|
|
12534
12640
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
12535
|
-
const fp =
|
|
12641
|
+
const fp = join53(repoDir, f);
|
|
12536
12642
|
try {
|
|
12537
12643
|
if (v.startsWith("!")) {
|
|
12538
12644
|
const st = lstatSync10(fp);
|
|
@@ -12551,7 +12657,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
12551
12657
|
try {
|
|
12552
12658
|
let m = 420;
|
|
12553
12659
|
try {
|
|
12554
|
-
const l = lstatSync10(
|
|
12660
|
+
const l = lstatSync10(join53(repoDir, f));
|
|
12555
12661
|
if (l.isFile())
|
|
12556
12662
|
m = l.mode & 511;
|
|
12557
12663
|
} catch {}
|
|
@@ -12627,9 +12733,9 @@ var init_scope2 = __esm(() => {
|
|
|
12627
12733
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
12628
12734
|
import { spawn as spawn7 } from "child_process";
|
|
12629
12735
|
import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
|
|
12630
|
-
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as
|
|
12736
|
+
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync16, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
|
|
12631
12737
|
import { createServer } from "net";
|
|
12632
|
-
import { dirname as dirname23, isAbsolute as isAbsolute8, join as
|
|
12738
|
+
import { dirname as dirname23, isAbsolute as isAbsolute8, join as join54, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
|
|
12633
12739
|
function routeFor(file) {
|
|
12634
12740
|
let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
|
|
12635
12741
|
r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
|
|
@@ -12639,7 +12745,7 @@ function hashScreens(runDir, rels) {
|
|
|
12639
12745
|
const out2 = [];
|
|
12640
12746
|
for (const rel of rels) {
|
|
12641
12747
|
try {
|
|
12642
|
-
out2.push({ path: rel, sha256: sha256File(
|
|
12748
|
+
out2.push({ path: rel, sha256: sha256File(join54(runDir, rel)) });
|
|
12643
12749
|
} catch {}
|
|
12644
12750
|
}
|
|
12645
12751
|
return out2;
|
|
@@ -12652,10 +12758,10 @@ function checkScreens(runDir, screens) {
|
|
|
12652
12758
|
return "evidence_screens entry is malformed";
|
|
12653
12759
|
if (isAbsolute8(s.path) || normalize2(s.path).startsWith(".."))
|
|
12654
12760
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
12655
|
-
const file =
|
|
12761
|
+
const file = join54(runDir, s.path);
|
|
12656
12762
|
let cur = runDir;
|
|
12657
12763
|
for (const seg of normalize2(s.path).split(sep12)) {
|
|
12658
|
-
cur =
|
|
12764
|
+
cur = join54(cur, seg);
|
|
12659
12765
|
if (isSymlink(cur))
|
|
12660
12766
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
12661
12767
|
}
|
|
@@ -12702,11 +12808,11 @@ async function waitUp(url, ms, signal) {
|
|
|
12702
12808
|
}
|
|
12703
12809
|
function openapiPaths(repoDir) {
|
|
12704
12810
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
12705
|
-
const p =
|
|
12811
|
+
const p = join54(repoDir, f);
|
|
12706
12812
|
if (!existsSync32(p))
|
|
12707
12813
|
continue;
|
|
12708
12814
|
try {
|
|
12709
|
-
const txt =
|
|
12815
|
+
const txt = readFileSync39(p, "utf8");
|
|
12710
12816
|
const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
|
|
12711
12817
|
return paths.filter((x) => !x.includes("{")).slice(0, 10);
|
|
12712
12818
|
} catch {
|
|
@@ -12720,24 +12826,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
12720
12826
|
if (!pw || !pkg)
|
|
12721
12827
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
12722
12828
|
try {
|
|
12723
|
-
const dir =
|
|
12829
|
+
const dir = join54(runDir, relDir, "e2e");
|
|
12724
12830
|
mkdirSync21(dir, { recursive: true });
|
|
12725
|
-
writeFileSync23(
|
|
12726
|
-
writeFileSync23(
|
|
12831
|
+
writeFileSync23(join54(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
12832
|
+
writeFileSync23(join54(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
12727
12833
|
`);
|
|
12728
|
-
if (!await shoot(pw, ["test", "--config",
|
|
12834
|
+
if (!await shoot(pw, ["test", "--config", join54(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
12729
12835
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
12730
12836
|
const rels = [];
|
|
12731
12837
|
const walk = (d) => {
|
|
12732
|
-
for (const e of
|
|
12733
|
-
const full =
|
|
12838
|
+
for (const e of readdirSync16(d, { withFileTypes: true })) {
|
|
12839
|
+
const full = join54(d, e.name);
|
|
12734
12840
|
if (e.isDirectory())
|
|
12735
12841
|
walk(full);
|
|
12736
12842
|
else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
|
|
12737
|
-
rels.push(
|
|
12843
|
+
rels.push(join54(relDir, "e2e", relative8(dir, full)));
|
|
12738
12844
|
}
|
|
12739
12845
|
};
|
|
12740
|
-
walk(
|
|
12846
|
+
walk(join54(dir, "out"));
|
|
12741
12847
|
return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
|
|
12742
12848
|
} catch (e) {
|
|
12743
12849
|
return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
|
|
@@ -12797,6 +12903,16 @@ async function stopServer(c, graceMs, signal) {
|
|
|
12797
12903
|
for (let i = 0;i < 10 && groupAlive(pid); i++)
|
|
12798
12904
|
await new Promise((r) => setTimeout(r, 50));
|
|
12799
12905
|
}
|
|
12906
|
+
function noRootReason(subs, lacking) {
|
|
12907
|
+
const list = (xs) => xs.map((d) => `${d}/`).join(" or ");
|
|
12908
|
+
if (subs.length === 0)
|
|
12909
|
+
return "no package.json";
|
|
12910
|
+
if (lacking.length === subs.length)
|
|
12911
|
+
return `no dev, preview or start script in ${list(lacking)} (no root package.json)`;
|
|
12912
|
+
if (lacking.length === 0)
|
|
12913
|
+
return `no root package.json (${list(subs)} have a dev, preview or start script but no UI boot was resolved)`;
|
|
12914
|
+
return `no root package.json; no dev, preview or start script in ${list(lacking)} (${list(subs.filter((d) => !lacking.includes(d)))} has one)`;
|
|
12915
|
+
}
|
|
12800
12916
|
async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
12801
12917
|
const skip = (why) => ({ screens: [], http: false, skipped: why });
|
|
12802
12918
|
const signal = opts.signal;
|
|
@@ -12806,13 +12922,22 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12806
12922
|
if (!visualEvidenceEnabled(opts.env))
|
|
12807
12923
|
return skip("LOKI_VISUAL_EVIDENCE is 0");
|
|
12808
12924
|
const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
|
|
12809
|
-
const appDir = boot ?
|
|
12810
|
-
const pkgPath =
|
|
12925
|
+
const appDir = boot ? join54(repoDir, boot.boot.cwd) : repoDir;
|
|
12926
|
+
const pkgPath = join54(appDir, "package.json");
|
|
12811
12927
|
let script = null;
|
|
12812
12928
|
if (!boot) {
|
|
12813
|
-
if (!existsSync32(pkgPath))
|
|
12814
|
-
|
|
12815
|
-
|
|
12929
|
+
if (!existsSync32(pkgPath)) {
|
|
12930
|
+
const subs = ["backend", "frontend"].filter((d) => existsSync32(join54(repoDir, d, "package.json")));
|
|
12931
|
+
const has = (d) => {
|
|
12932
|
+
try {
|
|
12933
|
+
return pickScript(JSON.parse(readFileSync39(join54(repoDir, d, "package.json"), "utf8"))) !== null;
|
|
12934
|
+
} catch {
|
|
12935
|
+
return false;
|
|
12936
|
+
}
|
|
12937
|
+
};
|
|
12938
|
+
return skip(noRootReason(subs, subs.filter((d) => !has(d))));
|
|
12939
|
+
}
|
|
12940
|
+
script = pickScript(JSON.parse(readFileSync39(pkgPath, "utf8")));
|
|
12816
12941
|
if (!script)
|
|
12817
12942
|
return skip("no dev, preview or start script");
|
|
12818
12943
|
}
|
|
@@ -12839,20 +12964,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12839
12964
|
}
|
|
12840
12965
|
if (!await waitUp(base, left(), signal))
|
|
12841
12966
|
return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
|
|
12842
|
-
const evRoot =
|
|
12967
|
+
const evRoot = join54(runDir, "evidence");
|
|
12843
12968
|
if (isSymlink(evRoot))
|
|
12844
12969
|
return skip("evidence dir is a symlink");
|
|
12845
12970
|
mkdirSync21(evRoot, { recursive: true });
|
|
12846
12971
|
if (pages.length > 0) {
|
|
12847
|
-
const rel0 =
|
|
12848
|
-
mkdirSync21(
|
|
12972
|
+
const rel0 = join54("evidence", randomBytes4(6).toString("hex"));
|
|
12973
|
+
mkdirSync21(join54(runDir, rel0));
|
|
12849
12974
|
const rels = [];
|
|
12850
12975
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
12851
12976
|
if (aborted() || left() <= 0)
|
|
12852
12977
|
break;
|
|
12853
|
-
const rel =
|
|
12854
|
-
const ok = await shoot(pw, ["screenshot", `${base}${route}`,
|
|
12855
|
-
if (ok && !aborted() && !isSymlink(
|
|
12978
|
+
const rel = join54(rel0, `${screenName(route)}.png`);
|
|
12979
|
+
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join54(runDir, rel)], appDir, left(), signal);
|
|
12980
|
+
if (ok && !aborted() && !isSymlink(join54(runDir, rel)) && existsSync32(join54(runDir, rel)))
|
|
12856
12981
|
rels.push(rel);
|
|
12857
12982
|
}
|
|
12858
12983
|
if (aborted())
|
|
@@ -12877,9 +13002,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12877
13002
|
}
|
|
12878
13003
|
if (aborted())
|
|
12879
13004
|
return skip("aborted");
|
|
12880
|
-
const hdir =
|
|
13005
|
+
const hdir = join54(evRoot, randomBytes4(6).toString("hex"));
|
|
12881
13006
|
mkdirSync21(hdir);
|
|
12882
|
-
writeFileSync23(
|
|
13007
|
+
writeFileSync23(join54(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
12883
13008
|
`);
|
|
12884
13009
|
return { screens: [], http: true, skipped: null };
|
|
12885
13010
|
} catch (e) {
|
|
@@ -12904,12 +13029,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
12904
13029
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
12905
13030
|
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
|
|
12906
13031
|
}
|
|
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 =
|
|
13032
|
+
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) => {
|
|
13033
|
+
const bin = join54(repoDir, "node_modules", ".bin", "playwright");
|
|
12909
13034
|
return existsSync32(bin) ? bin : null;
|
|
12910
13035
|
}, playwrightTestPkg = (repoDir) => {
|
|
12911
|
-
const d =
|
|
12912
|
-
return existsSync32(
|
|
13036
|
+
const d = join54(repoDir, "node_modules", "@playwright", "test");
|
|
13037
|
+
return existsSync32(join54(d, "package.json")) ? d : null;
|
|
12913
13038
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
12914
13039
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
12915
13040
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
@@ -12940,14 +13065,14 @@ var init_visual_evidence = __esm(() => {
|
|
|
12940
13065
|
});
|
|
12941
13066
|
|
|
12942
13067
|
// ../../loki-ts/src/council/role_agents.ts
|
|
12943
|
-
import { existsSync as existsSync33, readdirSync as
|
|
13068
|
+
import { existsSync as existsSync33, readdirSync as readdirSync17, readFileSync as readFileSync40 } from "fs";
|
|
12944
13069
|
import { homedir as homedir9 } from "os";
|
|
12945
|
-
import { join as
|
|
13070
|
+
import { join as join55 } from "path";
|
|
12946
13071
|
function agentDirs(repoDir, env = process.env) {
|
|
12947
|
-
return [env["LOKI_AGENTS_PROJECT_DIR"] ||
|
|
13072
|
+
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
13073
|
}
|
|
12949
13074
|
function parseAgent(file, stem2) {
|
|
12950
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(
|
|
13075
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync40(file, "utf8"));
|
|
12951
13076
|
if (!m)
|
|
12952
13077
|
return null;
|
|
12953
13078
|
const body = m[2].trim();
|
|
@@ -12965,14 +13090,14 @@ function loadRoleAgent(repoDir, role, env = process.env) {
|
|
|
12965
13090
|
continue;
|
|
12966
13091
|
let names;
|
|
12967
13092
|
try {
|
|
12968
|
-
names =
|
|
13093
|
+
names = readdirSync17(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
|
|
12969
13094
|
} catch {
|
|
12970
13095
|
continue;
|
|
12971
13096
|
}
|
|
12972
13097
|
const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
|
|
12973
13098
|
for (const n2 of ordered) {
|
|
12974
13099
|
try {
|
|
12975
|
-
const a = parseAgent(
|
|
13100
|
+
const a = parseAgent(join55(d, n2), n2.slice(0, -3));
|
|
12976
13101
|
if (a && a.role === role)
|
|
12977
13102
|
return a;
|
|
12978
13103
|
} catch {}
|
|
@@ -12989,8 +13114,8 @@ var init_role_agents = __esm(() => {
|
|
|
12989
13114
|
});
|
|
12990
13115
|
|
|
12991
13116
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
12992
|
-
import { existsSync as existsSync34, readFileSync as
|
|
12993
|
-
import { join as
|
|
13117
|
+
import { existsSync as existsSync34, readFileSync as readFileSync41 } from "fs";
|
|
13118
|
+
import { join as join56 } from "path";
|
|
12994
13119
|
function minVerdict(verdict, review) {
|
|
12995
13120
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
12996
13121
|
return RANK3[ceiling] < RANK3[verdict] ? ceiling : verdict;
|
|
@@ -13003,11 +13128,11 @@ function explicitReview(repoDir, env) {
|
|
|
13003
13128
|
if (flag && isOff(ev))
|
|
13004
13129
|
return "off";
|
|
13005
13130
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
13006
|
-
const p =
|
|
13131
|
+
const p = join56(repoDir, f);
|
|
13007
13132
|
if (!existsSync34(p))
|
|
13008
13133
|
continue;
|
|
13009
13134
|
try {
|
|
13010
|
-
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(
|
|
13135
|
+
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync41(p, "utf8"));
|
|
13011
13136
|
const v = asProvider(m?.[1]);
|
|
13012
13137
|
if (v)
|
|
13013
13138
|
return v;
|
|
@@ -13217,16 +13342,41 @@ function recordRun(env, repoDir, model, verdict, usd, unmetered, wallS, cacheRoo
|
|
|
13217
13342
|
return false;
|
|
13218
13343
|
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
13344
|
}
|
|
13220
|
-
var NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0";
|
|
13345
|
+
var MIN_RUNS = 3, NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0", ESTIMATE_HELP;
|
|
13221
13346
|
var init_cost_preview = __esm(() => {
|
|
13222
13347
|
init_cache();
|
|
13348
|
+
init_session();
|
|
13223
13349
|
init_history();
|
|
13224
13350
|
init_engine_origin();
|
|
13351
|
+
ESTIMATE_HELP = `loki estimate - Show what a run is likely to cost
|
|
13352
|
+
|
|
13353
|
+
Usage: loki estimate [--model M] [--provider P] [--json]
|
|
13354
|
+
|
|
13355
|
+
Prints the same estimate line "loki start" prints before a run: the dollar and time
|
|
13356
|
+
range of this repo's prior verified runs of the same project shape. With fewer than
|
|
13357
|
+
${MIN_RUNS} such runs it prints a rough per-model prior, or NOT AVAILABLE (reason).
|
|
13358
|
+
It never estimates from task text and never prints a number it did not measure.
|
|
13359
|
+
|
|
13360
|
+
Options:
|
|
13361
|
+
--model M Model whose rough prior to use when there is no history (default: the start model)
|
|
13362
|
+
--provider P Provider whose default model is used (default: LOKI_PROVIDER or claude)
|
|
13363
|
+
--json Machine-readable output (the estimate result object)
|
|
13364
|
+
--help, -h Show this help
|
|
13365
|
+
|
|
13366
|
+
Changed: --iterations is removed. The old iteration-based projection (tools/estimate-run.py)
|
|
13367
|
+
is replaced by this single estimator. LOKI_COST_PREVIEW=0 turns the estimate off.
|
|
13368
|
+
|
|
13369
|
+
Examples:
|
|
13370
|
+
loki estimate
|
|
13371
|
+
loki estimate --json
|
|
13372
|
+
|
|
13373
|
+
See also: loki cost --detail (what a run ALREADY spent)
|
|
13374
|
+
`;
|
|
13225
13375
|
});
|
|
13226
13376
|
|
|
13227
13377
|
// ../../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
|
|
13378
|
+
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync42, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
13379
|
+
import { dirname as dirname24, join as join57 } from "path";
|
|
13230
13380
|
import { createHash as createHash13 } from "crypto";
|
|
13231
13381
|
function contractEnabled(env = process.env) {
|
|
13232
13382
|
return env["LOKI_CONTRACT"] !== "0";
|
|
@@ -13258,7 +13408,7 @@ function untracedLines(trace) {
|
|
|
13258
13408
|
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
13409
|
}
|
|
13260
13410
|
function contractPath(repoDir) {
|
|
13261
|
-
return
|
|
13411
|
+
return join57(repoDir, ".loki", "contract.json");
|
|
13262
13412
|
}
|
|
13263
13413
|
function readContract(repoDir) {
|
|
13264
13414
|
const p = contractPath(repoDir);
|
|
@@ -13274,7 +13424,7 @@ function readContract(repoDir) {
|
|
|
13274
13424
|
return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
|
|
13275
13425
|
let raw2;
|
|
13276
13426
|
try {
|
|
13277
|
-
raw2 =
|
|
13427
|
+
raw2 = readFileSync42(p);
|
|
13278
13428
|
} catch {
|
|
13279
13429
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
13280
13430
|
}
|
|
@@ -13343,16 +13493,16 @@ var init_contract = __esm(() => {
|
|
|
13343
13493
|
init_safe_git();
|
|
13344
13494
|
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
13495
|
MAX_CONTRACT_BYTES = 1024 * 1024;
|
|
13346
|
-
REQ_MAP_REL =
|
|
13496
|
+
REQ_MAP_REL = join57(".loki", "state", "requirements-map.json");
|
|
13347
13497
|
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
13498
|
});
|
|
13349
13499
|
|
|
13350
13500
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
13351
13501
|
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
|
|
13502
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync18, readFileSync as readFileSync43, renameSync as renameSync9, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
|
|
13503
|
+
import { basename as basename19, dirname as dirname25, join as join58 } from "path";
|
|
13354
13504
|
function sealGroup(runDir, rh) {
|
|
13355
|
-
const manifestPath =
|
|
13505
|
+
const manifestPath = join58(runDir, "group", "manifest.json");
|
|
13356
13506
|
if (!existsSync36(manifestPath))
|
|
13357
13507
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
13358
13508
|
const notProven = [];
|
|
@@ -13377,10 +13527,10 @@ function sealGroup(runDir, rh) {
|
|
|
13377
13527
|
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
13378
13528
|
seen.add(id);
|
|
13379
13529
|
seenLower.add(id.toLowerCase());
|
|
13380
|
-
const dir =
|
|
13530
|
+
const dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
|
|
13381
13531
|
let bytes;
|
|
13382
13532
|
try {
|
|
13383
|
-
bytes =
|
|
13533
|
+
bytes = readFileSync43(rp);
|
|
13384
13534
|
} catch {
|
|
13385
13535
|
return fail(`unit ${id} sub-receipt missing`);
|
|
13386
13536
|
}
|
|
@@ -13397,7 +13547,7 @@ function sealGroup(runDir, rh) {
|
|
|
13397
13547
|
unit_id: id,
|
|
13398
13548
|
sub_receipt_sha256: sha2(bytes),
|
|
13399
13549
|
receipt_sha256: sub["receipt_sha256"],
|
|
13400
|
-
events_file_sha256: existsSync36(ep) ? sha2(
|
|
13550
|
+
events_file_sha256: existsSync36(ep) ? sha2(readFileSync43(ep)) : null,
|
|
13401
13551
|
verdict,
|
|
13402
13552
|
goal: text2(u["goal"], 200),
|
|
13403
13553
|
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
@@ -13422,7 +13572,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13422
13572
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
13423
13573
|
const g = receipt["group"];
|
|
13424
13574
|
if (g === undefined)
|
|
13425
|
-
return existsSync36(
|
|
13575
|
+
return existsSync36(join58(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
13426
13576
|
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
13427
13577
|
return bad("group section malformed");
|
|
13428
13578
|
const entries = g["units"];
|
|
@@ -13434,20 +13584,20 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13434
13584
|
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
13435
13585
|
let unchecked = null;
|
|
13436
13586
|
for (let i = 0;i < entries.length; i++) {
|
|
13437
|
-
const e = entries[i], id = ids[i], dir =
|
|
13587
|
+
const e = entries[i], id = ids[i], dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
|
|
13438
13588
|
if (e["index"] !== i)
|
|
13439
13589
|
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
13440
13590
|
let bytes;
|
|
13441
13591
|
try {
|
|
13442
13592
|
if (!statSync17(dir).isDirectory())
|
|
13443
13593
|
return bad(`unit ${id} is not a directory`);
|
|
13444
|
-
bytes =
|
|
13594
|
+
bytes = readFileSync43(rp);
|
|
13445
13595
|
} catch {
|
|
13446
13596
|
return bad(`unit ${id} sub-receipt missing`);
|
|
13447
13597
|
}
|
|
13448
13598
|
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
13449
13599
|
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
13450
|
-
const evSha = existsSync36(ep) ? sha2(
|
|
13600
|
+
const evSha = existsSync36(ep) ? sha2(readFileSync43(ep)) : null;
|
|
13451
13601
|
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
13452
13602
|
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
13453
13603
|
const sub = readJson5(rp);
|
|
@@ -13467,8 +13617,8 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13467
13617
|
}
|
|
13468
13618
|
function compositeVerdict(childVerdicts) {
|
|
13469
13619
|
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:
|
|
13620
|
+
const failed2 = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
|
|
13621
|
+
return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed2, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
|
|
13472
13622
|
}
|
|
13473
13623
|
async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
13474
13624
|
const runDir = dirname25(receiptPath);
|
|
@@ -13479,13 +13629,13 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13479
13629
|
return bad("body malformed");
|
|
13480
13630
|
let ibytes;
|
|
13481
13631
|
try {
|
|
13482
|
-
ibytes =
|
|
13632
|
+
ibytes = readFileSync43(join58(runDir, "integration.json"));
|
|
13483
13633
|
} catch {
|
|
13484
13634
|
return bad("integration.json missing");
|
|
13485
13635
|
}
|
|
13486
13636
|
if (sha2(ibytes) !== receipt["integration_sha256"])
|
|
13487
13637
|
return bad("integration.json does not match its recorded hash");
|
|
13488
|
-
const integ = readJson5(
|
|
13638
|
+
const integ = readJson5(join58(runDir, "integration.json"));
|
|
13489
13639
|
if (!isObj10(integ) || !isObj10(integ["outcomes"]))
|
|
13490
13640
|
return bad("integration.json has no outcomes");
|
|
13491
13641
|
const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
|
|
@@ -13503,10 +13653,10 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13503
13653
|
return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
|
|
13504
13654
|
if (outcomes[repo] !== "ok")
|
|
13505
13655
|
return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
|
|
13506
|
-
const rp =
|
|
13656
|
+
const rp = join58(runDir, rel);
|
|
13507
13657
|
let bytes;
|
|
13508
13658
|
try {
|
|
13509
|
-
bytes =
|
|
13659
|
+
bytes = readFileSync43(rp);
|
|
13510
13660
|
} catch {
|
|
13511
13661
|
return bad(`child ${repo} receipt missing`);
|
|
13512
13662
|
}
|
|
@@ -13541,15 +13691,15 @@ function compositeHash(r) {
|
|
|
13541
13691
|
const { composite_sha256: _c, verification: _v, ...body } = r;
|
|
13542
13692
|
return sha2(canonicalJson(body));
|
|
13543
13693
|
}
|
|
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) =>
|
|
13694
|
+
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
13695
|
try {
|
|
13546
|
-
return JSON.parse(
|
|
13696
|
+
return JSON.parse(readFileSync43(p, "utf8"));
|
|
13547
13697
|
} catch {
|
|
13548
13698
|
return;
|
|
13549
13699
|
}
|
|
13550
13700
|
}, dirNames = (d) => {
|
|
13551
13701
|
try {
|
|
13552
|
-
return
|
|
13702
|
+
return readdirSync18(d).filter((n2) => !n2.startsWith(".")).sort();
|
|
13553
13703
|
} catch {
|
|
13554
13704
|
return null;
|
|
13555
13705
|
}
|
|
@@ -13561,8 +13711,8 @@ var init_seal_group = __esm(() => {
|
|
|
13561
13711
|
});
|
|
13562
13712
|
|
|
13563
13713
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
13564
|
-
import { existsSync as existsSync37, readFileSync as
|
|
13565
|
-
import { basename as basename20, join as
|
|
13714
|
+
import { existsSync as existsSync37, readFileSync as readFileSync44, statSync as statSync18 } from "fs";
|
|
13715
|
+
import { basename as basename20, join as join59 } from "path";
|
|
13566
13716
|
function supplyEnabled(env) {
|
|
13567
13717
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
13568
13718
|
}
|
|
@@ -13577,13 +13727,13 @@ function isManifestPath(path) {
|
|
|
13577
13727
|
return MANIFESTS.test(basename20(path));
|
|
13578
13728
|
}
|
|
13579
13729
|
function readDeclared(repoDir) {
|
|
13580
|
-
const p =
|
|
13730
|
+
const p = join59(repoDir, ".loki", DECLARED_FILE);
|
|
13581
13731
|
if (!existsSync37(p))
|
|
13582
13732
|
return { deps: [], problem: null };
|
|
13583
13733
|
try {
|
|
13584
13734
|
if (statSync18(p).size > MAX_FILE_BYTES)
|
|
13585
13735
|
return { deps: [], problem: "declaration file too large" };
|
|
13586
|
-
const j = JSON.parse(
|
|
13736
|
+
const j = JSON.parse(readFileSync44(p, "utf8"));
|
|
13587
13737
|
if (!Array.isArray(j))
|
|
13588
13738
|
return { deps: [], problem: "declaration is not a JSON array" };
|
|
13589
13739
|
const deps = [];
|
|
@@ -13603,11 +13753,11 @@ function readDeclared(repoDir) {
|
|
|
13603
13753
|
}
|
|
13604
13754
|
}
|
|
13605
13755
|
function readAllowlist(repoDir) {
|
|
13606
|
-
const p =
|
|
13756
|
+
const p = join59(repoDir, ".loki", "supply-allowlist");
|
|
13607
13757
|
if (!existsSync37(p))
|
|
13608
13758
|
return new Set;
|
|
13609
13759
|
try {
|
|
13610
|
-
return new Set(
|
|
13760
|
+
return new Set(readFileSync44(p, "utf8").split(`
|
|
13611
13761
|
`).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
|
|
13612
13762
|
} catch {
|
|
13613
13763
|
return new Set;
|
|
@@ -13648,7 +13798,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13648
13798
|
if (eco === "npm") {
|
|
13649
13799
|
if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
|
|
13650
13800
|
return true;
|
|
13651
|
-
return files(
|
|
13801
|
+
return files(join59(repoDir, ".npmrc"), home ? join59(home, ".npmrc") : "").some((t) => t.split(`
|
|
13652
13802
|
`).some((l) => {
|
|
13653
13803
|
const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
|
|
13654
13804
|
return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
|
|
@@ -13657,7 +13807,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13657
13807
|
if (eco === "pypi") {
|
|
13658
13808
|
if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
|
|
13659
13809
|
return true;
|
|
13660
|
-
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ?
|
|
13810
|
+
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join59(home, ".config", "pip", "pip.conf") : "", home ? join59(home, ".pip", "pip.conf") : "", join59(repoDir, "pip.conf")];
|
|
13661
13811
|
return files(...cfg).some((t) => t.split(`
|
|
13662
13812
|
`).some((l) => {
|
|
13663
13813
|
const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
|
|
@@ -13667,7 +13817,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
13667
13817
|
if (eco === "cargo") {
|
|
13668
13818
|
if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
|
|
13669
13819
|
return true;
|
|
13670
|
-
return files(
|
|
13820
|
+
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
13821
|
}
|
|
13672
13822
|
if (eco === "go") {
|
|
13673
13823
|
const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
|
|
@@ -13790,7 +13940,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
|
|
|
13790
13940
|
}
|
|
13791
13941
|
}, 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
13942
|
try {
|
|
13793
|
-
return statSync18(p).size <= MAX_FILE_BYTES ?
|
|
13943
|
+
return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync44(p, "utf8") : "";
|
|
13794
13944
|
} catch {
|
|
13795
13945
|
return "";
|
|
13796
13946
|
}
|
|
@@ -13806,8 +13956,8 @@ var init_supply_guard = __esm(() => {
|
|
|
13806
13956
|
|
|
13807
13957
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
13808
13958
|
import { createHash as createHash15 } from "crypto";
|
|
13809
|
-
import { existsSync as existsSync38, readdirSync as
|
|
13810
|
-
import { basename as basename21, isAbsolute as isAbsolute9, join as
|
|
13959
|
+
import { existsSync as existsSync38, readdirSync as readdirSync19, mkdirSync as mkdirSync24, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
|
|
13960
|
+
import { basename as basename21, isAbsolute as isAbsolute9, join as join60, relative as relative9 } from "path";
|
|
13811
13961
|
function sanitizeReason(s) {
|
|
13812
13962
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
13813
13963
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
@@ -13882,7 +14032,7 @@ function renderReceiptMd(r) {
|
|
|
13882
14032
|
`);
|
|
13883
14033
|
}
|
|
13884
14034
|
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,
|
|
14035
|
+
const r = relative9(ctx.repoDir, join60(ctx.runDir, "receipt.json"));
|
|
13886
14036
|
return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
13887
14037
|
}, commitStage, sealStage;
|
|
13888
14038
|
var init_seal = __esm(() => {
|
|
@@ -13985,7 +14135,7 @@ var init_seal = __esm(() => {
|
|
|
13985
14135
|
if (wallNotRun > 0) {
|
|
13986
14136
|
try {
|
|
13987
14137
|
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
|
|
13988
|
-
for (const n2 of
|
|
14138
|
+
for (const n2 of readdirSync19(join60(ctx.runDir, "wall")).sort())
|
|
13989
14139
|
if (!kept.has(n2))
|
|
13990
14140
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
13991
14141
|
} catch {}
|
|
@@ -14046,19 +14196,19 @@ var init_seal = __esm(() => {
|
|
|
14046
14196
|
for (const [s, d] of Object.entries(o))
|
|
14047
14197
|
if (typeof d?.duration_s === "number")
|
|
14048
14198
|
stages[s] = d.duration_s;
|
|
14049
|
-
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(
|
|
14199
|
+
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
14200
|
const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
|
|
14051
14201
|
if (iterIds.length === 0)
|
|
14052
14202
|
notProven.add("cost not measured (no iteration ids recorded)");
|
|
14053
14203
|
const cost = ctx.cost.read(ctx.repoDir, iterIds);
|
|
14054
14204
|
if (cost.unmetered)
|
|
14055
14205
|
notProven.add("cost unmetered (CLI invoker; recorded as 0)");
|
|
14056
|
-
const eventsPath =
|
|
14206
|
+
const eventsPath = join60(ctx.runDir, "events.jsonl");
|
|
14057
14207
|
const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
|
|
14058
14208
|
const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
|
|
14059
14209
|
if (wallFiles.length > 0 && wallPassed === null)
|
|
14060
14210
|
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(
|
|
14211
|
+
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
14212
|
if (routeBlock)
|
|
14063
14213
|
for (const l of routeNotProven(routeBlock))
|
|
14064
14214
|
notProven.add(l);
|
|
@@ -14100,7 +14250,7 @@ var init_seal = __esm(() => {
|
|
|
14100
14250
|
provider: ctx.provider,
|
|
14101
14251
|
model: ctx.model,
|
|
14102
14252
|
resumed: o.intake?.resumed === true,
|
|
14103
|
-
events_sha256: sha2562(existsSync38(eventsPath) ?
|
|
14253
|
+
events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync45(eventsPath) : ""),
|
|
14104
14254
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
14105
14255
|
log_seal: true,
|
|
14106
14256
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -14121,10 +14271,10 @@ var init_seal = __esm(() => {
|
|
|
14121
14271
|
}
|
|
14122
14272
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
14123
14273
|
mkdirSync24(ctx.runDir, { recursive: true });
|
|
14124
|
-
const path =
|
|
14274
|
+
const path = join60(ctx.runDir, "receipt.json");
|
|
14125
14275
|
writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
|
|
14126
14276
|
`);
|
|
14127
|
-
writeFileSync26(
|
|
14277
|
+
writeFileSync26(join60(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
14128
14278
|
recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
|
|
14129
14279
|
const signed = sig.jwt !== null;
|
|
14130
14280
|
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 +14293,8 @@ var init_seal = __esm(() => {
|
|
|
14143
14293
|
});
|
|
14144
14294
|
|
|
14145
14295
|
// ../../loki-ts/src/features/receipt_dsse.ts
|
|
14146
|
-
import { createPublicKey as createPublicKey3, sign as sign2, verify as
|
|
14147
|
-
import { readFileSync as
|
|
14296
|
+
import { createPublicKey as createPublicKey3, sign as sign2, verify as verify3 } from "crypto";
|
|
14297
|
+
import { readFileSync as readFileSync46 } from "fs";
|
|
14148
14298
|
function pae(payloadType, body) {
|
|
14149
14299
|
const t = Buffer.from(payloadType, "utf8");
|
|
14150
14300
|
return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
|
|
@@ -14163,7 +14313,7 @@ function verifyEnvelope(env, pubFor) {
|
|
|
14163
14313
|
continue;
|
|
14164
14314
|
}
|
|
14165
14315
|
known++;
|
|
14166
|
-
if (
|
|
14316
|
+
if (verify3(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
|
|
14167
14317
|
kid = s.keyid;
|
|
14168
14318
|
break;
|
|
14169
14319
|
}
|
|
@@ -14221,7 +14371,7 @@ function verifyDsseReceipt(env, d) {
|
|
|
14221
14371
|
}
|
|
14222
14372
|
function runIdGuard(receiptPath, runId) {
|
|
14223
14373
|
try {
|
|
14224
|
-
return envelopeRunIdProblem(JSON.parse(
|
|
14374
|
+
return envelopeRunIdProblem(JSON.parse(readFileSync46(receiptPath, "utf8")), runId);
|
|
14225
14375
|
} catch {
|
|
14226
14376
|
return null;
|
|
14227
14377
|
}
|
|
@@ -14235,16 +14385,16 @@ var init_keys_cmd = __esm(() => {
|
|
|
14235
14385
|
});
|
|
14236
14386
|
|
|
14237
14387
|
// ../../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
|
|
14388
|
+
import { existsSync as existsSync39, readFileSync as readFileSync47, renameSync as renameSync10, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
|
|
14389
|
+
import { basename as basename22, dirname as dirname27, join as join61 } from "path";
|
|
14390
|
+
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify4 } from "crypto";
|
|
14241
14391
|
function pubFor(kid, given) {
|
|
14242
14392
|
if (given)
|
|
14243
14393
|
return kidOf2(given) === kid ? given : undefined;
|
|
14244
14394
|
const active = loadSigningKey(false);
|
|
14245
14395
|
const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
|
|
14246
14396
|
try {
|
|
14247
|
-
return [createPublicKey4(
|
|
14397
|
+
return [createPublicKey4(readFileSync47(f))];
|
|
14248
14398
|
} catch {
|
|
14249
14399
|
return [];
|
|
14250
14400
|
}
|
|
@@ -14268,7 +14418,7 @@ function checkAttestation(jwt, expectedHash, given) {
|
|
|
14268
14418
|
const pub = pubFor(header.kid, given);
|
|
14269
14419
|
if (!pub)
|
|
14270
14420
|
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 (!
|
|
14421
|
+
if (!verify4(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url")))
|
|
14272
14422
|
return bad("signature does not verify");
|
|
14273
14423
|
return payload?.receipt_sha256 === expectedHash ? { status: "verified", reason: null } : bad("attestation binds a different receipt hash");
|
|
14274
14424
|
}
|
|
@@ -14276,17 +14426,17 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14276
14426
|
const bound = receipt["events_sha256"];
|
|
14277
14427
|
if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
|
|
14278
14428
|
return null;
|
|
14279
|
-
const path =
|
|
14429
|
+
const path = join61(dirname27(receiptPath), "events.jsonl");
|
|
14280
14430
|
if (!existsSync39(path))
|
|
14281
14431
|
return "events.jsonl is missing";
|
|
14282
|
-
const raw2 =
|
|
14432
|
+
const raw2 = readFileSync47(path, "utf8"), events2 = readEvents(path), lines2 = raw2.split(`
|
|
14283
14433
|
`).filter((l) => l.trim() !== "");
|
|
14284
|
-
if (events2.length !==
|
|
14434
|
+
if (events2.length !== lines2.length || events2.some((e, i) => e.seq !== i))
|
|
14285
14435
|
return "events.jsonl has a forged, edited or missing line";
|
|
14286
14436
|
if (events2.some((e) => e.type === "tamper.detected"))
|
|
14287
14437
|
return "the supervisor detected events.jsonl being modified during the run";
|
|
14288
14438
|
const h = createHash16("sha256");
|
|
14289
|
-
for (const l of
|
|
14439
|
+
for (const l of lines2) {
|
|
14290
14440
|
if (h.copy().digest("hex") === bound)
|
|
14291
14441
|
return null;
|
|
14292
14442
|
h.update(l + `
|
|
@@ -14295,7 +14445,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14295
14445
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
14296
14446
|
}
|
|
14297
14447
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
14298
|
-
const path =
|
|
14448
|
+
const path = join61(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
14299
14449
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
14300
14450
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
14301
14451
|
if (done < 0)
|
|
@@ -14303,12 +14453,12 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14303
14453
|
const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
|
|
14304
14454
|
if (!d)
|
|
14305
14455
|
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(
|
|
14456
|
+
const lines2 = readFileSync47(path, "utf8").split(`
|
|
14457
|
+
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines2.slice(0, done + 1).join(`
|
|
14308
14458
|
`) + `
|
|
14309
14459
|
`).digest("hex");
|
|
14310
14460
|
const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
|
|
14311
|
-
if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !
|
|
14461
|
+
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
14462
|
return { verdict: "TAMPERED", reason: "log.sealed line is invalid, forged, or records a tamper" };
|
|
14313
14463
|
return null;
|
|
14314
14464
|
}
|
|
@@ -14317,7 +14467,7 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14317
14467
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
14318
14468
|
let receipt;
|
|
14319
14469
|
try {
|
|
14320
|
-
receipt = JSON.parse((deps.read ?? ((p) =>
|
|
14470
|
+
receipt = JSON.parse((deps.read ?? ((p) => readFileSync47(p, "utf8")))(receiptPath));
|
|
14321
14471
|
} catch {
|
|
14322
14472
|
return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
|
|
14323
14473
|
}
|
|
@@ -14399,8 +14549,8 @@ var init_verify_cmd = __esm(() => {
|
|
|
14399
14549
|
});
|
|
14400
14550
|
|
|
14401
14551
|
// src/server/app.ts
|
|
14402
|
-
import { existsSync as existsSync41, readFileSync as
|
|
14403
|
-
import { join as
|
|
14552
|
+
import { existsSync as existsSync41, readFileSync as readFileSync50, statSync as statSync21 } from "fs";
|
|
14553
|
+
import { join as join64, resolve as resolve15, sep as sep14 } from "path";
|
|
14404
14554
|
|
|
14405
14555
|
// node_modules/hono/dist/request/constants.js
|
|
14406
14556
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -20172,6 +20322,9 @@ var SHA = /^[0-9a-f]{40,64}$/;
|
|
|
20172
20322
|
var MAX_RECEIPT_BYTES = 1024 * 1024;
|
|
20173
20323
|
var GIT_TIMEOUT_MS = 5000;
|
|
20174
20324
|
var cache = new Map;
|
|
20325
|
+
var FAIL_TTL_MS = 60000;
|
|
20326
|
+
var failed = new Map;
|
|
20327
|
+
var diffStatGitRuns = { n: 0 };
|
|
20175
20328
|
var gitEnv = () => ({ PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" });
|
|
20176
20329
|
function parseNumstat(out) {
|
|
20177
20330
|
const files = [];
|
|
@@ -20229,13 +20382,19 @@ function diffStatFor(db, source, run) {
|
|
|
20229
20382
|
const base = typeof r?.base_sha === "string" ? r.base_sha : "", head = typeof r?.head_sha === "string" ? r.head_sha : "";
|
|
20230
20383
|
if (!SHA.test(base) || !SHA.test(head))
|
|
20231
20384
|
return null;
|
|
20385
|
+
const failKey = `${key}:${base}:${head}`;
|
|
20386
|
+
const failedAt = failed.get(failKey);
|
|
20387
|
+
if (failedAt !== undefined && Date.now() - failedAt < FAIL_TTL_MS)
|
|
20388
|
+
return null;
|
|
20232
20389
|
try {
|
|
20390
|
+
diffStatGitRuns.n++;
|
|
20233
20391
|
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
20392
|
const files = parseNumstat(out);
|
|
20235
20393
|
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
20394
|
cache.set(key, stat);
|
|
20237
20395
|
return stat;
|
|
20238
20396
|
} catch {
|
|
20397
|
+
failed.set(failKey, Date.now());
|
|
20239
20398
|
return null;
|
|
20240
20399
|
}
|
|
20241
20400
|
}
|
|
@@ -20604,10 +20763,10 @@ function stopReason(evs, verdict) {
|
|
|
20604
20763
|
return { reason: /[.!?]$/.test(reason) ? reason : `${reason}.`, ownRules: OWN_RULES.test(text2) };
|
|
20605
20764
|
}
|
|
20606
20765
|
if (verdict === "FAILED") {
|
|
20607
|
-
const
|
|
20608
|
-
const why = typeof
|
|
20609
|
-
if (
|
|
20610
|
-
return { reason: `The run failed in the ${
|
|
20766
|
+
const failed2 = [...evs].reverse().find((e) => e.type === "stage.failed");
|
|
20767
|
+
const why = typeof failed2?.data?.reason === "string" ? oneLine(failed2.data.reason, 240) : "";
|
|
20768
|
+
if (failed2 && failed2.stage)
|
|
20769
|
+
return { reason: `The run failed in the ${failed2.stage} stage${why ? `: ${why}` : ""}.`, ownRules: false };
|
|
20611
20770
|
const bad = evs.filter((e) => e.type === "test.result" && e.data.result === "fail").length;
|
|
20612
20771
|
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
20772
|
}
|
|
@@ -22197,6 +22356,7 @@ var isObj4 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
|
22197
22356
|
var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
|
|
22198
22357
|
var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
22199
22358
|
var MAX_CHECKPOINTS = 50;
|
|
22359
|
+
var ROLLBACK_ALLOW = new Set(["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json", "state", "queue"]);
|
|
22200
22360
|
var validCheckpointId = (id) => SAFE_ID.test(id);
|
|
22201
22361
|
var storeOf = (repoDir) => join17(repoDir, ".loki", "state", "checkpoints");
|
|
22202
22362
|
var lokiOf = (repoDir) => join17(repoDir, ".loki");
|
|
@@ -22336,16 +22496,33 @@ function rollbackCheckpoint(repoDir, id, now2 = new Date) {
|
|
|
22336
22496
|
`);
|
|
22337
22497
|
let restored = 0;
|
|
22338
22498
|
const errors = [];
|
|
22499
|
+
const skipped = (src) => src === join17(real2, "state", "checkpoints");
|
|
22339
22500
|
for (const e of readdirSync4(real2, { withFileTypes: true })) {
|
|
22340
22501
|
if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
|
|
22341
22502
|
continue;
|
|
22342
|
-
|
|
22503
|
+
if (!ROLLBACK_ALLOW.has(e.name)) {
|
|
22504
|
+
errors.push(`${e.name}: not in the rollback allowlist, skipped`);
|
|
22505
|
+
continue;
|
|
22506
|
+
}
|
|
22507
|
+
const src = join17(real2, e.name), dst = join17(loki, e.name);
|
|
22343
22508
|
try {
|
|
22344
22509
|
if (lstatSync2(src).isSymbolicLink()) {
|
|
22345
22510
|
errors.push(`${e.name}: symlink skipped`);
|
|
22346
22511
|
continue;
|
|
22347
22512
|
}
|
|
22348
|
-
|
|
22513
|
+
if (existsSync8(dst) && lstatSync2(dst).isSymbolicLink()) {
|
|
22514
|
+
errors.push(`${e.name}: destination is a symlink, skipped`);
|
|
22515
|
+
continue;
|
|
22516
|
+
}
|
|
22517
|
+
cpSync(src, dst, { recursive: true, filter: (s) => {
|
|
22518
|
+
if (skipped(s))
|
|
22519
|
+
return false;
|
|
22520
|
+
if (lstatSync2(s).isSymbolicLink()) {
|
|
22521
|
+
errors.push(`${s.slice(real2.length + 1)}: nested symlink skipped`);
|
|
22522
|
+
return false;
|
|
22523
|
+
}
|
|
22524
|
+
return true;
|
|
22525
|
+
} });
|
|
22349
22526
|
restored++;
|
|
22350
22527
|
} catch (err) {
|
|
22351
22528
|
errors.push(`${e.name}: ${err.message}`);
|
|
@@ -23567,8 +23744,8 @@ function tasksList(loki, status) {
|
|
|
23567
23744
|
const out2 = order2.map((o) => ("ref" in o) ? byId.get(o.ref) : o);
|
|
23568
23745
|
for (const t of out2) {
|
|
23569
23746
|
if (t.type === "iteration" && !t.description) {
|
|
23570
|
-
const ec = t.exitCode,
|
|
23571
|
-
const o = Number.isInteger(ec) && ec === 0 && !
|
|
23747
|
+
const ec = t.exitCode, failed2 = t._terminal_outcome === "failed";
|
|
23748
|
+
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
23749
|
if (o)
|
|
23573
23750
|
t.description = `Iteration ${o}${t.provider ? `, built by ${t.provider}` : ""}.`;
|
|
23574
23751
|
}
|
|
@@ -24575,7 +24752,7 @@ function mount19(ctx) {
|
|
|
24575
24752
|
function computeStats(db, since) {
|
|
24576
24753
|
const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
|
|
24577
24754
|
const by = {};
|
|
24578
|
-
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0,
|
|
24755
|
+
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed2 = 0, satisfied = 0, other = 0;
|
|
24579
24756
|
let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
|
|
24580
24757
|
for (const r of all) {
|
|
24581
24758
|
if (r.endedAt)
|
|
@@ -24593,7 +24770,7 @@ function computeStats(db, since) {
|
|
|
24593
24770
|
else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
|
|
24594
24771
|
satisfied++;
|
|
24595
24772
|
else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
|
|
24596
|
-
|
|
24773
|
+
failed2++;
|
|
24597
24774
|
else
|
|
24598
24775
|
other++;
|
|
24599
24776
|
}
|
|
@@ -24625,8 +24802,8 @@ function computeStats(db, since) {
|
|
|
24625
24802
|
runs_running: running,
|
|
24626
24803
|
by_verdict: by,
|
|
24627
24804
|
blocked_waiting: blocked,
|
|
24628
|
-
verified_rate: verified + unchecked +
|
|
24629
|
-
breakdown: { verified, failed, already_satisfied: satisfied, other },
|
|
24805
|
+
verified_rate: verified + unchecked + failed2 ? verified / (verified + unchecked + failed2) : null,
|
|
24806
|
+
breakdown: { verified, failed: failed2, already_satisfied: satisfied, other },
|
|
24630
24807
|
verified_unchecked: unchecked,
|
|
24631
24808
|
keys_configured: pubkeysFromEnv().configured === true,
|
|
24632
24809
|
...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
|
|
@@ -24650,9 +24827,9 @@ function mount20(ctx) {
|
|
|
24650
24827
|
}
|
|
24651
24828
|
|
|
24652
24829
|
// src/server/routes/verify.ts
|
|
24653
|
-
import { readFileSync as
|
|
24830
|
+
import { readFileSync as readFileSync48, realpathSync as realpathSync20 } from "fs";
|
|
24654
24831
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
24655
|
-
import { join as
|
|
24832
|
+
import { join as join62, sep as sep13 } from "path";
|
|
24656
24833
|
init_verify_cmd();
|
|
24657
24834
|
init_seal();
|
|
24658
24835
|
init_receipt_dsse();
|
|
@@ -24679,11 +24856,11 @@ function mount21(ctx) {
|
|
|
24679
24856
|
return done(404, "receipt not available", "not_found");
|
|
24680
24857
|
let runDir, file;
|
|
24681
24858
|
try {
|
|
24682
|
-
const root = realpathSync20(
|
|
24683
|
-
runDir = realpathSync20(
|
|
24684
|
-
if (runDir !==
|
|
24859
|
+
const root = realpathSync20(join62(repo.p, ".loki", "runs"));
|
|
24860
|
+
runDir = realpathSync20(join62(root, run2));
|
|
24861
|
+
if (runDir !== join62(root, run2))
|
|
24685
24862
|
return done(404, "receipt not found", "not_found");
|
|
24686
|
-
file = realpathSync20(
|
|
24863
|
+
file = realpathSync20(join62(runDir, "receipt.json"));
|
|
24687
24864
|
} catch {
|
|
24688
24865
|
return done(404, "receipt not found", "not_found");
|
|
24689
24866
|
}
|
|
@@ -24702,7 +24879,7 @@ function mount21(ctx) {
|
|
|
24702
24879
|
sha3 = r.receiptSha256 ?? null;
|
|
24703
24880
|
}
|
|
24704
24881
|
try {
|
|
24705
|
-
outcome = outcomeOf(JSON.parse(
|
|
24882
|
+
outcome = outcomeOf(JSON.parse(readFileSync48(file, "utf8")));
|
|
24706
24883
|
} catch {
|
|
24707
24884
|
outcome = "UNREADABLE";
|
|
24708
24885
|
}
|
|
@@ -24764,8 +24941,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
24764
24941
|
}
|
|
24765
24942
|
|
|
24766
24943
|
// src/server/legacy/shim.ts
|
|
24767
|
-
import { existsSync as existsSync40, readFileSync as
|
|
24768
|
-
import { join as
|
|
24944
|
+
import { existsSync as existsSync40, readFileSync as readFileSync49 } from "fs";
|
|
24945
|
+
import { join as join63 } from "path";
|
|
24769
24946
|
|
|
24770
24947
|
// src/server/legacy/routes.ts
|
|
24771
24948
|
var LEGACY_ROUTES = [
|
|
@@ -25108,11 +25285,12 @@ function metricsMapped(repoDir) {
|
|
|
25108
25285
|
// src/server/legacy/routes-control.ts
|
|
25109
25286
|
function controlMapped(db, repoDir) {
|
|
25110
25287
|
const loki = () => lokiDirOf(repoDir);
|
|
25288
|
+
const ctl = (action) => (c) => peerIsLoopback(c) && isLoopbackHost(c.req.header("host")) ? runControl(db, repoDir, action, c, "legacy") : c.json({ error: "loopback only" }, 403);
|
|
25111
25289
|
return {
|
|
25112
|
-
"POST /api/control/pause": (
|
|
25113
|
-
"POST /api/control/resume": (
|
|
25114
|
-
"POST /api/control/stop": (
|
|
25115
|
-
"POST /api/council/force-review": (
|
|
25290
|
+
"POST /api/control/pause": ctl("pause"),
|
|
25291
|
+
"POST /api/control/resume": ctl("resume"),
|
|
25292
|
+
"POST /api/control/stop": ctl("stop"),
|
|
25293
|
+
"POST /api/council/force-review": ctl("council-review"),
|
|
25116
25294
|
"GET /api/council/state": (c) => c.json(councilState(loki())),
|
|
25117
25295
|
"GET /api/council/verdicts": (c) => {
|
|
25118
25296
|
const n2 = intQ(c.req.query("limit"), 1000);
|
|
@@ -25137,10 +25315,10 @@ var logged = new Set;
|
|
|
25137
25315
|
var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
|
|
25138
25316
|
var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
25139
25317
|
var repoVersion = () => {
|
|
25140
|
-
for (const f of [
|
|
25318
|
+
for (const f of [join63(import.meta.dir, "../../../../../VERSION")]) {
|
|
25141
25319
|
try {
|
|
25142
25320
|
if (existsSync40(f))
|
|
25143
|
-
return
|
|
25321
|
+
return readFileSync49(f, "utf8").trim();
|
|
25144
25322
|
} catch {}
|
|
25145
25323
|
}
|
|
25146
25324
|
return "unknown";
|
|
@@ -25322,7 +25500,7 @@ function legacyShim(opts) {
|
|
|
25322
25500
|
|
|
25323
25501
|
// src/server/app.ts
|
|
25324
25502
|
var MAX_BODY2 = 1e6;
|
|
25325
|
-
var defaultUiDir = () => [
|
|
25503
|
+
var defaultUiDir = () => [join64(import.meta.dir, "../../ui/dist"), join64(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join64(d, "index.html")));
|
|
25326
25504
|
function createApp(opts) {
|
|
25327
25505
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
25328
25506
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -25351,10 +25529,10 @@ function createApp(opts) {
|
|
|
25351
25529
|
await next();
|
|
25352
25530
|
});
|
|
25353
25531
|
const readInstalled = () => {
|
|
25354
|
-
for (const f of [process.env.LOKI_VERSION_FILE,
|
|
25532
|
+
for (const f of [process.env.LOKI_VERSION_FILE, join64(import.meta.dir, "../../../../VERSION")]) {
|
|
25355
25533
|
try {
|
|
25356
25534
|
if (f)
|
|
25357
|
-
return
|
|
25535
|
+
return readFileSync50(f, "utf8").trim() || "unknown";
|
|
25358
25536
|
} catch {}
|
|
25359
25537
|
}
|
|
25360
25538
|
return "unknown";
|
|
@@ -25469,7 +25647,7 @@ function createApp(opts) {
|
|
|
25469
25647
|
return new Response(Bun.file(f));
|
|
25470
25648
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
25471
25649
|
return c.json({ error: "not found" }, 404);
|
|
25472
|
-
return new Response(Bun.file(
|
|
25650
|
+
return new Response(Bun.file(join64(root, "index.html")));
|
|
25473
25651
|
});
|
|
25474
25652
|
return { app, db, close: () => {
|
|
25475
25653
|
clearInterval(reconciler);
|