loki-mode 11.17.37 → 11.17.39
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/chat.sh +3 -2
- package/autonomy/lib/config-map.sh +11 -4
- package/autonomy/lib/secret-scan.sh +1 -0
- package/autonomy/loki +172 -8
- package/autonomy/verify.sh +1 -0
- package/completions/_loki +5 -0
- package/completions/loki.bash +2 -2
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/DEPLOYMENT.md +22 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/BOARD.md +52 -19
- package/docs/v10/PROGRESS.md +6 -0
- package/loki-ts/dist/loki.js +1274 -1257
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +512 -318
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-8Ms4e7v3.js → AdminPage-sMRXVp8V.js} +1 -1
- package/web-app/dist/assets/{Avatar-D-_RMtAF.js → Avatar-sqshsKDj.js} +1 -1
- package/web-app/dist/assets/{Badge-BiTryACM.js → Badge-DVLT6AZl.js} +1 -1
- package/web-app/dist/assets/{Button-D5J90sJA.js → Button-CUGOx2F-.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-CBsCLHsX.js → CockpitPage-_5XpWSyB.js} +1 -1
- package/web-app/dist/assets/{ComparePage-ZU4NYIxk.js → ComparePage-CHybAwF6.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-kRsIDD2c.js → ErrorBoundary-yws0dvvJ.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-DV6ZxN0r.js → EvidenceReceiptPanel-DQPXKaYq.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-B0uPt2Uy.js → GitHubIssuesPanel-CDnklfFn.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-DnrIz7Qh.js → GitHubPRsPanel-F1H3q6Mg.js} +1 -1
- package/web-app/dist/assets/{HomePage-SjkWXTYO.js → HomePage-Donxu3UG.js} +2 -2
- package/web-app/dist/assets/{LoginPage-D1XU1UOK.js → LoginPage-vabZaQ4T.js} +1 -1
- package/web-app/dist/assets/{MagicPage-Cg6MzSOR.js → MagicPage-zJiSFKm6.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-Bs4xxEL4.js → MetricsPage-tW1d9Pc4.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-xoIjTbo3.js → NotFoundPage-DfL2kcAp.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-DtX7g5N3.js → ProjectPage-Bnx3Slii.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-BZiUAYq4.js → ProjectsPage-C74jNljb.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-BGWiG3O9.js → SettingsPage-FA9_6XvR.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-DgrnwUsK.js → ShowcasePage-CUYKoPB0.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-cHvinh4M.js → SystemSettingsPage-ClEk_OL-.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BVCDcah9.js → TeamsPage-CZod1nxw.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-BVZWaHmc.js → TemplatesPage-ftxcEJ5S.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-DXipqPhV.js → TerminalOutput-C-ztoDne.js} +1 -1
- package/web-app/dist/assets/{activity-CshhOoCC.js → activity-ClWb6nuh.js} +1 -1
- package/web-app/dist/assets/{bell-C2aVKCgl.js → bell-DSdTj9Kf.js} +1 -1
- package/web-app/dist/assets/{bot-CS4hsHcl.js → bot-DX8ebhTj.js} +1 -1
- package/web-app/dist/assets/{check-C9BOGqR2.js → check-6OAuuO_W.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BBTdOAuW.js → chevron-left-DTdjxSL_.js} +1 -1
- package/web-app/dist/assets/{circle-alert-D7SSuEIf.js → circle-alert-B40DtHik.js} +1 -1
- package/web-app/dist/assets/{clock-CHFYgPU3.js → clock-cVEP2obv.js} +1 -1
- package/web-app/dist/assets/{cloud-cEefzPuf.js → cloud-CmG80ODy.js} +1 -1
- package/web-app/dist/assets/{code-xml-B7St2q5i.js → code-xml-uDISZWZx.js} +1 -1
- package/web-app/dist/assets/{database-irsYoPwy.js → database-BBjrBzZH.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-ChdZBjDE.js → dollar-sign-QuSiEBvi.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CdnsaqHY.js → file-code-corner-DaYWw_54.js} +1 -1
- package/web-app/dist/assets/{file-plus-Cm5WJLtA.js → file-plus-DBrjgfG0.js} +1 -1
- package/web-app/dist/assets/{globe-itCMNb3u.js → globe-yLg95rUG.js} +1 -1
- package/web-app/dist/assets/{hammer-DpZbHRqZ.js → hammer-Cm_tQ3f7.js} +1 -1
- package/web-app/dist/assets/{index-BY6H3gcR.js → index-WwgJyEgR.js} +3 -3
- package/web-app/dist/assets/{layers-Pja4FEia.js → layers-CVu0QAr_.js} +1 -1
- package/web-app/dist/assets/{loader-circle-CeRQm3kJ.js → loader-circle-C1ByA4HR.js} +1 -1
- package/web-app/dist/assets/{lock-DN-Y7N_K.js → lock-COFCj4kU.js} +1 -1
- package/web-app/dist/assets/{package-xmriszo-.js → package-DPdWCR4l.js} +1 -1
- package/web-app/dist/assets/{plus-_Uj6A6jB.js → plus-CP2Oh8yP.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BsssvIIM.js → refresh-cw-CR9Wf9cI.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-BuGEgNBP.js → rotate-ccw-jJugFDir.js} +1 -1
- package/web-app/dist/assets/{scroll-text-D3I5iPxb.js → scroll-text-BiJUO5k9.js} +1 -1
- package/web-app/dist/assets/{server-CmqFqyr6.js → server-US70f4RI.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BDfXzBqa.js → shield-alert-C7ig1ziQ.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-_tO386xs.js → thumbs-up-D3SDwx7o.js} +1 -1
- package/web-app/dist/assets/{trash-2-BY17xDYh.js → trash-2-CikJqAs_.js} +1 -1
- package/web-app/dist/assets/{trending-down-2q53wyOn.js → trending-down-Bcyok37Q.js} +1 -1
- package/web-app/dist/assets/{trending-up-k3BMoWKv.js → trending-up-CFJbw392.js} +1 -1
- package/web-app/dist/assets/{upload-BchR2Xs4.js → upload-BvhLwHkC.js} +1 -1
- package/web-app/dist/assets/{usePolling-DO8v6wKt.js → usePolling-Bck_ydy1.js} +1 -1
- package/web-app/dist/assets/{user-ThJCW69N.js → user-BpuwYFeB.js} +1 -1
- package/web-app/dist/index.html +1 -1
|
@@ -146,7 +146,9 @@ var init_redact = __esm(() => {
|
|
|
146
146
|
[/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, "[REDACTED:JWT]"],
|
|
147
147
|
[/AI[a-zA-Z0-9_-]{30,}/g, "[REDACTED:GOOGLE_KEY]"],
|
|
148
148
|
[/sk-[A-Za-z0-9_-]{20,}/g, "[REDACTED:OPENAI_KEY]"],
|
|
149
|
-
[/(Bearer\s+)[A-Za-z0-9._~+/=-]{20,}/g, "$1[REDACTED]"]
|
|
149
|
+
[/(Bearer\s+)[A-Za-z0-9._~+/=-]{20,}/g, "$1[REDACTED]"],
|
|
150
|
+
[/(\b[a-z][a-z0-9+.-]*:\/\/)[^\s/?#@]+@/gi, "$1[REDACTED]@"],
|
|
151
|
+
[/(\bAuthorization\s*[:=]\s*)(?:Basic|Bearer|Token|Digest)?\s*[^\s,;"']+/gi, "$1[REDACTED]"]
|
|
150
152
|
];
|
|
151
153
|
});
|
|
152
154
|
|
|
@@ -7795,9 +7797,7 @@ function readJson3(path, isValid = (_v) => true) {
|
|
|
7795
7797
|
function isRepoMap(v) {
|
|
7796
7798
|
return typeof v === "object" && v !== null && Array.isArray(v.files) && Array.isArray(v.entries);
|
|
7797
7799
|
}
|
|
7798
|
-
|
|
7799
|
-
return readJson3(resolve8(dir, `repomap2-${tree}.json`), isRepoMap);
|
|
7800
|
-
}
|
|
7800
|
+
var readRepoMapCache = (dir, tree) => readJson3(resolve8(dir, `repomap2-${tree}.json`), isRepoMap);
|
|
7801
7801
|
var init_cache = __esm(() => {
|
|
7802
7802
|
init_paths();
|
|
7803
7803
|
});
|
|
@@ -9791,8 +9791,8 @@ var init_testmap = __esm(() => {
|
|
|
9791
9791
|
});
|
|
9792
9792
|
|
|
9793
9793
|
// ../../loki-ts/src/util/check_result.ts
|
|
9794
|
-
import { mkdirSync as mkdirSync13, readFileSync as readFileSync29, renameSync as renameSync8, writeFileSync as writeFileSync16 } from "fs";
|
|
9795
|
-
import { basename as basename11, join as join38 } from "path";
|
|
9794
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync29, realpathSync as realpathSync17, renameSync as renameSync8, writeFileSync as writeFileSync16 } from "fs";
|
|
9795
|
+
import { basename as basename11, isAbsolute as isAbsolute3, join as join38 } from "path";
|
|
9796
9796
|
function plainTestEnv(base = process.env) {
|
|
9797
9797
|
const e = {};
|
|
9798
9798
|
for (const [k, v] of Object.entries(tokenFreeEnv(base)))
|
|
@@ -10006,6 +10006,8 @@ function checkProvenance(name, ctx) {
|
|
|
10006
10006
|
return "independent";
|
|
10007
10007
|
if (ctx.addedFiles.some((a) => normPath(a) === p))
|
|
10008
10008
|
return "agent_authored";
|
|
10009
|
+
if (ctx.modifiedFiles?.some((m) => normPath(m) === p))
|
|
10010
|
+
return "agent_authored";
|
|
10009
10011
|
}
|
|
10010
10012
|
return "exogenous";
|
|
10011
10013
|
}
|
|
@@ -10161,6 +10163,92 @@ function failureExcerpt(out2, repoDir = "", cap = EXCERPT_GROUP_CAP) {
|
|
|
10161
10163
|
body = redactSecrets(body);
|
|
10162
10164
|
return truncateBytes(body, cap);
|
|
10163
10165
|
}
|
|
10166
|
+
function pytestCollectionIsRed(output, repoDir) {
|
|
10167
|
+
if (/ModuleNotFoundError/.test(output))
|
|
10168
|
+
return false;
|
|
10169
|
+
const real2 = (p) => {
|
|
10170
|
+
try {
|
|
10171
|
+
return realpathSync17(isAbsolute3(p) ? p : join38(repoDir, p));
|
|
10172
|
+
} catch {
|
|
10173
|
+
return null;
|
|
10174
|
+
}
|
|
10175
|
+
};
|
|
10176
|
+
const repoReal = real2(repoDir);
|
|
10177
|
+
const under = (p) => {
|
|
10178
|
+
const r = real2(p);
|
|
10179
|
+
return !!repoReal && !!r && (r === repoReal || r.startsWith(`${repoReal}/`));
|
|
10180
|
+
};
|
|
10181
|
+
const imp = /ImportError: cannot import name .* from ['"][\w.]+['"] \(([^)]+)\)/.exec(output);
|
|
10182
|
+
if (imp)
|
|
10183
|
+
return under(imp[1]);
|
|
10184
|
+
const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
|
|
10185
|
+
if (!hdr)
|
|
10186
|
+
return false;
|
|
10187
|
+
const cls = [...output.slice(hdr.index).matchAll(/^ERROR \S+ - (\w+Error): /gm)].pop()?.[1];
|
|
10188
|
+
if (cls !== "AttributeError" && cls !== "NameError")
|
|
10189
|
+
return false;
|
|
10190
|
+
const start = output.search(/^_+ ERROR collecting /m);
|
|
10191
|
+
if (start < 0 || start > hdr.index)
|
|
10192
|
+
return false;
|
|
10193
|
+
const block = output.slice(start, hdr.index).split(/^-+ Captured .*$/m)[0];
|
|
10194
|
+
const eLines = [...block.matchAll(new RegExp(`^E {3}${cls}: (.*)$`, "gm"))];
|
|
10195
|
+
const eLine = eLines[eLines.length - 1];
|
|
10196
|
+
if (!eLine)
|
|
10197
|
+
return false;
|
|
10198
|
+
if (cls === "AttributeError") {
|
|
10199
|
+
const m = /^module ['"]([\w.]+)['"] has no attribute/.exec(eLine[1]);
|
|
10200
|
+
return !!m && moduleUnderRepo(repoDir, m[1]);
|
|
10201
|
+
}
|
|
10202
|
+
const frames = [...block.slice(0, eLine.index).matchAll(/^(.+?):\d+: in \S+\n\s*(.*)$/gm)];
|
|
10203
|
+
const last = frames[frames.length - 1];
|
|
10204
|
+
return !!last && under(last[1]) && !/^\s*(import\s|from\s\S+\s+import\b)/.test(last[2] ?? "");
|
|
10205
|
+
}
|
|
10206
|
+
function moduleUnderRepo(repoDir, name) {
|
|
10207
|
+
const rel = name.replace(/\./g, "/");
|
|
10208
|
+
return existsSync23(join38(repoDir, `${rel}.py`)) || existsSync23(join38(repoDir, rel, "__init__.py"));
|
|
10209
|
+
}
|
|
10210
|
+
function pytestExit1IsRed(output) {
|
|
10211
|
+
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
10212
|
+
return false;
|
|
10213
|
+
if (/^=*\s*(\d+ \w+(, )?)*\d+ failed\b.* in [\d.]+s/.test(output.trimEnd().split(`
|
|
10214
|
+
`).pop() ?? ""))
|
|
10215
|
+
return true;
|
|
10216
|
+
const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
|
|
10217
|
+
return !!hdr && /^FAILED \S+/m.test(output.slice(hdr.index));
|
|
10218
|
+
}
|
|
10219
|
+
function parsedFailCount(runner, output) {
|
|
10220
|
+
const m = runner === "bun" ? /^\s*(\d+)\s+fail\s*$/m.exec(output) : runner === "jest" ? /Tests:\s*(\d+)\s+failed/i.exec(output) : runner === "vitest" ? /Tests\s+(\d+)\s+failed/i.exec(output) : null;
|
|
10221
|
+
return m ? Number(m[1]) : 0;
|
|
10222
|
+
}
|
|
10223
|
+
function nodeIsRed(f, raw2) {
|
|
10224
|
+
const o = raw2.replace(/^# (?!(?:tests|suites|pass|fail|cancelled|skipped|todo|duration_ms|Subtest)\b)/gm, "");
|
|
10225
|
+
const mod = /^Error(?: \[\w+\])?: Cannot find (?:module|package) '([^']+)'(?:(?!\u2716)[\s\S])*?\nNode\.js v/m.exec(o), top = /^(\S+):\d+\n.*\n.*\n\n?(?:ReferenceError|SyntaxError)\b/m.exec(o);
|
|
10226
|
+
const named = [...o.matchAll(/^(?:\u2716|\s*not ok \d+ -) (?!failing tests:)(.+?)(?: \(|$)/gm)].some((m) => !f.path.endsWith(m[1]) && !m[1].endsWith(f.path)), blk = o.split("failing tests:")[1] ?? "", esm = /^SyntaxError: The requested module '([^']+)' does not provide an export named/m.exec(o), errs = [...blk.matchAll(/^ {2}(\w+Error): .*\n\s+at (?:.*\()?([^\s()]+?):\d+:\d+/gm), ...o.matchAll(/^ +name: '(\w+Error)'\n(?:.*\n)*? +stack: \|-\n\s+(?:.*\()?([^\s()]+?):\d+:\d+/gm)];
|
|
10227
|
+
return /^(?:#|\u2139) fail [1-9]/m.test(raw2) && (/^ {2}AssertionError\b/m.test(blk) || /^ +name: 'AssertionError'/m.test(o) || (mod && !named ? /^[./]/.test(mod[1]) : esm ? /^[./]/.test(esm[1]) : top ? !top[1].endsWith(f.path) : !errs.length || errs.some((e) => !/^(?:Reference|Syntax)Error$/.test(e[1]) || !(e[2].startsWith("node:") || e[2].endsWith(f.path)))));
|
|
10228
|
+
}
|
|
10229
|
+
function notRunReason(status, interpreter) {
|
|
10230
|
+
if (interpreter === "system")
|
|
10231
|
+
return "ran under the system interpreter, not the project's, so the result proves nothing";
|
|
10232
|
+
if (status === null)
|
|
10233
|
+
return "the test runner did not start or timed out";
|
|
10234
|
+
if (status === 126 || status === 127)
|
|
10235
|
+
return `the test runner command was not found or not executable (exit ${status})`;
|
|
10236
|
+
return status === 0 ? "exit 0 but no executed test count could be confirmed" : `exit ${status} but the runner output showed no readable failing test`;
|
|
10237
|
+
}
|
|
10238
|
+
function classify(f, status, output, repoDir, interpreter) {
|
|
10239
|
+
if (interpreter === "system")
|
|
10240
|
+
return "not_run";
|
|
10241
|
+
output = stripAnsi(output);
|
|
10242
|
+
if (status === null || status === 126 || status === 127)
|
|
10243
|
+
return "not_run";
|
|
10244
|
+
if (status === 0)
|
|
10245
|
+
return classifyCheck({ kind: "test", ok: true, out: output, path: f.path, ...f.runner === "go" ? { runner: "go" } : {} }).result === "pass" ? "pass" : "not_run";
|
|
10246
|
+
if (f.runner === "pytest")
|
|
10247
|
+
return status === 1 ? pytestExit1IsRed(output) ? "fail" : "not_run" : status === 2 ? pytestCollectionIsRed(output, repoDir) ? "fail" : "not_run" : "not_run";
|
|
10248
|
+
if (f.runner === "jest" || f.runner === "vitest" || f.runner === "bun")
|
|
10249
|
+
return parsedFailCount(f.runner, output) > 0 ? "fail" : "not_run";
|
|
10250
|
+
return f.runner === "node" && nodeIsRed(f, output) ? "fail" : "not_run";
|
|
10251
|
+
}
|
|
10164
10252
|
var NO_TESTS_REASON = "no tests executed", GO_EXIT0_REASON = "test count could not be confirmed: go test output is produced by the code under test and cannot confirm execution (a Go exit 0 is never a pass)", UNCONFIRMED_REASON = "test count could not be confirmed", FAIL_EVIDENCE, GO_FAIL_EVIDENCE, UNMEASURED_REASON = "executed count unmeasured (Loki could not parse the runner summary, harness-owned)", stripAnsi = (s) => s.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g, "").replace(/(?:\u001b\[|\u009b)[0-9;?]*[ -\/]*[@-~]/g, "").replace(/\r\n?/g, `
|
|
10165
10253
|
`), tailLines = (out2) => out2.split(`
|
|
10166
10254
|
`).filter((x) => x.trim()).slice(-12), GO_PKG_RE, isExecutedProof = (c) => c.result === "pass" && typeof c.n === "number" && c.n > 0, hasExecutedProof = (checks) => checks.some(isExecutedProof), LLM_JUDGES, NON_RUNNER, normPath = (p) => p.replace(/^(?:\.\/)+/, ""), hasExogenousProof = (checks, ctx) => checks.some((c) => isExecutedProof(c) && ((p) => p !== "agent_authored" && p !== "llm_judge")(checkProvenance(c.name ?? "", ctx))), SENSITIVE, API, TEST_CFG, RANK2, FILES_MEDIUM = 4, FILES_HIGH = 15, LINES_MEDIUM = 100, LINES_HIGH = 500, RAN_ALWAYS, PLAN, LOW_SKIPPED, deferredOf = (tier, selected, executed) => (tier === "low" ? LOW_SKIPPED : selected).filter((c) => !RAN_ALWAYS.includes(c) && !executed.includes(c)), EXCERPT_GROUP_CAP = 1500, EXCERPT_TOTAL_CAP = 6144;
|
|
@@ -10213,8 +10301,8 @@ var init_state = __esm(() => {
|
|
|
10213
10301
|
});
|
|
10214
10302
|
|
|
10215
10303
|
// ../../loki-ts/src/runner/repo_profile.ts
|
|
10216
|
-
import { existsSync as
|
|
10217
|
-
import { basename as basename12, join as join39, resolve as resolve13, isAbsolute as
|
|
10304
|
+
import { existsSync as existsSync24, readFileSync as readFileSync30, mkdirSync as mkdirSync14, readdirSync as readdirSync14, renameSync as renameSync9, rmSync as rmSync8, writeFileSync as writeFileSync17 } from "fs";
|
|
10305
|
+
import { basename as basename12, join as join39, resolve as resolve13, isAbsolute as isAbsolute4 } from "path";
|
|
10218
10306
|
function loadProfile(repoDir) {
|
|
10219
10307
|
try {
|
|
10220
10308
|
const p = JSON.parse(readFileSync30(commandProfilePath(repoDir), "utf8"));
|
|
@@ -10350,7 +10438,7 @@ var init_context = __esm(() => {
|
|
|
10350
10438
|
});
|
|
10351
10439
|
|
|
10352
10440
|
// ../../loki-ts/src/util/steer.ts
|
|
10353
|
-
import { appendFileSync as appendFileSync2, closeSync as closeSync8, constants as constants2, fstatSync as fstatSync6, readdirSync as readdirSync15, lstatSync as lstatSync9, mkdirSync as mkdirSync15, linkSync as linkSync2, openSync as openSync8, readFileSync as readFileSync31, readSync as readSync7, renameSync as renameSync10, statSync as statSync15, lutimesSync, rmSync as rmSync9, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18, existsSync as
|
|
10441
|
+
import { appendFileSync as appendFileSync2, closeSync as closeSync8, constants as constants2, fstatSync as fstatSync6, readdirSync as readdirSync15, lstatSync as lstatSync9, mkdirSync as mkdirSync15, linkSync as linkSync2, openSync as openSync8, readFileSync as readFileSync31, readSync as readSync7, renameSync as renameSync10, statSync as statSync15, lutimesSync, rmSync as rmSync9, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18, existsSync as existsSync25 } from "fs";
|
|
10354
10442
|
import { createHash as createHash9, randomBytes as randomBytes4 } from "crypto";
|
|
10355
10443
|
import { basename as basename13, join as join40 } from "path";
|
|
10356
10444
|
function archiveTo(logs, prefix, src, link = linkSync2, onLinked) {
|
|
@@ -10387,7 +10475,7 @@ function orphanAlreadyArchived(logs, claim) {
|
|
|
10387
10475
|
const s = statSync15(join40(logs, a));
|
|
10388
10476
|
if (s.ino !== c.ino || s.dev !== c.dev)
|
|
10389
10477
|
continue;
|
|
10390
|
-
if (/^human-input-(ORPHAN|REJECTED)-/.test(a) ||
|
|
10478
|
+
if (/^human-input-(ORPHAN|REJECTED)-/.test(a) || existsSync25(appliedMarker(logs, a)))
|
|
10391
10479
|
return true;
|
|
10392
10480
|
} catch {}
|
|
10393
10481
|
}
|
|
@@ -10689,12 +10777,12 @@ var init_seal_drift = __esm(() => {
|
|
|
10689
10777
|
});
|
|
10690
10778
|
|
|
10691
10779
|
// ../../loki-ts/src/util/spec_file.ts
|
|
10692
|
-
import { isAbsolute as
|
|
10780
|
+
import { isAbsolute as isAbsolute5, join as join41, relative as relative2 } from "path";
|
|
10693
10781
|
function specReceiptBlock(env) {
|
|
10694
10782
|
const { LOKI_E10_SPEC_PATH: p, LOKI_E10_SPEC_SHA256: sha2 } = env;
|
|
10695
10783
|
if (!p || !sha2)
|
|
10696
10784
|
return {};
|
|
10697
|
-
const rel =
|
|
10785
|
+
const rel = isAbsolute5(p) && env.LOKI_E10_REPO_DIR ? relative2(env.LOKI_E10_REPO_DIR, p) : p;
|
|
10698
10786
|
return { spec: { path: rel, sha256: sha2 } };
|
|
10699
10787
|
}
|
|
10700
10788
|
var SPEC_DIR;
|
|
@@ -10720,11 +10808,11 @@ var init_run_cap = __esm(() => {
|
|
|
10720
10808
|
});
|
|
10721
10809
|
|
|
10722
10810
|
// ../../loki-ts/src/util/mutation_proof.ts
|
|
10723
|
-
import { existsSync as
|
|
10811
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync16, mkdtempSync as mkdtempSync3, readFileSync as readFileSync32, rmSync as rmSync10, symlinkSync, writeFileSync as writeFileSync19 } from "fs";
|
|
10724
10812
|
import { tmpdir as tmpdir4 } from "os";
|
|
10725
|
-
import { basename as basename14, dirname as dirname13, isAbsolute as
|
|
10813
|
+
import { basename as basename14, dirname as dirname13, isAbsolute as isAbsolute6, join as join42, relative as relative3 } from "path";
|
|
10726
10814
|
function mutationProof(i) {
|
|
10727
|
-
const rels = i.wallFiles.map((f) =>
|
|
10815
|
+
const rels = i.wallFiles.map((f) => isAbsolute6(f.path) ? relative3(i.repoDir, f.path) : f.path);
|
|
10728
10816
|
if (rels.length === 0)
|
|
10729
10817
|
return np("no Wall tests");
|
|
10730
10818
|
const tests = rels.flatMap((rel) => {
|
|
@@ -10743,12 +10831,12 @@ function mutationProof(i) {
|
|
|
10743
10831
|
parent = mkdtempSync3(join42(tmpdir4(), "loki-mutproof-"));
|
|
10744
10832
|
wt = join42(parent, "wt");
|
|
10745
10833
|
const add3 = git(i.repoDir, ["worktree", "add", "--detach", wt, i.baseSha], Math.max(1000, Math.min(30000, remaining())));
|
|
10746
|
-
added =
|
|
10834
|
+
added = existsSync26(wt) || add3.status === 0;
|
|
10747
10835
|
if (add3.status !== 0)
|
|
10748
10836
|
return np("could not create the base worktree");
|
|
10749
10837
|
for (const rel of rels) {
|
|
10750
|
-
const sealed = join42(i.runDir, "wall", basename14(rel)), src =
|
|
10751
|
-
if (!
|
|
10838
|
+
const sealed = join42(i.runDir, "wall", basename14(rel)), src = existsSync26(sealed) ? sealed : join42(i.repoDir, rel);
|
|
10839
|
+
if (!existsSync26(src))
|
|
10752
10840
|
return np(`Wall file missing: ${rel}`);
|
|
10753
10841
|
mkdirSync16(dirname13(join42(wt, rel)), { recursive: true });
|
|
10754
10842
|
writeFileSync19(join42(wt, rel), readFileSync32(src));
|
|
@@ -10759,7 +10847,7 @@ function mutationProof(i) {
|
|
|
10759
10847
|
const anc = parts.slice(0, n2).join("/");
|
|
10760
10848
|
for (const d of DEP_DIRS) {
|
|
10761
10849
|
const src = join42(i.repoDir, anc, d), dst = join42(wt, anc, d);
|
|
10762
|
-
if (
|
|
10850
|
+
if (existsSync26(src) && !existsSync26(dst)) {
|
|
10763
10851
|
try {
|
|
10764
10852
|
symlinkSync(src, dst);
|
|
10765
10853
|
} catch {}
|
|
@@ -10902,14 +10990,14 @@ var init_shell = __esm(() => {
|
|
|
10902
10990
|
});
|
|
10903
10991
|
|
|
10904
10992
|
// ../../loki-ts/src/util/python.ts
|
|
10905
|
-
import { accessSync, constants as constants3, existsSync as
|
|
10993
|
+
import { accessSync, constants as constants3, existsSync as existsSync27, mkdtempSync as mkdtempSync4, readFileSync as readFileSync33, realpathSync as realpathSync18, rmSync as rmSync11, statSync as statSync16 } from "fs";
|
|
10906
10994
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
10907
10995
|
import { tmpdir as tmpdir5 } from "os";
|
|
10908
10996
|
async function findPython3() {
|
|
10909
10997
|
if (_pythonCache !== undefined)
|
|
10910
10998
|
return _pythonCache;
|
|
10911
10999
|
const homebrew = "/opt/homebrew/bin/python3.12";
|
|
10912
|
-
if (
|
|
11000
|
+
if (existsSync27(homebrew)) {
|
|
10913
11001
|
_pythonCache = homebrew;
|
|
10914
11002
|
return homebrew;
|
|
10915
11003
|
}
|
|
@@ -11352,7 +11440,7 @@ Example: test('name', () => { assert.strictEqual(fn(1), 2); }); Run: node --tes
|
|
|
11352
11440
|
|
|
11353
11441
|
// ../../loki-ts/src/util/holdout.ts
|
|
11354
11442
|
import { createHash as createHash12 } from "crypto";
|
|
11355
|
-
import { appendFileSync as appendFileSync3, copyFileSync as copyFileSync2, existsSync as
|
|
11443
|
+
import { appendFileSync as appendFileSync3, copyFileSync as copyFileSync2, existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync34, rmSync as rmSync12, writeFileSync as writeFileSync20 } from "fs";
|
|
11356
11444
|
import { tmpdir as tmpdir6 } from "os";
|
|
11357
11445
|
import { basename as basename16, join as join43, relative as relative4 } from "path";
|
|
11358
11446
|
function wallHoldoutRatio(env = process.env) {
|
|
@@ -11403,7 +11491,7 @@ function stageHoldout(dir, repoDir) {
|
|
|
11403
11491
|
mkdirSync17(man.targetDir, { recursive: true });
|
|
11404
11492
|
man.files.forEach((f, i) => {
|
|
11405
11493
|
const src = join43(dir, f.name);
|
|
11406
|
-
if (!
|
|
11494
|
+
if (!existsSync28(src) || sha2(readFileSync34(src, "utf8")) !== f.sha256) {
|
|
11407
11495
|
missing.push(`holdout ${i + 1} missing or altered`);
|
|
11408
11496
|
return;
|
|
11409
11497
|
}
|
|
@@ -12077,7 +12165,7 @@ var init_runner_errors = __esm(() => {
|
|
|
12077
12165
|
|
|
12078
12166
|
// ../../loki-ts/src/runner/load_owner.ts
|
|
12079
12167
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
12080
|
-
import { mkdtempSync as mkdtempSync6, realpathSync as
|
|
12168
|
+
import { mkdtempSync as mkdtempSync6, realpathSync as realpathSync19, rmSync as rmSync14 } from "fs";
|
|
12081
12169
|
import { tmpdir as tmpdir8 } from "os";
|
|
12082
12170
|
import { basename as basename18, extname as extname2, join as join46 } from "path";
|
|
12083
12171
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
@@ -12099,7 +12187,7 @@ function loadSignature(out2, roots) {
|
|
|
12099
12187
|
let o = out2;
|
|
12100
12188
|
for (const r of roots.flatMap((x) => {
|
|
12101
12189
|
try {
|
|
12102
|
-
return [x,
|
|
12190
|
+
return [x, realpathSync19(x)];
|
|
12103
12191
|
} catch {
|
|
12104
12192
|
return [x];
|
|
12105
12193
|
}
|
|
@@ -12225,7 +12313,7 @@ var init_load_owner = __esm(() => {
|
|
|
12225
12313
|
});
|
|
12226
12314
|
|
|
12227
12315
|
// ../../loki-ts/src/util/wall_owned.ts
|
|
12228
|
-
import { isAbsolute as
|
|
12316
|
+
import { isAbsolute as isAbsolute7, join as join47, relative as relative5 } from "path";
|
|
12229
12317
|
function failingLocations(raw2, cwd, repoDir) {
|
|
12230
12318
|
const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
|
|
12231
12319
|
`), files = new Set;
|
|
@@ -12257,7 +12345,7 @@ function failingLocations(raw2, cwd, repoDir) {
|
|
|
12257
12345
|
}
|
|
12258
12346
|
if (errors2 === 0 || located !== errors2)
|
|
12259
12347
|
return null;
|
|
12260
|
-
return [...files].map((f) => relative5(repoDir,
|
|
12348
|
+
return [...files].map((f) => relative5(repoDir, isAbsolute7(f) ? f : join47(cwd, f)));
|
|
12261
12349
|
}
|
|
12262
12350
|
function wallOwnedFailure(raw2, cwd, repoDir, wall) {
|
|
12263
12351
|
const locs = failingLocations(raw2, cwd, repoDir);
|
|
@@ -12270,7 +12358,7 @@ var init_wall_owned = __esm(() => {
|
|
|
12270
12358
|
|
|
12271
12359
|
// ../../loki-ts/src/util/app_probe.ts
|
|
12272
12360
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12273
|
-
import { existsSync as
|
|
12361
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync37, renameSync as renameSync11, writeFileSync as writeFileSync21 } from "fs";
|
|
12274
12362
|
import { join as join48 } from "path";
|
|
12275
12363
|
function readJson4(p) {
|
|
12276
12364
|
try {
|
|
@@ -12282,7 +12370,7 @@ function readJson4(p) {
|
|
|
12282
12370
|
}
|
|
12283
12371
|
function stopApp(repoDir, opts = {}) {
|
|
12284
12372
|
const runner = opts.runnerPath ?? join48(REPO_ROOT, "autonomy", "app-runner.sh");
|
|
12285
|
-
if (!
|
|
12373
|
+
if (!existsSync29(runner) || !existsSync29(join48(repoDir, ".loki", "app-runner", "app.pid")))
|
|
12286
12374
|
return;
|
|
12287
12375
|
spawnSync4("bash", ["-c", STOP, "app-probe", repoDir, runner], { cwd: repoDir, env: plainTestEnv(), timeout: opts.timeoutMs ?? 60000, killSignal: "SIGKILL", stdio: "ignore" });
|
|
12288
12376
|
}
|
|
@@ -12296,7 +12384,7 @@ async function appProbe(repoDir, lokiDir2, opts = {}) {
|
|
|
12296
12384
|
return { out: r.stdout ?? "", status: r.error ? null : r.status };
|
|
12297
12385
|
};
|
|
12298
12386
|
let pid = null, url = null;
|
|
12299
|
-
if (!
|
|
12387
|
+
if (!existsSync29(runner))
|
|
12300
12388
|
return { checks: [mk2("app boot", "not_run", "app-runner.sh not found"), mk2("http probe", "not_run", "app not booted")], pid, url };
|
|
12301
12389
|
try {
|
|
12302
12390
|
const b = sh(BOOT);
|
|
@@ -12306,7 +12394,7 @@ async function appProbe(repoDir, lokiDir2, opts = {}) {
|
|
|
12306
12394
|
return { checks, pid, url };
|
|
12307
12395
|
}
|
|
12308
12396
|
const ir = Number(m[1]), sr = Number(m[2]), hr = Number(m[3]);
|
|
12309
|
-
const pidTxt =
|
|
12397
|
+
const pidTxt = existsSync29(join48(dir, "app.pid")) ? readFileSync37(join48(dir, "app.pid"), "utf8").trim() : "";
|
|
12310
12398
|
pid = /^\d+$/.test(pidTxt) && sr >= 0 ? Number(pidTxt) : null;
|
|
12311
12399
|
const st = readJson4(join48(dir, "state.json"));
|
|
12312
12400
|
url = typeof st?.["url"] === "string" ? st["url"] : null;
|
|
@@ -12333,7 +12421,7 @@ async function functionalVerify(opts, url, dir, lokiDir2, timeoutMs) {
|
|
|
12333
12421
|
const t0 = Date.now();
|
|
12334
12422
|
const res = (result, reason) => ({ name: "functional verify", cmd: "autonomy/lib/functional-verify.py", result, duration_s: (Date.now() - t0) / 1000, reason });
|
|
12335
12423
|
const py = await findPython3(), script = opts.verifyPath ?? join48(REPO_ROOT, "autonomy", "lib", "functional-verify.py");
|
|
12336
|
-
if (!py || !
|
|
12424
|
+
if (!py || !existsSync29(script))
|
|
12337
12425
|
return res("not_run", "python3 or functional-verify.py unavailable");
|
|
12338
12426
|
const specFile = join48(dir, "spec.txt");
|
|
12339
12427
|
writeFileSync21(specFile, opts.specText ?? "");
|
|
@@ -12381,14 +12469,15 @@ app_runner_stop >/dev/null 2>&1
|
|
|
12381
12469
|
});
|
|
12382
12470
|
|
|
12383
12471
|
// ../../loki-ts/src/util/secure_scan.ts
|
|
12384
|
-
import { existsSync as
|
|
12385
|
-
import {
|
|
12472
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync38, realpathSync as realpathSync20, rmSync as rmSync15, statSync as statSync17, writeFileSync as writeFileSync22 } from "fs";
|
|
12473
|
+
import { tmpdir as tmpdir9 } from "os";
|
|
12474
|
+
import { dirname as dirname16, join as join49, resolve as resolve14, sep as sep11 } from "path";
|
|
12386
12475
|
function fail(error) {
|
|
12387
12476
|
return { ok: false, findings: [], error };
|
|
12388
12477
|
}
|
|
12389
12478
|
async function runSecureScan(targetDir, opts = {}) {
|
|
12390
12479
|
const script = opts.scriptPath ?? join49(REPO_ROOT, "autonomy", "lib", "secure-scan.py");
|
|
12391
|
-
if (!
|
|
12480
|
+
if (!existsSync30(script))
|
|
12392
12481
|
return fail(`secure-scan script not found: ${script}`);
|
|
12393
12482
|
const py = await findPython3();
|
|
12394
12483
|
if (!py)
|
|
@@ -12406,9 +12495,15 @@ async function runSecureScan(targetDir, opts = {}) {
|
|
|
12406
12495
|
const obj = parsed && typeof parsed === "object" ? parsed : undefined;
|
|
12407
12496
|
if ((res.exitCode === 0 || res.exitCode === 1) && obj && Array.isArray(obj.findings)) {
|
|
12408
12497
|
const findings = obj.findings;
|
|
12409
|
-
if (res.exitCode === 1
|
|
12498
|
+
if (res.exitCode === 1 !== findings.length > 0)
|
|
12499
|
+
return fail(`secure-scan exit ${res.exitCode} disagrees with ${findings.length} findings`);
|
|
12500
|
+
if (opts.secretsEverywhere || opts.diffBase === false)
|
|
12410
12501
|
return { ok: true, findings };
|
|
12411
|
-
|
|
12502
|
+
const added = await scanAddedLines(targetDir, { ...opts, scriptPath: script, timeoutMs });
|
|
12503
|
+
if (!added.ok)
|
|
12504
|
+
return added;
|
|
12505
|
+
const seen = new Set(findings.map((f) => `${f.file}|${f.line}`));
|
|
12506
|
+
return { ok: true, findings: [...findings, ...added.findings.filter((f) => !seen.has(`${f.file}|${f.line}`))] };
|
|
12412
12507
|
}
|
|
12413
12508
|
if (res.exitCode !== 0 && res.exitCode !== 1 && res.exitCode !== 2 && elapsed >= timeoutMs) {
|
|
12414
12509
|
return fail(`secure-scan timed out after ${timeoutMs}ms`);
|
|
@@ -12419,10 +12514,194 @@ async function runSecureScan(targetDir, opts = {}) {
|
|
|
12419
12514
|
`).slice(-3).join(" | ");
|
|
12420
12515
|
return fail(`secure-scan failed (exit ${res.exitCode}): ${tail}`);
|
|
12421
12516
|
}
|
|
12422
|
-
|
|
12517
|
+
function git3(repoDir, args) {
|
|
12518
|
+
const r = safeGitSpawn(repoDir, args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
|
12519
|
+
return r.status === 0 && typeof r.stdout === "string" ? r.stdout : null;
|
|
12520
|
+
}
|
|
12521
|
+
function isWorkTreeRoot(dir) {
|
|
12522
|
+
const top = git3(dir, ["rev-parse", "--show-toplevel"]);
|
|
12523
|
+
if (top === null)
|
|
12524
|
+
return false;
|
|
12525
|
+
try {
|
|
12526
|
+
return realpathSync20(top.trim()) === realpathSync20(dir);
|
|
12527
|
+
} catch {
|
|
12528
|
+
return false;
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
function resolveDiffBase(repoDir, explicit) {
|
|
12532
|
+
const want = explicit ?? process.env["LOKI_SECURE_SCAN_BASE"];
|
|
12533
|
+
if (want)
|
|
12534
|
+
return git3(repoDir, ["rev-parse", "--verify", "--quiet", `${want}^{commit}`])?.trim() || null;
|
|
12535
|
+
for (const ref of ["refs/remotes/origin/HEAD", "@{upstream}"]) {
|
|
12536
|
+
const mb = git3(repoDir, ["merge-base", "HEAD", ref])?.trim();
|
|
12537
|
+
if (mb)
|
|
12538
|
+
return mb;
|
|
12539
|
+
}
|
|
12540
|
+
return git3(repoDir, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])?.trim() || null;
|
|
12541
|
+
}
|
|
12542
|
+
function parseDiffHeaderPath(header) {
|
|
12543
|
+
let p = header;
|
|
12544
|
+
if (p.startsWith('"')) {
|
|
12545
|
+
const bytes = [];
|
|
12546
|
+
let i = 1;
|
|
12547
|
+
let closed = false;
|
|
12548
|
+
while (i < p.length) {
|
|
12549
|
+
const c = String.fromCodePoint(p.codePointAt(i));
|
|
12550
|
+
if (c === '"') {
|
|
12551
|
+
closed = true;
|
|
12552
|
+
break;
|
|
12553
|
+
}
|
|
12554
|
+
if (c !== "\\") {
|
|
12555
|
+
for (const b of Buffer.from(c, "utf8"))
|
|
12556
|
+
bytes.push(b);
|
|
12557
|
+
i += c.length;
|
|
12558
|
+
continue;
|
|
12559
|
+
}
|
|
12560
|
+
const e = p[i + 1] ?? "";
|
|
12561
|
+
const oct = /^[0-7]{3}/.exec(p.slice(i + 1, i + 4));
|
|
12562
|
+
if (oct) {
|
|
12563
|
+
bytes.push(parseInt(oct[0], 8));
|
|
12564
|
+
i += 4;
|
|
12565
|
+
continue;
|
|
12566
|
+
}
|
|
12567
|
+
const esc2 = { a: 7, b: 8, t: 9, n: 10, v: 11, f: 12, r: 13, '"': 34, "\\": 92 };
|
|
12568
|
+
if (!(e in esc2))
|
|
12569
|
+
return null;
|
|
12570
|
+
bytes.push(esc2[e]);
|
|
12571
|
+
i += 2;
|
|
12572
|
+
}
|
|
12573
|
+
if (!closed)
|
|
12574
|
+
return null;
|
|
12575
|
+
p = Buffer.from(bytes).toString("utf8");
|
|
12576
|
+
} else if (p.endsWith("\t")) {
|
|
12577
|
+
p = p.slice(0, -1);
|
|
12578
|
+
}
|
|
12579
|
+
return p.startsWith("b/") && p.length > 2 ? p.slice(2) : null;
|
|
12580
|
+
}
|
|
12581
|
+
function parseAddedLines(diff) {
|
|
12582
|
+
const out2 = new Map;
|
|
12583
|
+
let cur = null;
|
|
12584
|
+
let next = 0;
|
|
12585
|
+
let oldLeft = 0;
|
|
12586
|
+
let newLeft = 0;
|
|
12587
|
+
for (const line2 of diff.split(`
|
|
12588
|
+
`)) {
|
|
12589
|
+
if (oldLeft > 0 || newLeft > 0) {
|
|
12590
|
+
if (line2.startsWith("\\"))
|
|
12591
|
+
continue;
|
|
12592
|
+
if (line2.startsWith("+")) {
|
|
12593
|
+
if (cur)
|
|
12594
|
+
cur.set(next, line2.slice(1));
|
|
12595
|
+
next++;
|
|
12596
|
+
newLeft--;
|
|
12597
|
+
} else if (line2.startsWith("-")) {
|
|
12598
|
+
oldLeft--;
|
|
12599
|
+
} else {
|
|
12600
|
+
next++;
|
|
12601
|
+
oldLeft--;
|
|
12602
|
+
newLeft--;
|
|
12603
|
+
}
|
|
12604
|
+
continue;
|
|
12605
|
+
}
|
|
12606
|
+
if (line2.startsWith("diff --git ")) {
|
|
12607
|
+
cur = null;
|
|
12608
|
+
continue;
|
|
12609
|
+
}
|
|
12610
|
+
if (line2.startsWith("+++ ")) {
|
|
12611
|
+
const p = parseDiffHeaderPath(line2.slice(4));
|
|
12612
|
+
cur = p === null ? null : out2.get(p) ?? new Map;
|
|
12613
|
+
if (p !== null && cur)
|
|
12614
|
+
out2.set(p, cur);
|
|
12615
|
+
continue;
|
|
12616
|
+
}
|
|
12617
|
+
const m = /^@@ -\d+(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(line2);
|
|
12618
|
+
if (m) {
|
|
12619
|
+
oldLeft = m[1] === undefined ? 1 : Number(m[1]);
|
|
12620
|
+
next = Number(m[2]);
|
|
12621
|
+
newLeft = m[3] === undefined ? 1 : Number(m[3]);
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
return out2;
|
|
12625
|
+
}
|
|
12626
|
+
async function scanAddedLines(repoDir, opts = {}) {
|
|
12627
|
+
if (opts.diffBase === false || !isWorkTreeRoot(repoDir))
|
|
12628
|
+
return { ok: true, findings: [] };
|
|
12629
|
+
const base = resolveDiffBase(repoDir, opts.diffBase);
|
|
12630
|
+
const added = new Map;
|
|
12631
|
+
if (base) {
|
|
12632
|
+
const diff = git3(repoDir, ["-c", "core.quotePath=false", "diff", "--src-prefix=a/", "--dst-prefix=b/", "-U0", "--no-color", "--no-ext-diff", "--no-renames", base, "--"]);
|
|
12633
|
+
if (diff === null)
|
|
12634
|
+
return fail(`secure-scan: git diff against ${base.slice(0, 12)} failed`);
|
|
12635
|
+
for (const [p, lines2] of parseAddedLines(diff))
|
|
12636
|
+
added.set(p, lines2);
|
|
12637
|
+
}
|
|
12638
|
+
const untracked = git3(repoDir, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
12639
|
+
if (untracked === null)
|
|
12640
|
+
return fail("secure-scan: git ls-files failed");
|
|
12641
|
+
for (const p of untracked.split("\x00").filter(Boolean)) {
|
|
12642
|
+
try {
|
|
12643
|
+
const st = statSync17(join49(repoDir, p));
|
|
12644
|
+
if (!st.isFile() || st.size > MAX_ADDED_FILE_BYTES)
|
|
12645
|
+
continue;
|
|
12646
|
+
const lines2 = new Map;
|
|
12647
|
+
readFileSync38(join49(repoDir, p), "utf8").split(`
|
|
12648
|
+
`).forEach((t, i) => lines2.set(i + 1, t));
|
|
12649
|
+
added.set(p, lines2);
|
|
12650
|
+
} catch {}
|
|
12651
|
+
}
|
|
12652
|
+
if (added.size === 0)
|
|
12653
|
+
return { ok: true, findings: [] };
|
|
12654
|
+
const mirror = mkdtempSync7(join49(tmpdir9(), "loki-secure-added-"));
|
|
12655
|
+
try {
|
|
12656
|
+
const root = resolve14(mirror) + sep11;
|
|
12657
|
+
for (const [p, lines2] of added) {
|
|
12658
|
+
if (lines2.size === 0)
|
|
12659
|
+
continue;
|
|
12660
|
+
const dest = resolve14(mirror, p);
|
|
12661
|
+
if (!dest.startsWith(root))
|
|
12662
|
+
continue;
|
|
12663
|
+
const max = Math.max(...lines2.keys());
|
|
12664
|
+
const body = [];
|
|
12665
|
+
for (let i = 1;i <= max; i++)
|
|
12666
|
+
body.push(lines2.get(i) ?? "");
|
|
12667
|
+
mkdirSync19(dirname16(dest), { recursive: true });
|
|
12668
|
+
writeFileSync22(dest, body.join(`
|
|
12669
|
+
`) + `
|
|
12670
|
+
`);
|
|
12671
|
+
}
|
|
12672
|
+
const r = await runSecureScan(mirror, { scriptPath: opts.scriptPath, timeoutMs: opts.timeoutMs, secretsEverywhere: true });
|
|
12673
|
+
if (!r.ok)
|
|
12674
|
+
return r;
|
|
12675
|
+
const byLine = new Map;
|
|
12676
|
+
for (const f of r.findings) {
|
|
12677
|
+
if (f.rule !== "secret-in-client-file")
|
|
12678
|
+
continue;
|
|
12679
|
+
const kind = /A live (\S+)/.exec(f.message)?.[1] ?? "provider key";
|
|
12680
|
+
const k = `${f.file}|${f.line}`;
|
|
12681
|
+
const prev = byLine.get(k);
|
|
12682
|
+
if (!prev || prev.kind === "openai-key" && kind !== "openai-key")
|
|
12683
|
+
byLine.set(k, { f, kind });
|
|
12684
|
+
}
|
|
12685
|
+
const findings = [...byLine.values()].map(({ f, kind }) => {
|
|
12686
|
+
return {
|
|
12687
|
+
rule: ADDED_LINE_RULE,
|
|
12688
|
+
file: f.file,
|
|
12689
|
+
line: f.line,
|
|
12690
|
+
severity: "HIGH",
|
|
12691
|
+
message: `A ${kind} was added at ${f.file}:${f.line}; the value is withheld here.`,
|
|
12692
|
+
fix: "Remove the key from the change, rotate it (assume it is compromised), and read it at runtime from an environment variable or secret manager."
|
|
12693
|
+
};
|
|
12694
|
+
});
|
|
12695
|
+
return { ok: true, findings };
|
|
12696
|
+
} finally {
|
|
12697
|
+
rmSync15(mirror, { recursive: true, force: true });
|
|
12698
|
+
}
|
|
12699
|
+
}
|
|
12700
|
+
var ADDED_LINE_RULE = "provider-key-in-added-line", MAX_ADDED_FILE_BYTES = 1e6, DEFAULT_SECURE_SCAN_TIMEOUT_MS = 120000;
|
|
12423
12701
|
var init_secure_scan = __esm(() => {
|
|
12424
12702
|
init_paths();
|
|
12425
12703
|
init_python();
|
|
12704
|
+
init_safe_git();
|
|
12426
12705
|
init_shell();
|
|
12427
12706
|
});
|
|
12428
12707
|
|
|
@@ -12532,9 +12811,9 @@ var init_route_block = __esm(() => {
|
|
|
12532
12811
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
12533
12812
|
import { spawn as spawn7 } from "child_process";
|
|
12534
12813
|
import { createHash as createHash13, randomBytes as randomBytes5 } from "crypto";
|
|
12535
|
-
import { existsSync as
|
|
12814
|
+
import { existsSync as existsSync31, lstatSync as lstatSync10, mkdirSync as mkdirSync20, readdirSync as readdirSync16, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
|
|
12536
12815
|
import { createServer } from "net";
|
|
12537
|
-
import { dirname as
|
|
12816
|
+
import { dirname as dirname17, isAbsolute as isAbsolute8, join as join50, normalize as normalize3, relative as relative6, sep as sep12 } from "path";
|
|
12538
12817
|
function routeFor(file) {
|
|
12539
12818
|
let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
|
|
12540
12819
|
r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
|
|
@@ -12555,16 +12834,16 @@ function checkScreens(runDir, screens) {
|
|
|
12555
12834
|
for (const s of screens) {
|
|
12556
12835
|
if (typeof s?.path !== "string" || typeof s.sha256 !== "string")
|
|
12557
12836
|
return "evidence_screens entry is malformed";
|
|
12558
|
-
if (
|
|
12837
|
+
if (isAbsolute8(s.path) || normalize3(s.path).startsWith(".."))
|
|
12559
12838
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
12560
12839
|
const file = join50(runDir, s.path);
|
|
12561
12840
|
let cur = runDir;
|
|
12562
|
-
for (const seg of normalize3(s.path).split(
|
|
12841
|
+
for (const seg of normalize3(s.path).split(sep12)) {
|
|
12563
12842
|
cur = join50(cur, seg);
|
|
12564
12843
|
if (isSymlink(cur))
|
|
12565
12844
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
12566
12845
|
}
|
|
12567
|
-
if (!
|
|
12846
|
+
if (!existsSync31(file))
|
|
12568
12847
|
return `evidence screenshot is missing: ${s.path}`;
|
|
12569
12848
|
if (!isRegularFile(file))
|
|
12570
12849
|
return `evidence screenshot is not a regular file: ${s.path}`;
|
|
@@ -12608,10 +12887,10 @@ async function waitUp(url, ms, signal) {
|
|
|
12608
12887
|
function openapiPaths(repoDir) {
|
|
12609
12888
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
12610
12889
|
const p = join50(repoDir, f);
|
|
12611
|
-
if (!
|
|
12890
|
+
if (!existsSync31(p))
|
|
12612
12891
|
continue;
|
|
12613
12892
|
try {
|
|
12614
|
-
const txt =
|
|
12893
|
+
const txt = readFileSync39(p, "utf8");
|
|
12615
12894
|
const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
|
|
12616
12895
|
return paths.filter((x) => !x.includes("{")).slice(0, 10);
|
|
12617
12896
|
} catch {
|
|
@@ -12626,9 +12905,9 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
12626
12905
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
12627
12906
|
try {
|
|
12628
12907
|
const dir = join50(runDir, relDir, "e2e");
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12908
|
+
mkdirSync20(dir, { recursive: true });
|
|
12909
|
+
writeFileSync23(join50(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
12910
|
+
writeFileSync23(join50(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
12632
12911
|
`);
|
|
12633
12912
|
if (!await shoot(pw, ["test", "--config", join50(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
12634
12913
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
@@ -12725,18 +13004,18 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12725
13004
|
const pkgPath = join50(appDir, "package.json");
|
|
12726
13005
|
let script = null;
|
|
12727
13006
|
if (!boot) {
|
|
12728
|
-
if (!
|
|
12729
|
-
const subs = ["backend", "frontend"].filter((d) =>
|
|
13007
|
+
if (!existsSync31(pkgPath)) {
|
|
13008
|
+
const subs = ["backend", "frontend"].filter((d) => existsSync31(join50(repoDir, d, "package.json")));
|
|
12730
13009
|
const has = (d) => {
|
|
12731
13010
|
try {
|
|
12732
|
-
return pickScript(JSON.parse(
|
|
13011
|
+
return pickScript(JSON.parse(readFileSync39(join50(repoDir, d, "package.json"), "utf8"))) !== null;
|
|
12733
13012
|
} catch {
|
|
12734
13013
|
return false;
|
|
12735
13014
|
}
|
|
12736
13015
|
};
|
|
12737
13016
|
return skip(noRootReason(subs, subs.filter((d) => !has(d))));
|
|
12738
13017
|
}
|
|
12739
|
-
script = pickScript(JSON.parse(
|
|
13018
|
+
script = pickScript(JSON.parse(readFileSync39(pkgPath, "utf8")));
|
|
12740
13019
|
if (!script)
|
|
12741
13020
|
return skip("no dev, preview or start script");
|
|
12742
13021
|
}
|
|
@@ -12766,17 +13045,17 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12766
13045
|
const evRoot = join50(runDir, "evidence");
|
|
12767
13046
|
if (isSymlink(evRoot))
|
|
12768
13047
|
return skip("evidence dir is a symlink");
|
|
12769
|
-
|
|
13048
|
+
mkdirSync20(evRoot, { recursive: true });
|
|
12770
13049
|
if (pages.length > 0) {
|
|
12771
13050
|
const rel0 = join50("evidence", randomBytes5(6).toString("hex"));
|
|
12772
|
-
|
|
13051
|
+
mkdirSync20(join50(runDir, rel0));
|
|
12773
13052
|
const rels = [];
|
|
12774
13053
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
12775
13054
|
if (aborted() || left() <= 0)
|
|
12776
13055
|
break;
|
|
12777
13056
|
const rel = join50(rel0, `${screenName(route)}.png`);
|
|
12778
13057
|
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join50(runDir, rel)], appDir, left(), signal);
|
|
12779
|
-
if (ok && !aborted() && !isSymlink(join50(runDir, rel)) &&
|
|
13058
|
+
if (ok && !aborted() && !isSymlink(join50(runDir, rel)) && existsSync31(join50(runDir, rel)))
|
|
12780
13059
|
rels.push(rel);
|
|
12781
13060
|
}
|
|
12782
13061
|
if (aborted())
|
|
@@ -12802,8 +13081,8 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12802
13081
|
if (aborted())
|
|
12803
13082
|
return skip("aborted");
|
|
12804
13083
|
const hdir = join50(evRoot, randomBytes5(6).toString("hex"));
|
|
12805
|
-
|
|
12806
|
-
|
|
13084
|
+
mkdirSync20(hdir);
|
|
13085
|
+
writeFileSync23(join50(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
12807
13086
|
`);
|
|
12808
13087
|
return { screens: [], http: true, skipped: null };
|
|
12809
13088
|
} catch (e) {
|
|
@@ -12830,14 +13109,14 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
12830
13109
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
12831
13110
|
if (visualGateEnabled() && typeof receipt["evidence_failed"] === "string")
|
|
12832
13111
|
return `visual evidence failed (LOKI_VISUAL_GATE=1): ${receipt["evidence_failed"]}`;
|
|
12833
|
-
return receipt["evidence_screens"] === undefined ? null : checkScreens(
|
|
13112
|
+
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname17(receiptPath), receipt["evidence_screens"]);
|
|
12834
13113
|
}
|
|
12835
|
-
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", visualGateEnabled = (env = process.env) => env["LOKI_VISUAL_GATE"] === "1", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash13("sha256").update(
|
|
13114
|
+
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", visualGateEnabled = (env = process.env) => env["LOKI_VISUAL_GATE"] === "1", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash13("sha256").update(readFileSync39(p)).digest("hex"), playwrightBin = (repoDir) => {
|
|
12836
13115
|
const bin = join50(repoDir, "node_modules", ".bin", "playwright");
|
|
12837
|
-
return
|
|
13116
|
+
return existsSync31(bin) ? bin : null;
|
|
12838
13117
|
}, playwrightTestPkg = (repoDir) => {
|
|
12839
13118
|
const d = join50(repoDir, "node_modules", "@playwright", "test");
|
|
12840
|
-
return
|
|
13119
|
+
return existsSync31(join50(d, "package.json")) ? d : null;
|
|
12841
13120
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
12842
13121
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
12843
13122
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
@@ -12902,15 +13181,15 @@ var init_pr = __esm(() => {
|
|
|
12902
13181
|
});
|
|
12903
13182
|
|
|
12904
13183
|
// ../../loki-ts/src/util/pr_lessons.ts
|
|
12905
|
-
import { existsSync as
|
|
12906
|
-
import { dirname as
|
|
13184
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync8, readFileSync as readFileSync40, rmSync as rmSync16, writeFileSync as writeFileSync24 } from "fs";
|
|
13185
|
+
import { dirname as dirname18, join as join52 } from "path";
|
|
12907
13186
|
import { createHash as createHash14 } from "crypto";
|
|
12908
13187
|
function loadLessons(repoDir) {
|
|
12909
13188
|
const p = lessonsPath(repoDir);
|
|
12910
|
-
if (!
|
|
13189
|
+
if (!existsSync32(p))
|
|
12911
13190
|
return [];
|
|
12912
13191
|
try {
|
|
12913
|
-
const j = JSON.parse(
|
|
13192
|
+
const j = JSON.parse(readFileSync40(p, "utf-8"));
|
|
12914
13193
|
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
12915
13194
|
} catch {
|
|
12916
13195
|
return [];
|
|
@@ -12918,7 +13197,7 @@ function loadLessons(repoDir) {
|
|
|
12918
13197
|
}
|
|
12919
13198
|
function save(repoDir, lessons) {
|
|
12920
13199
|
const p = lessonsPath(repoDir);
|
|
12921
|
-
|
|
13200
|
+
mkdirSync21(dirname18(p), { recursive: true });
|
|
12922
13201
|
atomicWriteJson(p, { version: 2, lessons });
|
|
12923
13202
|
}
|
|
12924
13203
|
function appendReceiptFact(repoDir, f, now2 = () => new Date) {
|
|
@@ -12984,8 +13263,8 @@ var init_attempts = __esm(() => {
|
|
|
12984
13263
|
var init_mcp_config = () => {};
|
|
12985
13264
|
|
|
12986
13265
|
// ../../loki-ts/src/providers/claude_flags.ts
|
|
12987
|
-
import { existsSync as
|
|
12988
|
-
import { resolve as resolvePath, dirname as
|
|
13266
|
+
import { existsSync as existsSync33, readFileSync as readFileSync41 } from "fs";
|
|
13267
|
+
import { resolve as resolvePath, dirname as dirname19 } from "path";
|
|
12989
13268
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12990
13269
|
function claudeFlagSupported(flag, helpText) {
|
|
12991
13270
|
const txt = helpText ?? _claudeHelpCache;
|
|
@@ -13032,14 +13311,14 @@ function leanReviewerFlags(opts = {}) {
|
|
|
13032
13311
|
}
|
|
13033
13312
|
function designArchetypesText() {
|
|
13034
13313
|
try {
|
|
13035
|
-
const moduleDir =
|
|
13314
|
+
const moduleDir = dirname19(fileURLToPath4(import.meta.url));
|
|
13036
13315
|
for (const rel of [
|
|
13037
13316
|
["..", "..", "..", "references", "design-archetypes.md"],
|
|
13038
13317
|
["..", "..", "references", "design-archetypes.md"]
|
|
13039
13318
|
]) {
|
|
13040
13319
|
const p = resolvePath(moduleDir, ...rel);
|
|
13041
|
-
if (
|
|
13042
|
-
return
|
|
13320
|
+
if (existsSync33(p))
|
|
13321
|
+
return readFileSync41(p, "utf8");
|
|
13043
13322
|
}
|
|
13044
13323
|
} catch {}
|
|
13045
13324
|
return "";
|
|
@@ -13123,14 +13402,14 @@ var init_providers = __esm(() => {
|
|
|
13123
13402
|
});
|
|
13124
13403
|
|
|
13125
13404
|
// ../../loki-ts/src/runner/router/route_record.ts
|
|
13126
|
-
import { existsSync as
|
|
13405
|
+
import { existsSync as existsSync34, lstatSync as lstatSync11, mkdirSync as mkdirSync22, readFileSync as readFileSync42, writeFileSync as writeFileSync25 } from "fs";
|
|
13127
13406
|
import { join as join53 } from "path";
|
|
13128
13407
|
function loadRouteRecord(runDir) {
|
|
13129
13408
|
try {
|
|
13130
13409
|
const p = join53(runDir, ROUTE_RECORD_FILE);
|
|
13131
|
-
if (!
|
|
13410
|
+
if (!existsSync34(p) || !lstatSync11(p).isFile())
|
|
13132
13411
|
return;
|
|
13133
|
-
const v = JSON.parse(
|
|
13412
|
+
const v = JSON.parse(readFileSync42(p, "utf8"));
|
|
13134
13413
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
13135
13414
|
} catch {
|
|
13136
13415
|
return;
|
|
@@ -13313,14 +13592,14 @@ __export(exports_deep, {
|
|
|
13313
13592
|
DEFAULT_SECRET_SCAN_SH: () => DEFAULT_SECRET_SCAN_SH,
|
|
13314
13593
|
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH2
|
|
13315
13594
|
});
|
|
13316
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
13317
|
-
import { tmpdir as
|
|
13318
|
-
import { dirname as
|
|
13595
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync9, rmSync as rmSync17, writeFileSync as writeFileSync26 } from "fs";
|
|
13596
|
+
import { tmpdir as tmpdir10 } from "os";
|
|
13597
|
+
import { dirname as dirname20, join as join55 } from "path";
|
|
13319
13598
|
function sessionCouncil(sessions) {
|
|
13320
13599
|
return { async run(input, signal) {
|
|
13321
|
-
const cwd =
|
|
13600
|
+
const cwd = mkdtempSync9(join55(tmpdir10(), "loki-deep-council-"));
|
|
13322
13601
|
try {
|
|
13323
|
-
|
|
13602
|
+
writeFileSync26(join55(cwd, "diff.patch"), input.diff, "utf8");
|
|
13324
13603
|
const r = await sessions.run({
|
|
13325
13604
|
stage: "deep",
|
|
13326
13605
|
tier: "development",
|
|
@@ -13342,7 +13621,7 @@ function sessionCouncil(sessions) {
|
|
|
13342
13621
|
throw new Error("reviewer reply has no LGTM or FINDING: line");
|
|
13343
13622
|
return { findings };
|
|
13344
13623
|
} finally {
|
|
13345
|
-
|
|
13624
|
+
rmSync17(cwd, { recursive: true, force: true });
|
|
13346
13625
|
}
|
|
13347
13626
|
} };
|
|
13348
13627
|
}
|
|
@@ -13432,21 +13711,21 @@ async function runCouncilCheck(ctx, changed, baseSha, signal, opts, checks, notP
|
|
|
13432
13711
|
}
|
|
13433
13712
|
}
|
|
13434
13713
|
async function secureScanChanged(repoDir, changed, opts) {
|
|
13435
|
-
const dir =
|
|
13714
|
+
const dir = mkdtempSync9(join55(tmpdir10(), "loki-deep-secure-"));
|
|
13436
13715
|
try {
|
|
13437
13716
|
for (const f of changed)
|
|
13438
|
-
if (
|
|
13439
|
-
|
|
13717
|
+
if (existsSync35(join55(repoDir, f)) && lstatSync12(join55(repoDir, f)).isFile()) {
|
|
13718
|
+
mkdirSync23(dirname20(join55(dir, f)), { recursive: true });
|
|
13440
13719
|
copyFileSync3(join55(repoDir, f), join55(dir, f));
|
|
13441
13720
|
}
|
|
13442
13721
|
return await runSecureScan(dir, { ...opts.secureScanPath ? { scriptPath: opts.secureScanPath } : {}, timeoutMs: 60000 });
|
|
13443
13722
|
} finally {
|
|
13444
|
-
|
|
13723
|
+
rmSync17(dir, { recursive: true, force: true });
|
|
13445
13724
|
}
|
|
13446
13725
|
}
|
|
13447
13726
|
async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
13448
13727
|
const scriptPath = opts.secretScanShPath ?? DEFAULT_SECRET_SCAN_SH, name = "security scan", cmd = "secret-scan.sh + secure-scan.py";
|
|
13449
|
-
if (!
|
|
13728
|
+
if (!existsSync35(scriptPath)) {
|
|
13450
13729
|
checks.push({ name, cmd, result: "not_run", duration_s: 0 });
|
|
13451
13730
|
notProven.add(`not run: ${name} (secret-scan.sh not found)`);
|
|
13452
13731
|
return;
|
|
@@ -13497,9 +13776,9 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
|
|
|
13497
13776
|
const prUrl = ctx.outputs().pr?.pr_url;
|
|
13498
13777
|
const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
|
|
13499
13778
|
if (prNumber) {
|
|
13500
|
-
|
|
13779
|
+
mkdirSync23(ctx.runDir, { recursive: true });
|
|
13501
13780
|
const bodyFile = join55(ctx.runDir, "deep-comment.md");
|
|
13502
|
-
|
|
13781
|
+
writeFileSync26(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
|
|
13503
13782
|
const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
13504
13783
|
if (commentResult.exitCode !== 0)
|
|
13505
13784
|
out2.push(`deep comment not posted (engine10-push.sh exit ${commentResult.exitCode})`);
|
|
@@ -13551,8 +13830,8 @@ async function runDeep(ctx, signal, opts = {}) {
|
|
|
13551
13830
|
const addendumSha256 = sha2562(canonicalJson(addendumBody));
|
|
13552
13831
|
const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
|
|
13553
13832
|
const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
13554
|
-
|
|
13555
|
-
|
|
13833
|
+
mkdirSync23(ctx.runDir, { recursive: true });
|
|
13834
|
+
writeFileSync26(join55(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
|
|
13556
13835
|
`);
|
|
13557
13836
|
const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
|
|
13558
13837
|
const allNotProven = [...notProvenList, ...pushNotProven];
|
|
@@ -13597,21 +13876,21 @@ var init_deep = __esm(() => {
|
|
|
13597
13876
|
});
|
|
13598
13877
|
|
|
13599
13878
|
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
13600
|
-
import { existsSync as
|
|
13601
|
-
import { tmpdir as
|
|
13602
|
-
import { dirname as
|
|
13879
|
+
import { existsSync as existsSync36, mkdtempSync as mkdtempSync10, realpathSync as realpathSync21, rmSync as rmSync18 } from "fs";
|
|
13880
|
+
import { tmpdir as tmpdir11 } from "os";
|
|
13881
|
+
import { dirname as dirname21, isAbsolute as isAbsolute9, join as join56, relative as relative7 } from "path";
|
|
13603
13882
|
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
13604
13883
|
const inRepo = (p) => {
|
|
13605
13884
|
try {
|
|
13606
|
-
const r = relative7(
|
|
13607
|
-
return r !== "" && !r.startsWith("..") && !
|
|
13885
|
+
const r = relative7(realpathSync21(repoDir), realpathSync21(p));
|
|
13886
|
+
return r !== "" && !r.startsWith("..") && !isAbsolute9(r);
|
|
13608
13887
|
} catch {
|
|
13609
13888
|
return false;
|
|
13610
13889
|
}
|
|
13611
13890
|
};
|
|
13612
13891
|
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
13613
|
-
const project = [join56(repoDir, ".venv", "bin", bin), join56(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join56(venvEnv, "bin", bin)] : []].find(
|
|
13614
|
-
return project ? [project, "project", []] :
|
|
13892
|
+
const project = [join56(repoDir, ".venv", "bin", bin), join56(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join56(venvEnv, "bin", bin)] : []].find(existsSync36);
|
|
13893
|
+
return project ? [project, "project", []] : existsSync36(join56(repoDir, "uv.lock")) && Bun.which("uv") ? ["uv", "project", ["run", "--", bin]] : [Bun.which(fallback) ? fallback : lastResort, "system", []];
|
|
13615
13894
|
}
|
|
13616
13895
|
function runnerCmd(t, repoDir) {
|
|
13617
13896
|
switch (t.runner) {
|
|
@@ -13630,7 +13909,7 @@ function runnerCmd(t, repoDir) {
|
|
|
13630
13909
|
case "npm":
|
|
13631
13910
|
return ["npm", ["test", "--silent"]];
|
|
13632
13911
|
case "go":
|
|
13633
|
-
return ["go", ["test", "-json", `./${
|
|
13912
|
+
return ["go", ["test", "-json", `./${dirname21(t.path)}`]];
|
|
13634
13913
|
case "cargo":
|
|
13635
13914
|
return ["cargo", ["test"]];
|
|
13636
13915
|
}
|
|
@@ -13725,12 +14004,12 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
13725
14004
|
const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
|
|
13726
14005
|
if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
|
|
13727
14006
|
return out2;
|
|
13728
|
-
const dir =
|
|
13729
|
-
const
|
|
14007
|
+
const dir = mkdtempSync10(join56(tmpdir11(), "e10-base-"));
|
|
14008
|
+
const git4 = (args) => {
|
|
13730
14009
|
safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
|
|
13731
14010
|
};
|
|
13732
14011
|
try {
|
|
13733
|
-
|
|
14012
|
+
git4(["-c", "core.hooksPath=/dev/null", "worktree", "add", "--detach", dir, ctx.baseSha]);
|
|
13734
14013
|
const base = new Map;
|
|
13735
14014
|
for (const { c, t } of new Set([...un, ...sus])) {
|
|
13736
14015
|
const bc = commandFor(t, dir, api);
|
|
@@ -13753,17 +14032,17 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
13753
14032
|
}
|
|
13754
14033
|
} catch {} finally {
|
|
13755
14034
|
try {
|
|
13756
|
-
|
|
14035
|
+
git4(["worktree", "remove", "--force", dir]);
|
|
13757
14036
|
} catch {}
|
|
13758
|
-
|
|
14037
|
+
rmSync18(dir, { recursive: true, force: true });
|
|
13759
14038
|
try {
|
|
13760
|
-
|
|
14039
|
+
git4(["worktree", "prune"]);
|
|
13761
14040
|
} catch {}
|
|
13762
14041
|
}
|
|
13763
14042
|
return out2;
|
|
13764
14043
|
}
|
|
13765
14044
|
function isLokiModeRepo(repoDir) {
|
|
13766
|
-
return
|
|
14045
|
+
return existsSync36(join56(repoDir, "scripts", "select-tests.sh"));
|
|
13767
14046
|
}
|
|
13768
14047
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
13769
14048
|
const { api, ...runOpts } = opts;
|
|
@@ -13785,9 +14064,9 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
13785
14064
|
}
|
|
13786
14065
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
13787
14066
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
13788
|
-
if (
|
|
14067
|
+
if (existsSync36(join56(g.cwd, "tsconfig.json")))
|
|
13789
14068
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
13790
|
-
if (ESLINT_CONFIGS.some((f) =>
|
|
14069
|
+
if (ESLINT_CONFIGS.some((f) => existsSync36(join56(g.cwd, f)))) {
|
|
13791
14070
|
const c = await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
13792
14071
|
if (await excuseLint(ctx, signal, c, "eslint", "npx", ["eslint"], g.cwd, g.rel, co))
|
|
13793
14072
|
pre.push(c.name);
|
|
@@ -13861,7 +14140,7 @@ var init_verify = __esm(() => {
|
|
|
13861
14140
|
const impacted = ctx.tests.impacted(map, changed);
|
|
13862
14141
|
const changedTestFiles = map.tests.filter((t) => changed.includes(t.path));
|
|
13863
14142
|
const wall = ctx.outputs().wall ?? {};
|
|
13864
|
-
const wallPaths = new Set((wall.files ?? []).map((f) =>
|
|
14143
|
+
const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute9(f.path) ? relative7(ctx.repoDir, f.path) : f.path));
|
|
13865
14144
|
const wallTests = map.tests.filter((t) => wallPaths.has(t.path));
|
|
13866
14145
|
const intake = ctx.outputs().intake, relevant2 = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
|
|
13867
14146
|
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant2]);
|
|
@@ -13967,8 +14246,8 @@ ${x}` : c.cmd };
|
|
|
13967
14246
|
});
|
|
13968
14247
|
|
|
13969
14248
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
13970
|
-
import { existsSync as
|
|
13971
|
-
import { dirname as
|
|
14249
|
+
import { existsSync as existsSync37, readdirSync as readdirSync17, readFileSync as readFileSync43 } from "fs";
|
|
14250
|
+
import { dirname as dirname22, join as join57 } from "path";
|
|
13972
14251
|
function parseJsonc(text2) {
|
|
13973
14252
|
let out2 = "", i = 0;
|
|
13974
14253
|
while (i < text2.length) {
|
|
@@ -14003,8 +14282,8 @@ function moduleOf(file, depth = 0) {
|
|
|
14003
14282
|
const ext = typeof j.extends === "string" ? j.extends : null;
|
|
14004
14283
|
if (!ext || !ext.startsWith("."))
|
|
14005
14284
|
return null;
|
|
14006
|
-
const next = join57(
|
|
14007
|
-
return
|
|
14285
|
+
const next = join57(dirname22(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
14286
|
+
return existsSync37(next) ? moduleOf(next, depth + 1) : null;
|
|
14008
14287
|
}
|
|
14009
14288
|
function readPackageConventions(pkgDir, runner = null) {
|
|
14010
14289
|
let names = [];
|
|
@@ -14043,7 +14322,7 @@ function conventionViolation(c, fileName, content) {
|
|
|
14043
14322
|
}
|
|
14044
14323
|
var readJson5 = (p) => {
|
|
14045
14324
|
try {
|
|
14046
|
-
const v = parseJsonc(
|
|
14325
|
+
const v = parseJsonc(readFileSync43(p, "utf8"));
|
|
14047
14326
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
14048
14327
|
} catch {
|
|
14049
14328
|
return null;
|
|
@@ -14053,95 +14332,9 @@ var init_conventions = () => {};
|
|
|
14053
14332
|
|
|
14054
14333
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
14055
14334
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
14056
|
-
import { existsSync as
|
|
14057
|
-
import { tmpdir as
|
|
14058
|
-
import { dirname as
|
|
14059
|
-
function pytestCollectionIsRed(output, repoDir) {
|
|
14060
|
-
if (/ModuleNotFoundError/.test(output))
|
|
14061
|
-
return false;
|
|
14062
|
-
const real2 = (p) => {
|
|
14063
|
-
try {
|
|
14064
|
-
return realpathSync20(isAbsolute9(p) ? p : join58(repoDir, p));
|
|
14065
|
-
} catch {
|
|
14066
|
-
return null;
|
|
14067
|
-
}
|
|
14068
|
-
};
|
|
14069
|
-
const repoReal = real2(repoDir);
|
|
14070
|
-
const under = (p) => {
|
|
14071
|
-
const r = real2(p);
|
|
14072
|
-
return !!repoReal && !!r && (r === repoReal || r.startsWith(`${repoReal}/`));
|
|
14073
|
-
};
|
|
14074
|
-
const imp = /ImportError: cannot import name .* from ['"][\w.]+['"] \(([^)]+)\)/.exec(output);
|
|
14075
|
-
if (imp)
|
|
14076
|
-
return under(imp[1]);
|
|
14077
|
-
const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
|
|
14078
|
-
if (!hdr)
|
|
14079
|
-
return false;
|
|
14080
|
-
const cls = [...output.slice(hdr.index).matchAll(/^ERROR \S+ - (\w+Error): /gm)].pop()?.[1];
|
|
14081
|
-
if (cls !== "AttributeError" && cls !== "NameError")
|
|
14082
|
-
return false;
|
|
14083
|
-
const start = output.search(/^_+ ERROR collecting /m);
|
|
14084
|
-
if (start < 0 || start > hdr.index)
|
|
14085
|
-
return false;
|
|
14086
|
-
const block = output.slice(start, hdr.index).split(/^-+ Captured .*$/m)[0];
|
|
14087
|
-
const eLines = [...block.matchAll(new RegExp(`^E {3}${cls}: (.*)$`, "gm"))];
|
|
14088
|
-
const eLine = eLines[eLines.length - 1];
|
|
14089
|
-
if (!eLine)
|
|
14090
|
-
return false;
|
|
14091
|
-
if (cls === "AttributeError") {
|
|
14092
|
-
const m = /^module ['"]([\w.]+)['"] has no attribute/.exec(eLine[1]);
|
|
14093
|
-
return !!m && moduleUnderRepo(repoDir, m[1]);
|
|
14094
|
-
}
|
|
14095
|
-
const frames = [...block.slice(0, eLine.index).matchAll(/^(.+?):\d+: in \S+\n\s*(.*)$/gm)];
|
|
14096
|
-
const last = frames[frames.length - 1];
|
|
14097
|
-
return !!last && under(last[1]) && !/^\s*(import\s|from\s\S+\s+import\b)/.test(last[2] ?? "");
|
|
14098
|
-
}
|
|
14099
|
-
function moduleUnderRepo(repoDir, name) {
|
|
14100
|
-
const rel = name.replace(/\./g, "/");
|
|
14101
|
-
return existsSync37(join58(repoDir, `${rel}.py`)) || existsSync37(join58(repoDir, rel, "__init__.py"));
|
|
14102
|
-
}
|
|
14103
|
-
function pytestExit1IsRed(output) {
|
|
14104
|
-
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
14105
|
-
return false;
|
|
14106
|
-
if (/^=*\s*(\d+ \w+(, )?)*\d+ failed\b.* in [\d.]+s/.test(output.trimEnd().split(`
|
|
14107
|
-
`).pop() ?? ""))
|
|
14108
|
-
return true;
|
|
14109
|
-
const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
|
|
14110
|
-
return !!hdr && /^FAILED \S+/m.test(output.slice(hdr.index));
|
|
14111
|
-
}
|
|
14112
|
-
function parsedFailCount(runner, output) {
|
|
14113
|
-
const m = runner === "bun" ? /^\s*(\d+)\s+fail\s*$/m.exec(output) : runner === "jest" ? /Tests:\s*(\d+)\s+failed/i.exec(output) : runner === "vitest" ? /Tests\s+(\d+)\s+failed/i.exec(output) : null;
|
|
14114
|
-
return m ? Number(m[1]) : 0;
|
|
14115
|
-
}
|
|
14116
|
-
function nodeIsRed(f, raw2) {
|
|
14117
|
-
const o = raw2.replace(/^# (?!(?:tests|suites|pass|fail|cancelled|skipped|todo|duration_ms|Subtest)\b)/gm, "");
|
|
14118
|
-
const mod = /^Error(?: \[\w+\])?: Cannot find (?:module|package) '([^']+)'(?:(?!\u2716)[\s\S])*?\nNode\.js v/m.exec(o), top = /^(\S+):\d+\n.*\n.*\n\n?(?:ReferenceError|SyntaxError)\b/m.exec(o);
|
|
14119
|
-
const named = [...o.matchAll(/^(?:\u2716|\s*not ok \d+ -) (?!failing tests:)(.+?)(?: \(|$)/gm)].some((m) => !f.path.endsWith(m[1]) && !m[1].endsWith(f.path)), blk = o.split("failing tests:")[1] ?? "", esm = /^SyntaxError: The requested module '([^']+)' does not provide an export named/m.exec(o), errs = [...blk.matchAll(/^ {2}(\w+Error): .*\n\s+at (?:.*\()?([^\s()]+?):\d+:\d+/gm), ...o.matchAll(/^ +name: '(\w+Error)'\n(?:.*\n)*? +stack: \|-\n\s+(?:.*\()?([^\s()]+?):\d+:\d+/gm)];
|
|
14120
|
-
return /^(?:#|\u2139) fail [1-9]/m.test(raw2) && (/^ {2}AssertionError\b/m.test(blk) || /^ +name: 'AssertionError'/m.test(o) || (mod && !named ? /^[./]/.test(mod[1]) : esm ? /^[./]/.test(esm[1]) : top ? !top[1].endsWith(f.path) : !errs.length || errs.some((e) => !/^(?:Reference|Syntax)Error$/.test(e[1]) || !(e[2].startsWith("node:") || e[2].endsWith(f.path)))));
|
|
14121
|
-
}
|
|
14122
|
-
function notRunReason(status, interpreter) {
|
|
14123
|
-
if (interpreter === "system")
|
|
14124
|
-
return "ran under the system interpreter, not the project's, so the result proves nothing";
|
|
14125
|
-
if (status === null)
|
|
14126
|
-
return "the test runner did not start or timed out";
|
|
14127
|
-
if (status === 126 || status === 127)
|
|
14128
|
-
return `the test runner command was not found or not executable (exit ${status})`;
|
|
14129
|
-
return status === 0 ? "exit 0 but no executed test count could be confirmed" : `exit ${status} but the runner output showed no readable failing test`;
|
|
14130
|
-
}
|
|
14131
|
-
function classify(f, status, output, repoDir, interpreter) {
|
|
14132
|
-
if (interpreter === "system")
|
|
14133
|
-
return "not_run";
|
|
14134
|
-
output = stripAnsi(output);
|
|
14135
|
-
if (status === null || status === 126 || status === 127)
|
|
14136
|
-
return "not_run";
|
|
14137
|
-
if (status === 0)
|
|
14138
|
-
return classifyCheck({ kind: "test", ok: true, out: output, path: f.path, ...f.runner === "go" ? { runner: "go" } : {} }).result === "pass" ? "pass" : "not_run";
|
|
14139
|
-
if (f.runner === "pytest")
|
|
14140
|
-
return status === 1 ? pytestExit1IsRed(output) ? "fail" : "not_run" : status === 2 ? pytestCollectionIsRed(output, repoDir) ? "fail" : "not_run" : "not_run";
|
|
14141
|
-
if (f.runner === "jest" || f.runner === "vitest" || f.runner === "bun")
|
|
14142
|
-
return parsedFailCount(f.runner, output) > 0 ? "fail" : "not_run";
|
|
14143
|
-
return f.runner === "node" && nodeIsRed(f, output) ? "fail" : "not_run";
|
|
14144
|
-
}
|
|
14335
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync24, mkdtempSync as mkdtempSync11, readFileSync as readFileSync44, readdirSync as readdirSync18, rmSync as rmSync19, writeFileSync as writeFileSync27 } from "fs";
|
|
14336
|
+
import { tmpdir as tmpdir12 } from "os";
|
|
14337
|
+
import { dirname as dirname23, join as join58, relative as relative8 } from "path";
|
|
14145
14338
|
|
|
14146
14339
|
class RealBaseTestRunner {
|
|
14147
14340
|
api;
|
|
@@ -14186,7 +14379,7 @@ ${repomapText}`] : [],
|
|
|
14186
14379
|
...acceptance.length ? [`Acceptance criteria frozen for this unit (each must be covered by a test):
|
|
14187
14380
|
${acceptance.map((a) => `Acceptance: ${a}`).join(`
|
|
14188
14381
|
`)}`] : [],
|
|
14189
|
-
"Write behavioral acceptance tests that prove the task is done. A test that errors on import, uses another framework's globals, or fails for a reason unrelated to the task is discarded.",
|
|
14382
|
+
"Write behavioral acceptance tests that prove the task is done. A test that errors on import, uses another framework's globals, or fails for a reason unrelated to the task is discarded. Negative case: for each acceptance line include one assertion that rejects bad input or would fail before the fix.",
|
|
14190
14383
|
`Test runner: ${runners.map((r) => RUNNER_HINT[r]).find(Boolean) ?? "the framework named in repomap.txt"}`,
|
|
14191
14384
|
...conventions ? [conventions] : [],
|
|
14192
14385
|
`Name every file you write starting with "${WALL_PREFIX}". Write nothing else.`
|
|
@@ -14198,9 +14391,9 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
14198
14391
|
if (fromPrior)
|
|
14199
14392
|
return fromPrior;
|
|
14200
14393
|
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join58(ctx.runDir, "issue.json");
|
|
14201
|
-
if (
|
|
14394
|
+
if (existsSync38(issueJsonPath)) {
|
|
14202
14395
|
try {
|
|
14203
|
-
const issue = JSON.parse(
|
|
14396
|
+
const issue = JSON.parse(readFileSync44(issueJsonPath, "utf8"));
|
|
14204
14397
|
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
14205
14398
|
|
|
14206
14399
|
`);
|
|
@@ -14212,7 +14405,7 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
14212
14405
|
}
|
|
14213
14406
|
function wallTargetDir(repoDir, existingTests) {
|
|
14214
14407
|
const first = existingTests[0];
|
|
14215
|
-
return first ? join58(repoDir,
|
|
14408
|
+
return first ? join58(repoDir, dirname23(first.path)) : join58(repoDir, "tests");
|
|
14216
14409
|
}
|
|
14217
14410
|
function guessRunner(fileName, runners) {
|
|
14218
14411
|
if (fileName.endsWith(".py"))
|
|
@@ -14246,10 +14439,10 @@ async function wallAuthor(ctx, signal, retry) {
|
|
|
14246
14439
|
const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
|
|
14247
14440
|
const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative8(ctx.repoDir, targetDir)}/x`);
|
|
14248
14441
|
const conv2 = readPackageConventions(join58(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
|
|
14249
|
-
const cwd =
|
|
14442
|
+
const cwd = mkdtempSync11(join58(tmpdir12(), "loki-e15-wall-"));
|
|
14250
14443
|
const wm = wallManifestFor(ctx.repoDir, tree, task);
|
|
14251
14444
|
for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
|
|
14252
|
-
|
|
14445
|
+
writeFileSync27(join58(cwd, n2), c, "utf8");
|
|
14253
14446
|
const model = retry?.model ?? wallFirstModel(sz.size), iterationId = `${ctx.runId}-wall${retry?.suffix ?? ""}`;
|
|
14254
14447
|
const session = await ctx.sessions.run({
|
|
14255
14448
|
stage: "wall",
|
|
@@ -14262,30 +14455,30 @@ async function wallAuthor(ctx, signal, retry) {
|
|
|
14262
14455
|
cwd
|
|
14263
14456
|
});
|
|
14264
14457
|
if (session.killed || session.exit === null) {
|
|
14265
|
-
|
|
14458
|
+
rmSync19(cwd, { recursive: true, force: true });
|
|
14266
14459
|
return done({ status: "failed", data: { iteration_ids: [iterationId], model }, reason: "wall session aborted, killed, or timed out", killed: true });
|
|
14267
14460
|
}
|
|
14268
|
-
const all = readdirSync18(cwd).filter((f) => f.startsWith(WALL_PREFIX)), held = stashHoldout({ runId: ctx.runId, repoDir: ctx.repoDir, srcDir: cwd, targetDir, names: all.filter((n2) => !conventionViolation(conv2, n2,
|
|
14461
|
+
const all = readdirSync18(cwd).filter((f) => f.startsWith(WALL_PREFIX)), held = stashHoldout({ runId: ctx.runId, repoDir: ctx.repoDir, srcDir: cwd, targetDir, names: all.filter((n2) => !conventionViolation(conv2, n2, readFileSync44(join58(cwd, n2), "utf8"))), runnerOf: (n2) => guessRunner(n2, runners) }), generated = all.filter((n2) => !held?.names.has(n2));
|
|
14269
14462
|
const sealedDir = join58(ctx.runDir, "wall");
|
|
14270
14463
|
if (generated.length > 0)
|
|
14271
|
-
|
|
14464
|
+
mkdirSync24(sealedDir, { recursive: true });
|
|
14272
14465
|
const contents = new Map;
|
|
14273
14466
|
const discarded = [];
|
|
14274
14467
|
for (const name of generated) {
|
|
14275
|
-
const content =
|
|
14276
|
-
|
|
14468
|
+
const content = readFileSync44(join58(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
|
|
14469
|
+
writeFileSync27(join58(sealedDir, name), content, "utf8");
|
|
14277
14470
|
if (bad)
|
|
14278
14471
|
discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
|
|
14279
14472
|
else
|
|
14280
14473
|
contents.set(name, content);
|
|
14281
14474
|
}
|
|
14282
|
-
|
|
14475
|
+
rmSync19(cwd, { recursive: true, force: true });
|
|
14283
14476
|
return { kind: "authored", model, iterationId, task, sizeName: sz.size, runners, targetDir, generated, contents, discarded, ...wm ? { manifestSha256: wm.sha256 } : {}, ...held ? { holdoutDir: held.dir } : {} };
|
|
14284
14477
|
}
|
|
14285
14478
|
function installWall(ctx, a, baseDir, opts = {}) {
|
|
14286
14479
|
const { generated, contents, discarded, targetDir, runners } = a;
|
|
14287
14480
|
if (generated.length > 0)
|
|
14288
|
-
|
|
14481
|
+
mkdirSync24(targetDir, { recursive: true });
|
|
14289
14482
|
const sealedFiles = [];
|
|
14290
14483
|
const readOnlyFiles = [];
|
|
14291
14484
|
const wallTests = [];
|
|
@@ -14294,7 +14487,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
14294
14487
|
if (content === undefined)
|
|
14295
14488
|
continue;
|
|
14296
14489
|
const dest = join58(targetDir, name), runner = guessRunner(name, runners);
|
|
14297
|
-
|
|
14490
|
+
writeFileSync27(dest, content, "utf8");
|
|
14298
14491
|
sealedFiles.push({ path: dest, sha256: sha2562(content) });
|
|
14299
14492
|
readOnlyFiles.push({ path: dest, content });
|
|
14300
14493
|
if (runner)
|
|
@@ -14310,7 +14503,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
14310
14503
|
baseRun.not_run += r.not_run ?? 0;
|
|
14311
14504
|
notRunFiles.push(...baseRunner.lastNotRunFiles ?? []);
|
|
14312
14505
|
if (r.pass + r.fail === 0) {
|
|
14313
|
-
|
|
14506
|
+
rmSync19(abs, { force: true });
|
|
14314
14507
|
for (const l of [sealedFiles, readOnlyFiles])
|
|
14315
14508
|
l.splice(0, l.length, ...l.filter((f) => f.path !== abs));
|
|
14316
14509
|
}
|
|
@@ -14343,8 +14536,8 @@ async function runWall(ctx, signal, opts = {}) {
|
|
|
14343
14536
|
return green ? { ...first, data: distrustGreenBase(first.data) } : first;
|
|
14344
14537
|
if (a.kind === "authored") {
|
|
14345
14538
|
for (const f of first.data.files ?? [])
|
|
14346
|
-
|
|
14347
|
-
|
|
14539
|
+
rmSync19(f.path, { force: true });
|
|
14540
|
+
rmSync19(join58(ctx.runDir, "wall"), { recursive: true, force: true });
|
|
14348
14541
|
ctx.wallSealed = false;
|
|
14349
14542
|
}
|
|
14350
14543
|
removeHoldout(ctx.runId, ctx.repoDir);
|
|
@@ -14367,6 +14560,7 @@ var init_wall = __esm(() => {
|
|
|
14367
14560
|
init_resolve();
|
|
14368
14561
|
init_check_result();
|
|
14369
14562
|
init_conventions();
|
|
14563
|
+
init_check_result();
|
|
14370
14564
|
WALL_RUNG_S = WALL_SESSION_MAX_S + WALL_BASE_FILES * (BASE_RUN_TIMEOUT_MS / 1000);
|
|
14371
14565
|
WALL_STAGE_LIMIT_S = 2 * WALL_RUNG_S;
|
|
14372
14566
|
wallStage = {
|
|
@@ -14379,20 +14573,20 @@ var init_wall = __esm(() => {
|
|
|
14379
14573
|
});
|
|
14380
14574
|
|
|
14381
14575
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
14382
|
-
import { chmodSync as chmodSync5, closeSync as closeSync9, lstatSync as lstatSync13, mkdirSync as
|
|
14383
|
-
import { dirname as
|
|
14576
|
+
import { chmodSync as chmodSync5, closeSync as closeSync9, lstatSync as lstatSync13, mkdirSync as mkdirSync25, openSync as openSync9, readFileSync as readFileSync45, realpathSync as realpathSync23, rmdirSync, unlinkSync as unlinkSync6, writeFileSync as writeFileSync28 } from "fs";
|
|
14577
|
+
import { dirname as dirname24, join as join59, relative as relative9, sep as sep13 } from "path";
|
|
14384
14578
|
function safeRestore(repoDir, f, want, mode) {
|
|
14385
|
-
const root =
|
|
14579
|
+
const root = realpathSync23(repoDir), fp = join59(root, f), rel = relative9(root, dirname24(fp));
|
|
14386
14580
|
if (rel.startsWith(".."))
|
|
14387
14581
|
throw new Error("outside repo");
|
|
14388
14582
|
let cur = root;
|
|
14389
|
-
for (const part of rel === "" ? [] : rel.split(
|
|
14583
|
+
for (const part of rel === "" ? [] : rel.split(sep13)) {
|
|
14390
14584
|
cur = join59(cur, part);
|
|
14391
14585
|
let st2;
|
|
14392
14586
|
try {
|
|
14393
14587
|
st2 = lstatSync13(cur);
|
|
14394
14588
|
} catch {
|
|
14395
|
-
|
|
14589
|
+
mkdirSync25(cur);
|
|
14396
14590
|
continue;
|
|
14397
14591
|
}
|
|
14398
14592
|
if (!st2.isDirectory())
|
|
@@ -14404,7 +14598,7 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
14404
14598
|
} catch {
|
|
14405
14599
|
st = null;
|
|
14406
14600
|
}
|
|
14407
|
-
if (st?.isFile() &&
|
|
14601
|
+
if (st?.isFile() && readFileSync45(fp).equals(want)) {
|
|
14408
14602
|
chmodSync5(fp, mode);
|
|
14409
14603
|
return;
|
|
14410
14604
|
}
|
|
@@ -14416,7 +14610,7 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
14416
14610
|
}
|
|
14417
14611
|
const fd = openSync9(fp, "wx", mode);
|
|
14418
14612
|
try {
|
|
14419
|
-
|
|
14613
|
+
writeFileSync28(fd, want);
|
|
14420
14614
|
} finally {
|
|
14421
14615
|
closeSync9(fd);
|
|
14422
14616
|
}
|
|
@@ -14432,13 +14626,13 @@ function catBlob(repoDir, sha3) {
|
|
|
14432
14626
|
throw r.error ?? new Error(`git cat-file blob ${sha3} failed`);
|
|
14433
14627
|
return r.stdout;
|
|
14434
14628
|
}
|
|
14435
|
-
async function discardIfSatisfied(
|
|
14629
|
+
async function discardIfSatisfied(git4, base, o, staged, keep, repoDir) {
|
|
14436
14630
|
if (!alreadySatisfied(o))
|
|
14437
14631
|
return null;
|
|
14438
14632
|
const pre = Object.assign(Object.create(null), o.intake?.preexisting_dirty ?? {}), own = new Set([...Array.isArray(o.intake?.preexisting_untracked) ? o.intake.preexisting_untracked : [], ...Object.keys(o.intake?.preexisting_untracked_blobs ?? {})]);
|
|
14439
14633
|
const gone = staged.filter(({ f }) => !keep.has(f) && !own.has(f) && !Object.hasOwn(pre, f) && !f.startsWith(".loki/")), lit = ["--literal-pathspecs"];
|
|
14440
14634
|
const del = gone.filter(({ st }) => st === "A").map(({ f }) => f), back = gone.filter(({ st }) => st !== "A").map(({ f }) => f);
|
|
14441
|
-
if (del.length > 0 && (await
|
|
14635
|
+
if (del.length > 0 && (await git4([...lit, "rm", "-q", "-f", "--", ...del])).code !== 0 || back.length > 0 && (await git4([...lit, "restore", `--source=${base}`, "--staged", "--worktree", "--", ...back])).code !== 0 || (await git4(["reset", "-q", base])).code !== 0)
|
|
14442
14636
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
14443
14637
|
const notProven = [];
|
|
14444
14638
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
@@ -14494,9 +14688,9 @@ var init_preexisting_dirty = __esm(() => {
|
|
|
14494
14688
|
});
|
|
14495
14689
|
|
|
14496
14690
|
// ../../loki-ts/src/e10ext/commit_filter.ts
|
|
14497
|
-
import { dirname as
|
|
14691
|
+
import { dirname as dirname25 } from "path";
|
|
14498
14692
|
function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
|
|
14499
|
-
return staged.concat(untouchedSinceIntake(repoDir, preexistingDirty, env).map((f) => ({ st: "L", f }))).filter(({ st, f }) => f.startsWith(".loki/") || st === "L" || /(^|\/)loki_wall_[^/]*$/.test(f) || st === "A" && !staged.some(({ f: m }) => /(^|\/)(package\.json|pyproject\.toml|requirements\.txt|Cargo\.toml|go\.mod)$/.test(m) &&
|
|
14693
|
+
return staged.concat(untouchedSinceIntake(repoDir, preexistingDirty, env).map((f) => ({ st: "L", f }))).filter(({ st, f }) => f.startsWith(".loki/") || st === "L" || /(^|\/)loki_wall_[^/]*$/.test(f) || st === "A" && !staged.some(({ f: m }) => /(^|\/)(package\.json|pyproject\.toml|requirements\.txt|Cargo\.toml|go\.mod)$/.test(m) && dirname25(m) === dirname25(f)) && /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|poetry\.lock|Cargo\.lock|go\.sum)$/.test(f));
|
|
14500
14694
|
}
|
|
14501
14695
|
var parseStaged = (out2) => out2.split("\x00").reduce((a, t, i, all) => i % 2 === 0 && t ? [...a, { st: t, f: all[i + 1] }] : a, []);
|
|
14502
14696
|
var init_commit_filter = __esm(() => {
|
|
@@ -14505,7 +14699,7 @@ var init_commit_filter = __esm(() => {
|
|
|
14505
14699
|
});
|
|
14506
14700
|
|
|
14507
14701
|
// ../../loki-ts/src/e10ext/scope.ts
|
|
14508
|
-
import { basename as basename20, dirname as
|
|
14702
|
+
import { basename as basename20, dirname as dirname26 } from "path";
|
|
14509
14703
|
function contractText(o) {
|
|
14510
14704
|
const c = o.intake?.contract_snapshot?.contract;
|
|
14511
14705
|
return Array.isArray(c?.criteria) ? c.criteria.map((x) => typeof x?.text === "string" ? x.text : "").join(`
|
|
@@ -14519,8 +14713,8 @@ function outsideScope(o, staged) {
|
|
|
14519
14713
|
const text2 = `${plan}
|
|
14520
14714
|
${typeof o.intake?.task === "string" ? o.intake.task : ""}
|
|
14521
14715
|
${contractText(o)}`, pre = o.intake?.preexisting_dirty ?? {};
|
|
14522
|
-
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) =>
|
|
14523
|
-
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename20(f)) && !plannedDirs.has(
|
|
14716
|
+
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) => dirname26(r)).filter((d) => d !== "."));
|
|
14717
|
+
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename20(f)) && !plannedDirs.has(dirname26(f)) && !dirSegs(f).some((d) => toks.has(d))).map(({ f }) => f);
|
|
14524
14718
|
}
|
|
14525
14719
|
function flagOutsideScope(o, staged) {
|
|
14526
14720
|
const unit = unitFlags(staged, process.env, o.intake?.preexisting_dirty ?? {});
|
|
@@ -14535,14 +14729,14 @@ var init_scope2 = __esm(() => {
|
|
|
14535
14729
|
});
|
|
14536
14730
|
|
|
14537
14731
|
// ../../loki-ts/src/council/role_agents.ts
|
|
14538
|
-
import { existsSync as
|
|
14732
|
+
import { existsSync as existsSync39, readdirSync as readdirSync19, readFileSync as readFileSync46 } from "fs";
|
|
14539
14733
|
import { homedir as homedir9 } from "os";
|
|
14540
14734
|
import { join as join60 } from "path";
|
|
14541
14735
|
function agentDirs(repoDir, env = process.env) {
|
|
14542
14736
|
return [env["LOKI_AGENTS_PROJECT_DIR"] || join60(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join60(env["XDG_CONFIG_HOME"] || join60(homedir9(), ".config"), "loki", "agents")];
|
|
14543
14737
|
}
|
|
14544
14738
|
function parseAgent(file, stem2) {
|
|
14545
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(
|
|
14739
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync46(file, "utf8"));
|
|
14546
14740
|
if (!m)
|
|
14547
14741
|
return null;
|
|
14548
14742
|
const body = m[2].trim();
|
|
@@ -14556,7 +14750,7 @@ function parseAgent(file, stem2) {
|
|
|
14556
14750
|
}
|
|
14557
14751
|
function loadRoleAgent(repoDir, role, env = process.env) {
|
|
14558
14752
|
for (const d of agentDirs(repoDir, env)) {
|
|
14559
|
-
if (!
|
|
14753
|
+
if (!existsSync39(d))
|
|
14560
14754
|
continue;
|
|
14561
14755
|
let names;
|
|
14562
14756
|
try {
|
|
@@ -14584,7 +14778,7 @@ var init_role_agents = __esm(() => {
|
|
|
14584
14778
|
});
|
|
14585
14779
|
|
|
14586
14780
|
// ../../loki-ts/src/util/child_ledger.ts
|
|
14587
|
-
import { appendFileSync as appendFileSync4, mkdirSync as
|
|
14781
|
+
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync26 } from "fs";
|
|
14588
14782
|
import { join as join61 } from "path";
|
|
14589
14783
|
function unmeasuredRecord(phase, model, source) {
|
|
14590
14784
|
return { phase, model, input_tokens: 0, output_tokens: 0, cache_creation_tokens: 0, cache_read_tokens: 0, source, cost_status: "unmeasured" };
|
|
@@ -14611,7 +14805,7 @@ function recordFromClaudeResult(phase, payload, source = "claude-json") {
|
|
|
14611
14805
|
function appendChildLedger(lokiDir2, rec2) {
|
|
14612
14806
|
try {
|
|
14613
14807
|
const dir = join61(lokiDir2, "metrics");
|
|
14614
|
-
|
|
14808
|
+
mkdirSync26(dir, { recursive: true });
|
|
14615
14809
|
appendFileSync4(join61(dir, "child-sessions.jsonl"), `${JSON.stringify(rec2)}
|
|
14616
14810
|
`);
|
|
14617
14811
|
return true;
|
|
@@ -14642,14 +14836,14 @@ var init_child_ledger = __esm(() => {
|
|
|
14642
14836
|
});
|
|
14643
14837
|
|
|
14644
14838
|
// ../../loki-ts/src/util/run_summary.ts
|
|
14645
|
-
import { existsSync as
|
|
14839
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync27, readFileSync as readFileSync47, renameSync as renameSync12, statSync as statSync18, writeFileSync as writeFileSync29 } from "fs";
|
|
14646
14840
|
import { join as join62 } from "path";
|
|
14647
14841
|
function readRiskRecord(repoDir, sinceMs = 0) {
|
|
14648
14842
|
try {
|
|
14649
14843
|
const p = join62(repoDir, ".loki", "state", "risk-tier.json");
|
|
14650
|
-
if (
|
|
14844
|
+
if (statSync18(p).mtimeMs < sinceMs)
|
|
14651
14845
|
return null;
|
|
14652
|
-
const r = JSON.parse(
|
|
14846
|
+
const r = JSON.parse(readFileSync47(p, "utf8"));
|
|
14653
14847
|
return r && typeof r === "object" ? r : null;
|
|
14654
14848
|
} catch {
|
|
14655
14849
|
return null;
|
|
@@ -14663,7 +14857,7 @@ var init_run_summary = __esm(() => {
|
|
|
14663
14857
|
});
|
|
14664
14858
|
|
|
14665
14859
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
14666
|
-
import { existsSync as
|
|
14860
|
+
import { existsSync as existsSync41, readFileSync as readFileSync48 } from "fs";
|
|
14667
14861
|
import { join as join63 } from "path";
|
|
14668
14862
|
function minVerdict(verdict, review) {
|
|
14669
14863
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
@@ -14678,10 +14872,10 @@ function explicitReview(repoDir, env) {
|
|
|
14678
14872
|
return "off";
|
|
14679
14873
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
14680
14874
|
const p = join63(repoDir, f);
|
|
14681
|
-
if (!
|
|
14875
|
+
if (!existsSync41(p))
|
|
14682
14876
|
continue;
|
|
14683
14877
|
try {
|
|
14684
|
-
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(
|
|
14878
|
+
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync48(p, "utf8"));
|
|
14685
14879
|
const v = asProvider(m?.[1]);
|
|
14686
14880
|
if (v)
|
|
14687
14881
|
return v;
|
|
@@ -14859,8 +15053,8 @@ See also: loki cost --detail (what a run ALREADY spent)
|
|
|
14859
15053
|
});
|
|
14860
15054
|
|
|
14861
15055
|
// ../../loki-ts/src/features/contract.ts
|
|
14862
|
-
import { existsSync as
|
|
14863
|
-
import { dirname as
|
|
15056
|
+
import { existsSync as existsSync42, lstatSync as lstatSync14, mkdirSync as mkdirSync28, readFileSync as readFileSync49, rmSync as rmSync20, writeFileSync as writeFileSync30 } from "fs";
|
|
15057
|
+
import { dirname as dirname27, join as join64 } from "path";
|
|
14864
15058
|
import { createHash as createHash15 } from "crypto";
|
|
14865
15059
|
function contractEnabled(env = process.env) {
|
|
14866
15060
|
return env["LOKI_CONTRACT"] !== "0";
|
|
@@ -14908,7 +15102,7 @@ function readContract(repoDir) {
|
|
|
14908
15102
|
return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
|
|
14909
15103
|
let raw2;
|
|
14910
15104
|
try {
|
|
14911
|
-
raw2 =
|
|
15105
|
+
raw2 = readFileSync49(p);
|
|
14912
15106
|
} catch {
|
|
14913
15107
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
14914
15108
|
}
|
|
@@ -14983,11 +15177,11 @@ var init_contract = __esm(() => {
|
|
|
14983
15177
|
|
|
14984
15178
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
14985
15179
|
import { createHash as createHash16 } from "crypto";
|
|
14986
|
-
import { existsSync as
|
|
14987
|
-
import { dirname as
|
|
15180
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync29, readdirSync as readdirSync20, readFileSync as readFileSync50, renameSync as renameSync13, statSync as statSync19, writeFileSync as writeFileSync31 } from "fs";
|
|
15181
|
+
import { dirname as dirname28, join as join65 } from "path";
|
|
14988
15182
|
function sealGroup(runDir, rh) {
|
|
14989
15183
|
const manifestPath = join65(runDir, "group", "manifest.json");
|
|
14990
|
-
if (!
|
|
15184
|
+
if (!existsSync43(manifestPath))
|
|
14991
15185
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
14992
15186
|
const notProven = [];
|
|
14993
15187
|
let problems = 0;
|
|
@@ -15014,7 +15208,7 @@ function sealGroup(runDir, rh) {
|
|
|
15014
15208
|
const dir = join65(unitsDir(runDir), id), rp = join65(dir, "receipt.json"), ep = join65(dir, "events.jsonl");
|
|
15015
15209
|
let bytes;
|
|
15016
15210
|
try {
|
|
15017
|
-
bytes =
|
|
15211
|
+
bytes = readFileSync50(rp);
|
|
15018
15212
|
} catch {
|
|
15019
15213
|
return fail2(`unit ${id} sub-receipt missing`);
|
|
15020
15214
|
}
|
|
@@ -15031,7 +15225,7 @@ function sealGroup(runDir, rh) {
|
|
|
15031
15225
|
unit_id: id,
|
|
15032
15226
|
sub_receipt_sha256: sha3(bytes),
|
|
15033
15227
|
receipt_sha256: sub["receipt_sha256"],
|
|
15034
|
-
events_file_sha256:
|
|
15228
|
+
events_file_sha256: existsSync43(ep) ? sha3(readFileSync50(ep)) : null,
|
|
15035
15229
|
verdict,
|
|
15036
15230
|
goal: text2(u["goal"], 200),
|
|
15037
15231
|
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
@@ -15051,12 +15245,12 @@ function capGroupVerdict(v, g) {
|
|
|
15051
15245
|
return !g.allUnitsPass && (v === "VERIFIED" || v === "ALREADY_SATISFIED") ? "PARTIAL" : v;
|
|
15052
15246
|
}
|
|
15053
15247
|
async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
15054
|
-
const runDir =
|
|
15248
|
+
const runDir = dirname28(receiptPath);
|
|
15055
15249
|
const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
|
|
15056
15250
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
15057
15251
|
const g = receipt["group"];
|
|
15058
15252
|
if (g === undefined)
|
|
15059
|
-
return
|
|
15253
|
+
return existsSync43(join65(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
15060
15254
|
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
15061
15255
|
return bad("group section malformed");
|
|
15062
15256
|
const entries = g["units"];
|
|
@@ -15073,15 +15267,15 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
15073
15267
|
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
15074
15268
|
let bytes;
|
|
15075
15269
|
try {
|
|
15076
|
-
if (!
|
|
15270
|
+
if (!statSync19(dir).isDirectory())
|
|
15077
15271
|
return bad(`unit ${id} is not a directory`);
|
|
15078
|
-
bytes =
|
|
15272
|
+
bytes = readFileSync50(rp);
|
|
15079
15273
|
} catch {
|
|
15080
15274
|
return bad(`unit ${id} sub-receipt missing`);
|
|
15081
15275
|
}
|
|
15082
15276
|
if (sha3(bytes) !== e["sub_receipt_sha256"])
|
|
15083
15277
|
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
15084
|
-
const evSha =
|
|
15278
|
+
const evSha = existsSync43(ep) ? sha3(readFileSync50(ep)) : null;
|
|
15085
15279
|
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
15086
15280
|
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
15087
15281
|
const sub = readJson6(rp);
|
|
@@ -15106,7 +15300,7 @@ function compositeVerdict(childVerdicts) {
|
|
|
15106
15300
|
}
|
|
15107
15301
|
var ID_RE, PASSING, sha3 = (b) => createHash16("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) => join65(runDir, "group", "units"), readJson6 = (p) => {
|
|
15108
15302
|
try {
|
|
15109
|
-
return JSON.parse(
|
|
15303
|
+
return JSON.parse(readFileSync50(p, "utf8"));
|
|
15110
15304
|
} catch {
|
|
15111
15305
|
return;
|
|
15112
15306
|
}
|
|
@@ -15124,7 +15318,7 @@ var init_seal_group = __esm(() => {
|
|
|
15124
15318
|
});
|
|
15125
15319
|
|
|
15126
15320
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
15127
|
-
import { existsSync as
|
|
15321
|
+
import { existsSync as existsSync44, readFileSync as readFileSync51, statSync as statSync20 } from "fs";
|
|
15128
15322
|
import { basename as basename21, join as join66 } from "path";
|
|
15129
15323
|
function supplyEnabled(env) {
|
|
15130
15324
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
@@ -15141,12 +15335,12 @@ function isManifestPath(path) {
|
|
|
15141
15335
|
}
|
|
15142
15336
|
function readDeclared(repoDir) {
|
|
15143
15337
|
const p = join66(repoDir, ".loki", DECLARED_FILE);
|
|
15144
|
-
if (!
|
|
15338
|
+
if (!existsSync44(p))
|
|
15145
15339
|
return { deps: [], problem: null };
|
|
15146
15340
|
try {
|
|
15147
|
-
if (
|
|
15341
|
+
if (statSync20(p).size > MAX_FILE_BYTES)
|
|
15148
15342
|
return { deps: [], problem: "declaration file too large" };
|
|
15149
|
-
const j = JSON.parse(
|
|
15343
|
+
const j = JSON.parse(readFileSync51(p, "utf8"));
|
|
15150
15344
|
if (!Array.isArray(j))
|
|
15151
15345
|
return { deps: [], problem: "declaration is not a JSON array" };
|
|
15152
15346
|
const deps = [];
|
|
@@ -15167,10 +15361,10 @@ function readDeclared(repoDir) {
|
|
|
15167
15361
|
}
|
|
15168
15362
|
function readAllowlist(repoDir) {
|
|
15169
15363
|
const p = join66(repoDir, ".loki", "supply-allowlist");
|
|
15170
|
-
if (!
|
|
15364
|
+
if (!existsSync44(p))
|
|
15171
15365
|
return new Set;
|
|
15172
15366
|
try {
|
|
15173
|
-
return new Set(
|
|
15367
|
+
return new Set(readFileSync51(p, "utf8").split(`
|
|
15174
15368
|
`).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
|
|
15175
15369
|
} catch {
|
|
15176
15370
|
return new Set;
|
|
@@ -15353,7 +15547,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
|
|
|
15353
15547
|
}
|
|
15354
15548
|
}, 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) => {
|
|
15355
15549
|
try {
|
|
15356
|
-
return
|
|
15550
|
+
return statSync20(p).size <= MAX_FILE_BYTES ? readFileSync51(p, "utf8") : "";
|
|
15357
15551
|
} catch {
|
|
15358
15552
|
return "";
|
|
15359
15553
|
}
|
|
@@ -15369,8 +15563,8 @@ var init_supply_guard = __esm(() => {
|
|
|
15369
15563
|
|
|
15370
15564
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
15371
15565
|
import { createHash as createHash17 } from "crypto";
|
|
15372
|
-
import { existsSync as
|
|
15373
|
-
import { basename as basename22, isAbsolute as
|
|
15566
|
+
import { existsSync as existsSync45, readdirSync as readdirSync21, mkdirSync as mkdirSync30, readFileSync as readFileSync52, writeFileSync as writeFileSync32 } from "fs";
|
|
15567
|
+
import { basename as basename22, isAbsolute as isAbsolute11, join as join67, relative as relative10 } from "path";
|
|
15374
15568
|
function sanitizeReason(s) {
|
|
15375
15569
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
15376
15570
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
@@ -15380,7 +15574,7 @@ function receiptSha256(r) {
|
|
|
15380
15574
|
const { verification: _v, receipt_sha256: _h, ...body } = r;
|
|
15381
15575
|
return sha2562(canonicalJson(body));
|
|
15382
15576
|
}
|
|
15383
|
-
async function
|
|
15577
|
+
async function git4(ctx, args, keep = {}) {
|
|
15384
15578
|
const r = await safeGitRun(ctx.repoDir, args, { timeoutMs: 20000, ...keep });
|
|
15385
15579
|
return { out: r.stdout, code: r.exitCode };
|
|
15386
15580
|
}
|
|
@@ -15447,7 +15641,7 @@ function renderReceiptMd(r) {
|
|
|
15447
15641
|
}
|
|
15448
15642
|
var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", AGENT_ONLY_REASON = "only agent-authored tests executed (no agent-independent proof)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash17("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) => {
|
|
15449
15643
|
const r = relative10(ctx.repoDir, join67(ctx.runDir, "receipt.json"));
|
|
15450
|
-
return r.startsWith("..") ||
|
|
15644
|
+
return r.startsWith("..") || isAbsolute11(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
15451
15645
|
}, commitStage, wallGreenOnBaseOf = (o) => {
|
|
15452
15646
|
const b = o.wall?.base_run ?? {};
|
|
15453
15647
|
return typeof b.pass === "number" && b.pass > 0 && b.fail === 0 && (typeof b.not_run === "number" ? b.not_run : 0) === 0;
|
|
@@ -15484,30 +15678,30 @@ var init_seal = __esm(() => {
|
|
|
15484
15678
|
name: "commit",
|
|
15485
15679
|
...STAGE_BUDGETS.commit,
|
|
15486
15680
|
async run(ctx) {
|
|
15487
|
-
if (!ctx.baseSha || (await
|
|
15681
|
+
if (!ctx.baseSha || (await git4(ctx, ["rev-parse", "--verify", "-q", `${ctx.baseSha}^{commit}`])).code !== 0)
|
|
15488
15682
|
return { status: "failed", data: {}, reason: "base commit not resolvable" };
|
|
15489
|
-
if ((await
|
|
15683
|
+
if ((await git4(ctx, ["add", "-A", "--", "."], { repoDrivers: true })).code !== 0)
|
|
15490
15684
|
return { status: "failed", data: {}, reason: "git add failed" };
|
|
15491
|
-
const sd = await
|
|
15685
|
+
const sd = await git4(ctx, ["diff", "--cached", "--name-status", "--no-renames", "-z", ctx.baseSha]);
|
|
15492
15686
|
if (sd.code !== 0)
|
|
15493
15687
|
return { status: "failed", data: {}, reason: "git diff against base failed" };
|
|
15494
15688
|
const staged = parseStaged(sd.out);
|
|
15495
15689
|
const drop = dropSet(ctx.repoDir, staged, ctx.outputs().intake?.preexisting_dirty);
|
|
15496
|
-
const sat = await discardIfSatisfied((a) =>
|
|
15690
|
+
const sat = await discardIfSatisfied((a) => git4(ctx, a), ctx.baseSha, ctx.outputs(), staged, new Set(drop.filter(({ st }) => st === "L").map(({ f }) => f)), ctx.repoDir);
|
|
15497
15691
|
if (sat)
|
|
15498
15692
|
return sat;
|
|
15499
|
-
if (drop.length > 0 && (await
|
|
15693
|
+
if (drop.length > 0 && (await git4(ctx, ["--literal-pathspecs", "reset", "-q", ctx.baseSha, "--", ...drop.map(({ f }) => f)])).code !== 0)
|
|
15500
15694
|
return { status: "failed", data: {}, reason: "git reset failed" };
|
|
15501
15695
|
const dropped2 = new Set(drop.map(({ f }) => f)), notes = flagOutsideScope(ctx.outputs(), staged.filter(({ f }) => !dropped2.has(f)));
|
|
15502
|
-
if ((await
|
|
15696
|
+
if ((await git4(ctx, ["diff", "--cached", "--quiet"])).code === 0)
|
|
15503
15697
|
return { status: "completed", data: { committed: false, scope_notes: notes } };
|
|
15504
15698
|
const title = (str3(ctx.outputs().intake?.title) ?? `run ${ctx.runId}`).split(`
|
|
15505
15699
|
`)[0].slice(0, 72);
|
|
15506
|
-
const c = await
|
|
15700
|
+
const c = await git4(ctx, ["commit", "-q", "-m", `loki: ${title}`, "-m", [`Loki-Run: ${ctx.runId}`, ...aiTrailers({ runId: ctx.runId, provider: ctx.provider, model: ctx.model, receiptRel: receiptRel(ctx) })].join(`
|
|
15507
15701
|
`)], { repoDrivers: true, userHooks: true });
|
|
15508
15702
|
if (c.code !== 0)
|
|
15509
15703
|
return { status: "failed", data: {}, reason: "git commit failed" };
|
|
15510
|
-
return { status: "completed", data: { committed: true, head_sha: (await
|
|
15704
|
+
return { status: "completed", data: { committed: true, head_sha: (await git4(ctx, ["rev-parse", "HEAD"])).out.trim(), scope_notes: notes } };
|
|
15511
15705
|
}
|
|
15512
15706
|
};
|
|
15513
15707
|
sealStage = {
|
|
@@ -15517,8 +15711,8 @@ var init_seal = __esm(() => {
|
|
|
15517
15711
|
const o = ctx.outputs(), stale = staleAtSeal(ctx.repoDir, ctx);
|
|
15518
15712
|
if (stale.length)
|
|
15519
15713
|
return { status: "failed", data: { stale_wall_revisions: stale.map((r) => r.rev) }, reason: staleReason(stale) };
|
|
15520
|
-
const head = (await
|
|
15521
|
-
const tree = (await
|
|
15714
|
+
const head = (await git4(ctx, ["rev-parse", "HEAD"])).out.trim();
|
|
15715
|
+
const tree = (await git4(ctx, ["rev-parse", "HEAD^{tree}"])).out.trim();
|
|
15522
15716
|
const diff = await safeGitRun(ctx.repoDir, ["diff-tree", "-r", "-z", "--raw", "--no-renames", "--no-abbrev", "-O/dev/null", ctx.baseSha, head, "--", ".", EXCLUDE_LOKI], { timeoutMs: 20000 });
|
|
15523
15717
|
const diffOk = diff.exitCode === 0 && /^[0-9a-f]{40,64}$/.test(head);
|
|
15524
15718
|
const verifyNotProven = strs2(o.verify?.not_proven);
|
|
@@ -15526,7 +15720,7 @@ var init_seal = __esm(() => {
|
|
|
15526
15720
|
const wallNotRun = typeof base.not_run === "number" ? base.not_run : 0;
|
|
15527
15721
|
const wallGreenOnBase = wallGreenOnBaseOf(o);
|
|
15528
15722
|
const preRedChecks = strs2(o.verify?.pre_red_checks);
|
|
15529
|
-
const rawDiff = diffOk ? diff.stdout.split("\x00").filter(Boolean) : [], pctx = { addedFiles: rawDiff.flatMap((m, i) => i % 2 === 0 && m.trim().split(" ").pop() === "A" && rawDiff[i + 1] ? [rawDiff[i + 1]] : []), wallFiles: Array.isArray(o.wall?.files) ? o.wall.files.map((f) => String(f.path)) : [] }, checks = checksOf(o.verify?.checks, pctx);
|
|
15723
|
+
const rawDiff = diffOk ? diff.stdout.split("\x00").filter(Boolean) : [], pctx = { addedFiles: rawDiff.flatMap((m, i) => i % 2 === 0 && m.trim().split(" ").pop() === "A" && rawDiff[i + 1] ? [rawDiff[i + 1]] : []), wallFiles: Array.isArray(o.wall?.files) ? o.wall.files.map((f) => String(f.path)) : [], modifiedFiles: rawDiff.flatMap((m, i) => i % 2 === 0 && m.trim().split(" ").pop() !== "A" && rawDiff[i + 1] && isTestFile(rawDiff[i + 1]) ? [rawDiff[i + 1]] : []) }, checks = checksOf(o.verify?.checks, pctx);
|
|
15530
15724
|
const weakTests = [];
|
|
15531
15725
|
for (let i = 0;i + 1 < rawDiff.length; i += 2)
|
|
15532
15726
|
if ((rawDiff[i].trim().split(" ").pop() ?? "") !== "A" && isTestFile(rawDiff[i + 1]))
|
|
@@ -15676,7 +15870,7 @@ var init_seal = __esm(() => {
|
|
|
15676
15870
|
provider: ctx.provider,
|
|
15677
15871
|
model: ctx.model,
|
|
15678
15872
|
resumed: o.intake?.resumed === true,
|
|
15679
|
-
events_sha256: sha2562(
|
|
15873
|
+
events_sha256: sha2562(existsSync45(eventsPath) ? readFileSync52(eventsPath) : ""),
|
|
15680
15874
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
15681
15875
|
log_seal: true,
|
|
15682
15876
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -15697,11 +15891,11 @@ var init_seal = __esm(() => {
|
|
|
15697
15891
|
hash = receiptSha256(body);
|
|
15698
15892
|
}
|
|
15699
15893
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
15700
|
-
|
|
15894
|
+
mkdirSync30(ctx.runDir, { recursive: true });
|
|
15701
15895
|
const path = join67(ctx.runDir, "receipt.json");
|
|
15702
|
-
|
|
15896
|
+
writeFileSync32(path, JSON.stringify(receipt, null, 2) + `
|
|
15703
15897
|
`);
|
|
15704
|
-
|
|
15898
|
+
writeFileSync32(join67(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
15705
15899
|
recordRun(process.env, ctx.repoDir, ranModel(o.implement, ctx.model, o.fix), verdict, cost.usd, cost.unmetered === true, totalS);
|
|
15706
15900
|
try {
|
|
15707
15901
|
hooks.sealed?.(ctx.runDir, JSON.parse(JSON.stringify(receipt), (_k, v) => v && typeof v === "object" ? Object.freeze(v) : v));
|
|
@@ -15727,7 +15921,7 @@ var init_seal = __esm(() => {
|
|
|
15727
15921
|
|
|
15728
15922
|
// ../../loki-ts/src/features/receipt_dsse.ts
|
|
15729
15923
|
import { createPublicKey as createPublicKey3, sign as sign2, verify as verify3 } from "crypto";
|
|
15730
|
-
import { readFileSync as
|
|
15924
|
+
import { readFileSync as readFileSync53 } from "fs";
|
|
15731
15925
|
function pae(payloadType, body) {
|
|
15732
15926
|
const t = Buffer.from(payloadType, "utf8");
|
|
15733
15927
|
return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
|
|
@@ -15804,7 +15998,7 @@ function verifyDsseReceipt(env, d) {
|
|
|
15804
15998
|
}
|
|
15805
15999
|
function runIdGuard(receiptPath, runId) {
|
|
15806
16000
|
try {
|
|
15807
|
-
return envelopeRunIdProblem(JSON.parse(
|
|
16001
|
+
return envelopeRunIdProblem(JSON.parse(readFileSync53(receiptPath, "utf8")), runId);
|
|
15808
16002
|
} catch {
|
|
15809
16003
|
return null;
|
|
15810
16004
|
}
|
|
@@ -15813,14 +16007,14 @@ var DSSE_PAYLOAD_TYPE = "application/vnd.in-toto+json", STATEMENT_TYPE = "https:
|
|
|
15813
16007
|
var init_receipt_dsse = () => {};
|
|
15814
16008
|
|
|
15815
16009
|
// ../../loki-ts/src/util/composite_seal.ts
|
|
15816
|
-
import { existsSync as
|
|
15817
|
-
import { basename as basename23, dirname as
|
|
16010
|
+
import { existsSync as existsSync46, readFileSync as readFileSync54 } from "fs";
|
|
16011
|
+
import { basename as basename23, dirname as dirname30, join as join68 } from "path";
|
|
15818
16012
|
function logProblem(runDir, integ) {
|
|
15819
16013
|
if (typeof integ["log_sha256"] !== "string")
|
|
15820
16014
|
return null;
|
|
15821
16015
|
let b;
|
|
15822
16016
|
try {
|
|
15823
|
-
b =
|
|
16017
|
+
b = readFileSync54(join68(runDir, "integration.log"));
|
|
15824
16018
|
} catch {
|
|
15825
16019
|
return "integration.log is missing but integration.json records its hash";
|
|
15826
16020
|
}
|
|
@@ -15835,7 +16029,7 @@ function integrationProblem(integ) {
|
|
|
15835
16029
|
return null;
|
|
15836
16030
|
}
|
|
15837
16031
|
async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
15838
|
-
const runDir =
|
|
16032
|
+
const runDir = dirname30(receiptPath);
|
|
15839
16033
|
const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
|
|
15840
16034
|
const bad = (reason) => ({ problem: { verdict: "TAMPERED", reason: `composite: ${reason}` }, verdict: "FAILED" });
|
|
15841
16035
|
const kids = receipt["children"];
|
|
@@ -15843,7 +16037,7 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
15843
16037
|
return bad("body malformed");
|
|
15844
16038
|
let ibytes;
|
|
15845
16039
|
try {
|
|
15846
|
-
ibytes =
|
|
16040
|
+
ibytes = readFileSync54(join68(runDir, "integration.json"));
|
|
15847
16041
|
} catch {
|
|
15848
16042
|
return bad("integration.json missing");
|
|
15849
16043
|
}
|
|
@@ -15882,7 +16076,7 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
15882
16076
|
const rp = join68(runDir, rel);
|
|
15883
16077
|
let bytes;
|
|
15884
16078
|
try {
|
|
15885
|
-
bytes =
|
|
16079
|
+
bytes = readFileSync54(rp);
|
|
15886
16080
|
} catch {
|
|
15887
16081
|
return bad(`child ${repo} receipt missing`);
|
|
15888
16082
|
}
|
|
@@ -15931,8 +16125,8 @@ var init_keys_cmd = __esm(() => {
|
|
|
15931
16125
|
});
|
|
15932
16126
|
|
|
15933
16127
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
15934
|
-
import { existsSync as
|
|
15935
|
-
import { basename as basename24, dirname as
|
|
16128
|
+
import { existsSync as existsSync47, readFileSync as readFileSync55, renameSync as renameSync14, statSync as statSync22, writeFileSync as writeFileSync33 } from "fs";
|
|
16129
|
+
import { basename as basename24, dirname as dirname31, join as join69 } from "path";
|
|
15936
16130
|
import { createHash as createHash18, createPublicKey as createPublicKey4, verify as verify4 } from "crypto";
|
|
15937
16131
|
function pubFor(kid, given) {
|
|
15938
16132
|
if (given)
|
|
@@ -15940,7 +16134,7 @@ function pubFor(kid, given) {
|
|
|
15940
16134
|
const active = loadSigningKey(false);
|
|
15941
16135
|
const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
|
|
15942
16136
|
try {
|
|
15943
|
-
return [createPublicKey4(
|
|
16137
|
+
return [createPublicKey4(readFileSync55(f))];
|
|
15944
16138
|
} catch {
|
|
15945
16139
|
return [];
|
|
15946
16140
|
}
|
|
@@ -15984,10 +16178,10 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
15984
16178
|
const claims = st !== null && typeof st === "object" && Object.keys(st).length > 0;
|
|
15985
16179
|
return claims ? "the receipt records stages but binds the empty-log hash, so no events.jsonl can back it (missing, empty or non-empty); the receipt is forged or was sealed without a log. Re-run the task to produce a fresh receipt" : null;
|
|
15986
16180
|
}
|
|
15987
|
-
const path = join69(
|
|
15988
|
-
if (!
|
|
16181
|
+
const path = join69(dirname31(receiptPath), "events.jsonl");
|
|
16182
|
+
if (!existsSync47(path))
|
|
15989
16183
|
return "events.jsonl is missing";
|
|
15990
|
-
const raw2 =
|
|
16184
|
+
const raw2 = readFileSync55(path, "utf8"), events2 = readEvents(path), lines2 = raw2.split(`
|
|
15991
16185
|
`).filter((l) => l.trim() !== "");
|
|
15992
16186
|
if (events2.length !== lines2.length || events2.some((e, i) => e.seq !== i))
|
|
15993
16187
|
return "events.jsonl has a forged, edited or missing line";
|
|
@@ -16003,7 +16197,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
16003
16197
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
16004
16198
|
}
|
|
16005
16199
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
16006
|
-
const path = join69(
|
|
16200
|
+
const path = join69(dirname31(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
16007
16201
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
16008
16202
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
16009
16203
|
if (done < 0)
|
|
@@ -16011,7 +16205,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
16011
16205
|
const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
|
|
16012
16206
|
if (!d)
|
|
16013
16207
|
return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
|
|
16014
|
-
const lines2 =
|
|
16208
|
+
const lines2 = readFileSync55(path, "utf8").split(`
|
|
16015
16209
|
`).filter((l) => l.trim() !== ""), sha4 = createHash18("sha256").update(lines2.slice(0, done + 1).join(`
|
|
16016
16210
|
`) + `
|
|
16017
16211
|
`).digest("hex");
|
|
@@ -16021,11 +16215,11 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
16021
16215
|
return null;
|
|
16022
16216
|
}
|
|
16023
16217
|
async function verifyReceipt(receiptPath, deps = {}) {
|
|
16024
|
-
if (!
|
|
16218
|
+
if (!existsSync47(receiptPath))
|
|
16025
16219
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
16026
16220
|
let receipt;
|
|
16027
16221
|
try {
|
|
16028
|
-
receipt = JSON.parse((deps.read ?? ((p) =>
|
|
16222
|
+
receipt = JSON.parse((deps.read ?? ((p) => readFileSync55(p, "utf8")))(receiptPath));
|
|
16029
16223
|
} catch {
|
|
16030
16224
|
return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
|
|
16031
16225
|
}
|
|
@@ -16114,8 +16308,8 @@ var init_verify_cmd = __esm(() => {
|
|
|
16114
16308
|
});
|
|
16115
16309
|
|
|
16116
16310
|
// src/server/app.ts
|
|
16117
|
-
import { existsSync as
|
|
16118
|
-
import { join as join72, resolve as
|
|
16311
|
+
import { existsSync as existsSync49, readFileSync as readFileSync58, statSync as statSync23 } from "fs";
|
|
16312
|
+
import { join as join72, resolve as resolve15, sep as sep15 } from "path";
|
|
16119
16313
|
|
|
16120
16314
|
// node_modules/hono/dist/request/constants.js
|
|
16121
16315
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -26397,9 +26591,9 @@ function mount20(ctx) {
|
|
|
26397
26591
|
}
|
|
26398
26592
|
|
|
26399
26593
|
// src/server/routes/verify.ts
|
|
26400
|
-
import { readFileSync as
|
|
26594
|
+
import { readFileSync as readFileSync56, realpathSync as realpathSync24 } from "fs";
|
|
26401
26595
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
26402
|
-
import { join as join70, sep as
|
|
26596
|
+
import { join as join70, sep as sep14 } from "path";
|
|
26403
26597
|
init_verify_cmd();
|
|
26404
26598
|
init_seal();
|
|
26405
26599
|
init_receipt_dsse();
|
|
@@ -26426,15 +26620,15 @@ function mount21(ctx) {
|
|
|
26426
26620
|
return done(404, "receipt not available", "not_found");
|
|
26427
26621
|
let runDir, file;
|
|
26428
26622
|
try {
|
|
26429
|
-
const root =
|
|
26430
|
-
runDir =
|
|
26623
|
+
const root = realpathSync24(join70(repo.p, ".loki", "runs"));
|
|
26624
|
+
runDir = realpathSync24(join70(root, run2));
|
|
26431
26625
|
if (runDir !== join70(root, run2))
|
|
26432
26626
|
return done(404, "receipt not found", "not_found");
|
|
26433
|
-
file =
|
|
26627
|
+
file = realpathSync24(join70(runDir, "receipt.json"));
|
|
26434
26628
|
} catch {
|
|
26435
26629
|
return done(404, "receipt not found", "not_found");
|
|
26436
26630
|
}
|
|
26437
|
-
if (!file.startsWith(runDir +
|
|
26631
|
+
if (!file.startsWith(runDir + sep14))
|
|
26438
26632
|
return done(404, "receipt not found", "not_found");
|
|
26439
26633
|
let integrity, reasons, sha4 = null, outcome = "UNREADABLE";
|
|
26440
26634
|
try {
|
|
@@ -26449,7 +26643,7 @@ function mount21(ctx) {
|
|
|
26449
26643
|
sha4 = r.receiptSha256 ?? null;
|
|
26450
26644
|
}
|
|
26451
26645
|
try {
|
|
26452
|
-
outcome = outcomeOf(JSON.parse(
|
|
26646
|
+
outcome = outcomeOf(JSON.parse(readFileSync56(file, "utf8")));
|
|
26453
26647
|
} catch {
|
|
26454
26648
|
outcome = "UNREADABLE";
|
|
26455
26649
|
}
|
|
@@ -26511,7 +26705,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
26511
26705
|
}
|
|
26512
26706
|
|
|
26513
26707
|
// src/server/legacy/shim.ts
|
|
26514
|
-
import { existsSync as
|
|
26708
|
+
import { existsSync as existsSync48, readFileSync as readFileSync57 } from "fs";
|
|
26515
26709
|
import { join as join71 } from "path";
|
|
26516
26710
|
|
|
26517
26711
|
// src/server/legacy/routes.ts
|
|
@@ -26892,8 +27086,8 @@ var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
|
26892
27086
|
var repoVersion = () => {
|
|
26893
27087
|
for (const f of [join71(import.meta.dir, "../../../../../VERSION")]) {
|
|
26894
27088
|
try {
|
|
26895
|
-
if (
|
|
26896
|
-
return
|
|
27089
|
+
if (existsSync48(f))
|
|
27090
|
+
return readFileSync57(f, "utf8").trim();
|
|
26897
27091
|
} catch {}
|
|
26898
27092
|
}
|
|
26899
27093
|
return "unknown";
|
|
@@ -27075,7 +27269,7 @@ function legacyShim(opts) {
|
|
|
27075
27269
|
|
|
27076
27270
|
// src/server/app.ts
|
|
27077
27271
|
var MAX_BODY2 = 1e6;
|
|
27078
|
-
var defaultUiDir = () => [join72(import.meta.dir, "../../ui/dist"), join72(import.meta.dir, "../ui/dist")].find((d) =>
|
|
27272
|
+
var defaultUiDir = () => [join72(import.meta.dir, "../../ui/dist"), join72(import.meta.dir, "../ui/dist")].find((d) => existsSync49(join72(d, "index.html")));
|
|
27079
27273
|
function createApp(opts) {
|
|
27080
27274
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
27081
27275
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -27107,7 +27301,7 @@ function createApp(opts) {
|
|
|
27107
27301
|
for (const f of [process.env.LOKI_VERSION_FILE, join72(import.meta.dir, "../../../../VERSION")]) {
|
|
27108
27302
|
try {
|
|
27109
27303
|
if (f)
|
|
27110
|
-
return
|
|
27304
|
+
return readFileSync58(f, "utf8").trim() || "unknown";
|
|
27111
27305
|
} catch {}
|
|
27112
27306
|
}
|
|
27113
27307
|
return "unknown";
|
|
@@ -27216,9 +27410,9 @@ function createApp(opts) {
|
|
|
27216
27410
|
}
|
|
27217
27411
|
if (!uiDir || p.startsWith("/v1/"))
|
|
27218
27412
|
return c.json({ error: "not found" }, 404);
|
|
27219
|
-
const root =
|
|
27220
|
-
const f =
|
|
27221
|
-
if (f.startsWith(root +
|
|
27413
|
+
const root = resolve15(uiDir);
|
|
27414
|
+
const f = resolve15(root, `.${p}`);
|
|
27415
|
+
if (f.startsWith(root + sep15) && existsSync49(f) && statSync23(f).isFile())
|
|
27222
27416
|
return new Response(Bun.file(f));
|
|
27223
27417
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
27224
27418
|
return c.json({ error: "not found" }, 404);
|