loki-mode 11.6.0 → 11.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/backlog.py +11 -2
- package/autonomy/lib/chat.sh +57 -1
- package/autonomy/lib/loki_yaml.py +8 -2
- package/autonomy/lib/workspace.py +267 -11
- package/autonomy/loki +315 -21
- package/autonomy/trigger-server.py +185 -2
- package/autonomy/verify.sh +4 -0
- package/completions/_loki +4 -0
- package/completions/loki.bash +20 -2
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +2 -1
- package/docs/DEPLOY-PLAN.md +17 -8
- package/docs/EVALUATING.md +8 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +15 -2
- package/docs/dev/architecture-reference.md +8 -0
- package/docs/sentry-intake.md +11 -0
- package/docs/v10/CLI-MODERN.md +1 -1
- package/loki-ts/dist/loki.js +1009 -975
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +618 -469
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/schemas/loki-yaml.schema.json +5 -2
- package/src/policies/engine.js +41 -2
- package/src/policies/types.js +49 -0
- package/web-app/dist/assets/{AdminPage-DhXdJNQk.js → AdminPage-S3BYeapm.js} +1 -1
- package/web-app/dist/assets/{Avatar-ixVj76fM.js → Avatar-C1rHwLgn.js} +1 -1
- package/web-app/dist/assets/{Badge-BGMSp22s.js → Badge-CzlnN5aQ.js} +1 -1
- package/web-app/dist/assets/{Button-pYJc32tG.js → Button-B1w5AeGC.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-C1JBBouA.js → CockpitPage-Dyeqjfkc.js} +1 -1
- package/web-app/dist/assets/{ComparePage-qD0NV4RK.js → ComparePage-C6y2GkGR.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-C-yxO58j.js → ErrorBoundary-Dg2fcGkR.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-ByPzuE1S.js → EvidenceReceiptPanel-CxbyjvTj.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-BNv_2BEJ.js → GitHubIssuesPanel-Dpys1xDb.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CuLZQ9CS.js → GitHubPRsPanel-CRVjWoFl.js} +1 -1
- package/web-app/dist/assets/{HomePage-CS_FW8Je.js → HomePage-BKnv-oYw.js} +2 -2
- package/web-app/dist/assets/{LoginPage-DwiNf1Wm.js → LoginPage-BGksmFRd.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CmQybLPZ.js → MagicPage-OCvhp16M.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-D9ERdpv8.js → MetricsPage-CJOJpXWC.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D9ccgdbF.js → NotFoundPage-C04p3QvN.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-B9Jslmgv.js → ProjectPage-BRB8txsY.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DnU99Go2.js → ProjectsPage-CoUYeQEh.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DcTyx6wo.js → SettingsPage-BkjmJGfM.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-jEY9ghSr.js → ShowcasePage-B4vU9TjQ.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-CSGltW-8.js → SystemSettingsPage-Z2jiVsni.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-gUuJXb9l.js → TeamsPage-Ulm8SISq.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-37Zhe--v.js → TemplatesPage-C_7MM6Ey.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-B09hRwaR.js → TerminalOutput-Cumd7Tgs.js} +1 -1
- package/web-app/dist/assets/{activity-q_4MY9J0.js → activity-CZAUkbiY.js} +1 -1
- package/web-app/dist/assets/{bell-X8OC_PFP.js → bell-BOvSXpEr.js} +1 -1
- package/web-app/dist/assets/{bot-C-N1PUOD.js → bot-DOTZxBYj.js} +1 -1
- package/web-app/dist/assets/{check-B2c76Jmp.js → check-BxpE0B5u.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BF76YLNw.js → chevron-left-D6KPvBOA.js} +1 -1
- package/web-app/dist/assets/{circle-alert-AeFKBD8h.js → circle-alert-Bajq86oe.js} +1 -1
- package/web-app/dist/assets/{clock-CiXRf-FH.js → clock-CnlHAVJc.js} +1 -1
- package/web-app/dist/assets/{cloud-DKQ8SRJ5.js → cloud-DjaymjwM.js} +1 -1
- package/web-app/dist/assets/{code-xml-DEb6jXWP.js → code-xml-BADEw2Ld.js} +1 -1
- package/web-app/dist/assets/{database-6W7e-E9c.js → database-B4aKb5Cq.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-Ds2GOuyW.js → dollar-sign-DrldXpja.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CZU0jg52.js → file-code-corner-DVebDVR-.js} +1 -1
- package/web-app/dist/assets/{file-plus-DTVYnXCN.js → file-plus-BD-N_-Tl.js} +1 -1
- package/web-app/dist/assets/{globe-0mUzKYDe.js → globe-C7ieInC-.js} +1 -1
- package/web-app/dist/assets/{hammer-BMHZg3YX.js → hammer-BSugKIji.js} +1 -1
- package/web-app/dist/assets/{index-BvOTvjax.js → index-nLQgfvOn.js} +3 -3
- package/web-app/dist/assets/{layers-Df7TdcWB.js → layers-CHXxU2on.js} +1 -1
- package/web-app/dist/assets/{loader-circle-kPsVfrSY.js → loader-circle-BEEz0LbD.js} +1 -1
- package/web-app/dist/assets/{lock-CXYcbEc5.js → lock-CzQGfcnE.js} +1 -1
- package/web-app/dist/assets/{package-Cqhua1ID.js → package-CcHYQLGd.js} +1 -1
- package/web-app/dist/assets/{plus-lseXhsn-.js → plus-DdzgFEXn.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-OZriI6gD.js → refresh-cw-7x0HbH9Y.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CKCfjPg8.js → rotate-ccw-DBahX1Sp.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DoVyyfKb.js → scroll-text-BB8y8a9Z.js} +1 -1
- package/web-app/dist/assets/{server-DGy7TflW.js → server-CfkbW2MH.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BCzRcMXi.js → shield-alert-DUgD8rBF.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-C_znJd5g.js → thumbs-up-DGoyY6QC.js} +1 -1
- package/web-app/dist/assets/{trash-2-BosCbeeA.js → trash-2-B6xrSUMr.js} +1 -1
- package/web-app/dist/assets/{trending-down-CkR5zDkV.js → trending-down-B6RGKTOx.js} +1 -1
- package/web-app/dist/assets/{trending-up-DpUIfOH4.js → trending-up-B44DCrt1.js} +1 -1
- package/web-app/dist/assets/{upload-CH9aKc8L.js → upload-4kWm33ZY.js} +1 -1
- package/web-app/dist/assets/{usePolling-pzxrHJeg.js → usePolling-zkUtRQG6.js} +1 -1
- package/web-app/dist/assets/{user-B7OHa-VQ.js → user-04cEVIe2.js} +1 -1
- package/web-app/dist/index.html +1 -1
|
@@ -7674,6 +7674,74 @@ var init_mutation_proof = __esm(() => {
|
|
|
7674
7674
|
DEP_DIRS = ["node_modules", ".venv", "venv"];
|
|
7675
7675
|
});
|
|
7676
7676
|
|
|
7677
|
+
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
7678
|
+
import { closeSync as closeSync5, constants, fstatSync as fstatSync4, lstatSync as lstatSync5, openSync as openSync5, readSync as readSync4, realpathSync as realpathSync14 } from "fs";
|
|
7679
|
+
function readBounded(path) {
|
|
7680
|
+
const l = lstatSync5(path);
|
|
7681
|
+
if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
|
|
7682
|
+
return null;
|
|
7683
|
+
const fd = openSync5(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
7684
|
+
try {
|
|
7685
|
+
const st = fstatSync4(fd);
|
|
7686
|
+
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
7687
|
+
return null;
|
|
7688
|
+
const buf = Buffer.alloc(st.size + 1);
|
|
7689
|
+
let n2 = 0;
|
|
7690
|
+
for (;; ) {
|
|
7691
|
+
const r = readSync4(fd, buf, n2, buf.length - n2, null);
|
|
7692
|
+
if (r <= 0)
|
|
7693
|
+
break;
|
|
7694
|
+
n2 += r;
|
|
7695
|
+
if (n2 >= buf.length)
|
|
7696
|
+
return null;
|
|
7697
|
+
}
|
|
7698
|
+
return buf.subarray(0, n2).toString("utf8");
|
|
7699
|
+
} finally {
|
|
7700
|
+
closeSync5(fd);
|
|
7701
|
+
}
|
|
7702
|
+
}
|
|
7703
|
+
function parseText(txt) {
|
|
7704
|
+
try {
|
|
7705
|
+
const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
|
|
7706
|
+
if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
|
|
7707
|
+
return null;
|
|
7708
|
+
const acc = j.acceptance === undefined ? [] : strs(j.acceptance);
|
|
7709
|
+
if (acc === null)
|
|
7710
|
+
return null;
|
|
7711
|
+
return { id: j.id, writeSet: ws, pack, tokenBudget: tb, ...acc.length ? { acceptance: acc.map((a) => a.slice(0, 500)).slice(0, 50) } : {} };
|
|
7712
|
+
} catch {
|
|
7713
|
+
return null;
|
|
7714
|
+
}
|
|
7715
|
+
}
|
|
7716
|
+
function parseSpec(path) {
|
|
7717
|
+
try {
|
|
7718
|
+
const txt = readBounded(path);
|
|
7719
|
+
return txt === null ? null : parseText(txt);
|
|
7720
|
+
} catch {
|
|
7721
|
+
return null;
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
function unitSpec(env = process.env) {
|
|
7725
|
+
const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
|
|
7726
|
+
if (env["LOKI_SPEED"] === "0" || !path)
|
|
7727
|
+
return null;
|
|
7728
|
+
if (frozen !== undefined)
|
|
7729
|
+
return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
|
|
7730
|
+
if (!memo.has(path))
|
|
7731
|
+
memo.set(path, parseSpec(path));
|
|
7732
|
+
return memo.get(path) ?? null;
|
|
7733
|
+
}
|
|
7734
|
+
function unitFlags(staged, env = process.env, pre = {}) {
|
|
7735
|
+
const s = unitSpec(env);
|
|
7736
|
+
return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
|
|
7737
|
+
}
|
|
7738
|
+
var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm2 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm2) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
|
|
7739
|
+
var init_unit_mode = __esm(() => {
|
|
7740
|
+
MAX_SPEC_BYTES = 1024 * 1024;
|
|
7741
|
+
CTRL = /[\x00-\x1f\x7f]/;
|
|
7742
|
+
memo = new Map;
|
|
7743
|
+
});
|
|
7744
|
+
|
|
7677
7745
|
// ../../loki-ts/src/features/lean_prefix.ts
|
|
7678
7746
|
var LEAN_PREFIX, STAGE_PREFIX, withStagePrefix = (brief) => process.env["LOKI_SPEED"] !== "0" ? `${STAGE_PREFIX}
|
|
7679
7747
|
|
|
@@ -7683,10 +7751,216 @@ var init_lean_prefix = __esm(() => {
|
|
|
7683
7751
|
STAGE_PREFIX = "You are Loki, an autonomous coding agent working one stage of a run. " + "Follow the stage prompt exactly, including the role it gives you and the rules it sets, " + "and report only what you did. " + "Stage-specific instructions and the task follow this block.";
|
|
7684
7752
|
});
|
|
7685
7753
|
|
|
7686
|
-
// ../../loki-ts/src/
|
|
7754
|
+
// ../../loki-ts/src/util/shell.ts
|
|
7755
|
+
async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
|
|
7756
|
+
const reader = stream.getReader();
|
|
7757
|
+
const decoder = new TextDecoder;
|
|
7758
|
+
let out2 = "";
|
|
7759
|
+
let total = 0;
|
|
7760
|
+
try {
|
|
7761
|
+
while (total < maxBytes) {
|
|
7762
|
+
const { done, value } = await reader.read();
|
|
7763
|
+
if (done)
|
|
7764
|
+
break;
|
|
7765
|
+
if (!value)
|
|
7766
|
+
continue;
|
|
7767
|
+
total += value.byteLength;
|
|
7768
|
+
if (total > maxBytes) {
|
|
7769
|
+
const keep = value.byteLength - (total - maxBytes);
|
|
7770
|
+
out2 += decoder.decode(value.subarray(0, keep), { stream: true });
|
|
7771
|
+
break;
|
|
7772
|
+
}
|
|
7773
|
+
out2 += decoder.decode(value, { stream: true });
|
|
7774
|
+
}
|
|
7775
|
+
out2 += decoder.decode();
|
|
7776
|
+
} finally {
|
|
7777
|
+
try {
|
|
7778
|
+
await reader.cancel();
|
|
7779
|
+
} catch {}
|
|
7780
|
+
reader.releaseLock();
|
|
7781
|
+
}
|
|
7782
|
+
return out2;
|
|
7783
|
+
}
|
|
7784
|
+
async function run(argv, opts = {}) {
|
|
7785
|
+
const proc = Bun.spawn({
|
|
7786
|
+
cmd: [...argv],
|
|
7787
|
+
stdout: "pipe",
|
|
7788
|
+
stderr: "pipe",
|
|
7789
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
7790
|
+
cwd: opts.cwd
|
|
7791
|
+
});
|
|
7792
|
+
let timer;
|
|
7793
|
+
let killTimer;
|
|
7794
|
+
if (opts.timeoutMs && opts.timeoutMs > 0) {
|
|
7795
|
+
timer = setTimeout(() => {
|
|
7796
|
+
try {
|
|
7797
|
+
proc.kill("SIGTERM");
|
|
7798
|
+
} catch {}
|
|
7799
|
+
killTimer = setTimeout(() => {
|
|
7800
|
+
try {
|
|
7801
|
+
proc.kill("SIGKILL");
|
|
7802
|
+
} catch {}
|
|
7803
|
+
}, 2000);
|
|
7804
|
+
}, opts.timeoutMs);
|
|
7805
|
+
}
|
|
7806
|
+
try {
|
|
7807
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
7808
|
+
readStreamCapped(proc.stdout),
|
|
7809
|
+
new Response(proc.stderr).text(),
|
|
7810
|
+
proc.exited
|
|
7811
|
+
]);
|
|
7812
|
+
return { stdout, stderr, exitCode };
|
|
7813
|
+
} finally {
|
|
7814
|
+
if (timer)
|
|
7815
|
+
clearTimeout(timer);
|
|
7816
|
+
if (killTimer)
|
|
7817
|
+
clearTimeout(killTimer);
|
|
7818
|
+
}
|
|
7819
|
+
}
|
|
7820
|
+
var MAX_STDOUT_BYTES;
|
|
7821
|
+
var init_shell = __esm(() => {
|
|
7822
|
+
MAX_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
7823
|
+
});
|
|
7824
|
+
|
|
7825
|
+
// ../../loki-ts/src/util/python.ts
|
|
7826
|
+
import { accessSync, constants as constants2, existsSync as existsSync16, statSync as statSync12 } from "fs";
|
|
7687
7827
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7688
|
-
import { accessSync, constants, statSync as statSync12 } from "fs";
|
|
7689
7828
|
import { tmpdir as tmpdir5 } from "os";
|
|
7829
|
+
function resolvePython3() {
|
|
7830
|
+
for (const dir of (process.env.PATH ?? "").split(":")) {
|
|
7831
|
+
if (!dir || !dir.startsWith("/"))
|
|
7832
|
+
continue;
|
|
7833
|
+
const cand = `${dir}/python3`;
|
|
7834
|
+
try {
|
|
7835
|
+
accessSync(cand, constants2.X_OK);
|
|
7836
|
+
if (statSync12(cand).isFile())
|
|
7837
|
+
return cand;
|
|
7838
|
+
} catch {}
|
|
7839
|
+
}
|
|
7840
|
+
return null;
|
|
7841
|
+
}
|
|
7842
|
+
function pySignatures(src) {
|
|
7843
|
+
try {
|
|
7844
|
+
const py = resolvePython3();
|
|
7845
|
+
if (!py)
|
|
7846
|
+
return [];
|
|
7847
|
+
const r = spawnSync2(py, ["-I", "-S", "-c", PY_SCRIPT], {
|
|
7848
|
+
input: src.replace(/\r\n?/g, `
|
|
7849
|
+
`),
|
|
7850
|
+
cwd: tmpdir5(),
|
|
7851
|
+
timeout: PY_TIMEOUT_MS,
|
|
7852
|
+
encoding: "utf8",
|
|
7853
|
+
env: { PATH: process.env.PATH ?? "/usr/bin:/bin" },
|
|
7854
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
7855
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
7856
|
+
});
|
|
7857
|
+
if (r.error || r.status !== 0)
|
|
7858
|
+
return [];
|
|
7859
|
+
const parsed = JSON.parse(r.stdout);
|
|
7860
|
+
return Array.isArray(parsed) && parsed.every((l) => typeof l === "string") ? parsed : [];
|
|
7861
|
+
} catch {
|
|
7862
|
+
return [];
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7865
|
+
var PY_TIMEOUT_MS = 5000, PY_SCRIPT = `
|
|
7866
|
+
import ast, sys, json, re
|
|
7867
|
+
def u(n): return ast.unparse(n)
|
|
7868
|
+
def chain(n):
|
|
7869
|
+
if isinstance(n, ast.Name): return n.id
|
|
7870
|
+
if isinstance(n, ast.Attribute):
|
|
7871
|
+
c = chain(n.value)
|
|
7872
|
+
return None if c is None else c + "." + n.attr
|
|
7873
|
+
return None
|
|
7874
|
+
def deco(d):
|
|
7875
|
+
if isinstance(d, ast.Call):
|
|
7876
|
+
c = chain(d.func)
|
|
7877
|
+
return "@" + c + "(...)" if c else "@..."
|
|
7878
|
+
c = chain(d)
|
|
7879
|
+
return "@" + c if c else "@..."
|
|
7880
|
+
def lit(n):
|
|
7881
|
+
if isinstance(n, ast.Constant): return u(n)
|
|
7882
|
+
if isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub) and isinstance(n.operand, ast.Constant): return u(n)
|
|
7883
|
+
if isinstance(n, ast.Tuple):
|
|
7884
|
+
p = [lit(e) for e in n.elts]
|
|
7885
|
+
return None if None in p else ", ".join(p)
|
|
7886
|
+
return chain(n)
|
|
7887
|
+
def ann(n):
|
|
7888
|
+
if isinstance(n, (ast.Name, ast.Attribute)): return chain(n)
|
|
7889
|
+
if isinstance(n, ast.Constant): return u(n) if (n.value is None or n.value is Ellipsis) else None
|
|
7890
|
+
if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
|
|
7891
|
+
l, r = ann(n.left), ann(n.right)
|
|
7892
|
+
return None if l is None or r is None else l + " | " + r
|
|
7893
|
+
if isinstance(n, ast.List):
|
|
7894
|
+
p = [ann(e) for e in n.elts]
|
|
7895
|
+
return None if None in p else "[" + ", ".join(p) + "]"
|
|
7896
|
+
if isinstance(n, ast.Tuple):
|
|
7897
|
+
p = [ann(e) for e in n.elts]
|
|
7898
|
+
return None if None in p else ", ".join(p)
|
|
7899
|
+
if isinstance(n, ast.Subscript):
|
|
7900
|
+
v = chain(n.value)
|
|
7901
|
+
if v is None: return None
|
|
7902
|
+
last = v.split(".")[-1]
|
|
7903
|
+
if last == "Literal":
|
|
7904
|
+
s = lit(n.slice)
|
|
7905
|
+
return None if s is None else v + "[" + s + "]"
|
|
7906
|
+
if last == "Annotated" and isinstance(n.slice, ast.Tuple) and n.slice.elts:
|
|
7907
|
+
first = ann(n.slice.elts[0])
|
|
7908
|
+
return None if first is None else v + "[" + first + ", ...]"
|
|
7909
|
+
s = ann(n.slice)
|
|
7910
|
+
return None if s is None else v + "[" + s + "]"
|
|
7911
|
+
return None
|
|
7912
|
+
def a_or_dots(n):
|
|
7913
|
+
r = ann(n)
|
|
7914
|
+
return "..." if r is None else r
|
|
7915
|
+
def clean(n):
|
|
7916
|
+
r = ann(n)
|
|
7917
|
+
return "..." if r is None else r
|
|
7918
|
+
def arg(a, has_default):
|
|
7919
|
+
t = a.arg
|
|
7920
|
+
if a.annotation is not None: t += ": " + a_or_dots(a.annotation)
|
|
7921
|
+
if has_default: t += " = ..." if a.annotation is not None else "=..."
|
|
7922
|
+
return t
|
|
7923
|
+
def args(a):
|
|
7924
|
+
pos = list(a.posonlyargs) + list(a.args)
|
|
7925
|
+
first_default = len(pos) - len(a.defaults)
|
|
7926
|
+
out = []
|
|
7927
|
+
for i, x in enumerate(pos):
|
|
7928
|
+
out.append(arg(x, i >= first_default))
|
|
7929
|
+
if a.posonlyargs and i == len(a.posonlyargs) - 1: out.append("/")
|
|
7930
|
+
if a.vararg is not None: out.append("*" + arg(a.vararg, False))
|
|
7931
|
+
elif a.kwonlyargs: out.append("*")
|
|
7932
|
+
for x, d in zip(a.kwonlyargs, a.kw_defaults): out.append(arg(x, d is not None))
|
|
7933
|
+
if a.kwarg is not None: out.append("**" + arg(a.kwarg, False))
|
|
7934
|
+
return ", ".join(out)
|
|
7935
|
+
def fn(n, ind):
|
|
7936
|
+
r = [ind + deco(d) for d in n.decorator_list]
|
|
7937
|
+
h = ind + ("async def " if isinstance(n, ast.AsyncFunctionDef) else "def ") + n.name + "(" + args(n.args) + ")"
|
|
7938
|
+
if n.returns is not None: h += " -> " + a_or_dots(n.returns)
|
|
7939
|
+
return r + [h + ":"]
|
|
7940
|
+
def cls(n):
|
|
7941
|
+
r = [deco(d) for d in n.decorator_list]
|
|
7942
|
+
parts = [clean(b) for b in n.bases] + [(k.arg + "=" + clean(k.value)) if k.arg else "**..." for k in n.keywords]
|
|
7943
|
+
return r + ["class " + n.name + ("(" + ", ".join(parts) + ")" if parts else "") + ":"]
|
|
7944
|
+
tree = ast.parse(sys.stdin.read())
|
|
7945
|
+
out = []
|
|
7946
|
+
for n in tree.body:
|
|
7947
|
+
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and not n.name.startswith("_"):
|
|
7948
|
+
out += fn(n, "")
|
|
7949
|
+
elif isinstance(n, ast.ClassDef) and not n.name.startswith("_"):
|
|
7950
|
+
out += cls(n)
|
|
7951
|
+
for m in n.body:
|
|
7952
|
+
if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)) and (not m.name.startswith("_") or re.fullmatch(r"__\\w+__", m.name)):
|
|
7953
|
+
out += fn(m, " ")
|
|
7954
|
+
else:
|
|
7955
|
+
t = n.targets[0] if isinstance(n, ast.Assign) and len(n.targets) == 1 else (n.target if isinstance(n, ast.AnnAssign) else None)
|
|
7956
|
+
if isinstance(t, ast.Name) and re.fullmatch(r"[A-Z][A-Z0-9_]*", t.id): out.append(t.id + " = ...")
|
|
7957
|
+
print(json.dumps(out))
|
|
7958
|
+
`;
|
|
7959
|
+
var init_python = __esm(() => {
|
|
7960
|
+
init_shell();
|
|
7961
|
+
});
|
|
7962
|
+
|
|
7963
|
+
// ../../loki-ts/src/features/wall_manifest.ts
|
|
7690
7964
|
function templateEnd(s, i) {
|
|
7691
7965
|
for (let j = i + 1;j < s.length; ) {
|
|
7692
7966
|
const c = s[j];
|
|
@@ -8412,41 +8686,6 @@ function tsSignatures(src, jsx) {
|
|
|
8412
8686
|
}
|
|
8413
8687
|
return out2;
|
|
8414
8688
|
}
|
|
8415
|
-
function resolvePython3() {
|
|
8416
|
-
for (const dir of (process.env.PATH ?? "").split(":")) {
|
|
8417
|
-
if (!dir || !dir.startsWith("/"))
|
|
8418
|
-
continue;
|
|
8419
|
-
const cand = `${dir}/python3`;
|
|
8420
|
-
try {
|
|
8421
|
-
accessSync(cand, constants.X_OK);
|
|
8422
|
-
if (statSync12(cand).isFile())
|
|
8423
|
-
return cand;
|
|
8424
|
-
} catch {}
|
|
8425
|
-
}
|
|
8426
|
-
return null;
|
|
8427
|
-
}
|
|
8428
|
-
function pySignatures(src) {
|
|
8429
|
-
try {
|
|
8430
|
-
const py = resolvePython3();
|
|
8431
|
-
if (!py)
|
|
8432
|
-
return [];
|
|
8433
|
-
const r = spawnSync2(py, ["-I", "-S", "-c", PY_SCRIPT], {
|
|
8434
|
-
input: norm2(src),
|
|
8435
|
-
cwd: tmpdir5(),
|
|
8436
|
-
timeout: PY_TIMEOUT_MS,
|
|
8437
|
-
encoding: "utf8",
|
|
8438
|
-
env: { PATH: process.env.PATH ?? "/usr/bin:/bin" },
|
|
8439
|
-
maxBuffer: 8 * 1024 * 1024,
|
|
8440
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
8441
|
-
});
|
|
8442
|
-
if (r.error || r.status !== 0)
|
|
8443
|
-
return [];
|
|
8444
|
-
const parsed = JSON.parse(r.stdout);
|
|
8445
|
-
return Array.isArray(parsed) && parsed.every((l) => typeof l === "string") ? parsed : [];
|
|
8446
|
-
} catch {
|
|
8447
|
-
return [];
|
|
8448
|
-
}
|
|
8449
|
-
}
|
|
8450
8689
|
function packageRunner(content) {
|
|
8451
8690
|
try {
|
|
8452
8691
|
const pkg = JSON.parse(content);
|
|
@@ -8459,7 +8698,7 @@ function packageRunner(content) {
|
|
|
8459
8698
|
}
|
|
8460
8699
|
}
|
|
8461
8700
|
function pytestSection(content, header) {
|
|
8462
|
-
const lines =
|
|
8701
|
+
const lines = norm3(content).split(`
|
|
8463
8702
|
`);
|
|
8464
8703
|
const at = lines.findIndex((l) => header.test(l));
|
|
8465
8704
|
if (at < 0)
|
|
@@ -8476,10 +8715,10 @@ function runnerSection(files) {
|
|
|
8476
8715
|
if (js)
|
|
8477
8716
|
out2.push(`runner: ${js}`, "config: package.json (scripts.test only)");
|
|
8478
8717
|
for (const f of get(/^(bunfig\.toml|(vitest|jest)\.config\.[cm]?[jt]s)$/))
|
|
8479
|
-
out2.push(`config: ${f.path}`, ...
|
|
8718
|
+
out2.push(`config: ${f.path}`, ...norm3(f.content).split(`
|
|
8480
8719
|
`).slice(0, 20));
|
|
8481
8720
|
const py = [
|
|
8482
|
-
...get(/^pytest\.ini$/).map((f) => ({ f, lines:
|
|
8721
|
+
...get(/^pytest\.ini$/).map((f) => ({ f, lines: norm3(f.content).split(`
|
|
8483
8722
|
`).filter((l) => l.trim()) })),
|
|
8484
8723
|
...get(/^pyproject\.toml$/).map((f) => ({ f, lines: pytestSection(f.content, /^\[tool\.pytest/) })),
|
|
8485
8724
|
...get(/^(setup\.cfg|tox\.ini)$/).map((f) => ({ f, lines: pytestSection(f.content, /^\[(tool:)?pytest\]/) }))
|
|
@@ -8553,185 +8792,93 @@ function importsNamed(path, raw2, stems, importStems = stems, hits = () => false
|
|
|
8553
8792
|
if (specs.some((s) => s.trim().startsWith(".") && hits(resolveRel(dir, s.trim()))))
|
|
8554
8793
|
return true;
|
|
8555
8794
|
for (const py of views)
|
|
8556
|
-
for (const m of py.matchAll(/(?:^|:)[ \t\f]*from[ \t\f]+(\.+)([\w.]*?)[ \t\f]*\bimport\b[ \t\f]*(?:\(([^)]*)\)|([^\n]*))/gm)) {
|
|
8557
|
-
let base = dir;
|
|
8558
|
-
for (let i = 1;i < m[1].length; i++)
|
|
8559
|
-
base = dirOf(base);
|
|
8560
|
-
const r = resolveRel(base, (m[2] ?? "").replace(/\./g, "/"));
|
|
8561
|
-
if (hits(r) || names(m[3] ?? m[4] ?? "").some((n2) => hits(resolveRel(r, n2))))
|
|
8562
|
-
return true;
|
|
8563
|
-
}
|
|
8564
|
-
const lastSeg = (s) => normStem((s.trim().split(/[\\/]/).pop() ?? "").replace(/\.([cm]?[jt]sx?|py)$/i, "").replace(/^[#@~]+/, ""));
|
|
8565
|
-
return specs.some((s) => importStems.has(lastSeg(s)) || s.trim().replace(/\.(ts|js|py)$/, "").split(/[./\\]/).some((seg) => importStems.has(normStem(seg))));
|
|
8566
|
-
}
|
|
8567
|
-
function safeTs(content, path) {
|
|
8568
|
-
const jsx = /\.[jt]sx$/.test(path);
|
|
8569
|
-
try {
|
|
8570
|
-
return tsAmbiguous(
|
|
8571
|
-
} catch {
|
|
8572
|
-
return [];
|
|
8573
|
-
}
|
|
8574
|
-
}
|
|
8575
|
-
function buildWallManifest(files, taskModules, alsoNamed = []) {
|
|
8576
|
-
const all = [...files].sort(byPath).map((f) => ({ path: f.path.replace(/^\.\//, ""), content:
|
|
8577
|
-
const mods = [...new Set(taskModules.map((m) => m.replace(/^\.\//, "")))].sort();
|
|
8578
|
-
const excl = [...new Set([...mods, ...alsoNamed.map((m) => m.replace(/^\.\//, ""))])];
|
|
8579
|
-
const stems = new Set(excl.flatMap(stemsOfTarget));
|
|
8580
|
-
const tests = all.filter((f) => TEST_PATH.test(f.path));
|
|
8581
|
-
const out2 = ["# wall manifest (D77): signatures only, from the base tree", "", "## runner", ...runnerSection(all)];
|
|
8582
|
-
out2.push("", "## test layout", ...tests.slice(0, MAX_LAYOUT).map((f) => f.path));
|
|
8583
|
-
if (tests.length > MAX_LAYOUT)
|
|
8584
|
-
out2.push(`... ${tests.length - MAX_LAYOUT} more test files`);
|
|
8585
|
-
for (const path of mods) {
|
|
8586
|
-
const f = all.find((x) => x.path === path);
|
|
8587
|
-
if (!f)
|
|
8588
|
-
continue;
|
|
8589
|
-
out2.push("", `## signatures: ${path}`, ...path.endsWith(".py") ? pySignatures(f.content) : safeTs(f.content, path));
|
|
8590
|
-
}
|
|
8591
|
-
out2.push("", "## style examples");
|
|
8592
|
-
const named = new Set(excl.flatMap((m) => [baseOf(m).toLowerCase(), ...stemsOfTarget(m)]));
|
|
8593
|
-
const importStems = new Set(excl.flatMap((m) => {
|
|
8594
|
-
const s = stemsOfTarget(m);
|
|
8595
|
-
return s.length > 1 ? s.slice(1) : s;
|
|
8596
|
-
}));
|
|
8597
|
-
const info = excl.map((m) => ({ noext: m.replace(/\.[^./]*$/, "").toLowerCase(), dir: dirOf(m).toLowerCase(), mod: !!dirOf(m) && ["index", "__init__", "mod", "main"].includes(stemOf(m).toLowerCase()), src: !TEST_FILE.test(m) }));
|
|
8598
|
-
const hits = (r) => {
|
|
8599
|
-
const low = r.toLowerCase().replace(/\.(?:[cm]?[jt]sx?|py)$/, "");
|
|
8600
|
-
return [low, low.replace(/\/(?:index|__init__|mod|main)$/, "")].some((v) => info.some((t) => v === t.noext || t.mod && v === t.dir || v === "" || t.noext.startsWith(`${v}/`)));
|
|
8601
|
-
};
|
|
8602
|
-
const inTargetArea = (p) => {
|
|
8603
|
-
const d = dirOf(p).toLowerCase();
|
|
8604
|
-
return info.some((t) => t.src && (d === t.dir || t.mod && d.startsWith(`${t.dir}/`)));
|
|
8605
|
-
};
|
|
8606
|
-
const backstop = [...stems].filter((s) => !GENERIC_STEMS.includes(s));
|
|
8607
|
-
const eligible = (t) => TEST_FILE.test(t.path) && EXAMPLE_EXT.test(t.path) && !named.has(baseOf(t.path).toLowerCase()) && !named.has(normStem(stemOf(t.path))) && !inTargetArea(t.path) && !importsNamed(t.path, t.content, stems, importStems, hits) && !mentionsStem(t.content, backstop);
|
|
8608
|
-
for (const f of tests.filter(eligible).slice(0, MAX_EXAMPLES)) {
|
|
8609
|
-
out2.push(`--- example: ${f.path}`, ...f.content.split(`
|
|
8610
|
-
`).slice(0, EXAMPLE_LINES));
|
|
8611
|
-
}
|
|
8612
|
-
const lines = out2.join(`
|
|
8613
|
-
`).split(`
|
|
8614
|
-
`);
|
|
8615
|
-
if (lines.length <= MANIFEST_MAX_LINES)
|
|
8616
|
-
return lines.join(`
|
|
8617
|
-
`);
|
|
8618
|
-
const keep = MANIFEST_MAX_LINES - 1;
|
|
8619
|
-
return [...lines.slice(0, keep), `... truncated: ${lines.length - keep} lines omitted`].join(`
|
|
8620
|
-
`);
|
|
8621
|
-
}
|
|
8622
|
-
var MANIFEST_MAX_LINES = 400, MAX_EXAMPLES = 2, EXAMPLE_LINES = 40, MAX_LAYOUT = 60, TEST_PATH, TEST_FILE, EXAMPLE_EXT, baseOf = (p) => p.split("/").pop() ?? p, normStem = (s) => s.toLowerCase().replace(/[._-]+/g, ".").replace(/^\.|\.$/g, ""), GENERIC_STEMS, mentionsStem = (content, stems) => {
|
|
8623
|
-
const c = content.toLowerCase().replace(/[._-]+/g, ".");
|
|
8624
|
-
return stems.some((s) => new RegExp(`(?<![a-z0-9])${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![a-z0-9])`).test(c));
|
|
8625
|
-
}, dirOf = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "", resolveRel = (dir, spec) => {
|
|
8626
|
-
const out2 = dir ? dir.split("/") : [];
|
|
8627
|
-
for (const seg of spec.split("/")) {
|
|
8628
|
-
if (seg === "" || seg === ".")
|
|
8629
|
-
continue;
|
|
8630
|
-
if (seg === "..")
|
|
8631
|
-
out2.pop();
|
|
8632
|
-
else
|
|
8633
|
-
out2.push(seg);
|
|
8634
|
-
}
|
|
8635
|
-
return out2.join("/");
|
|
8636
|
-
}, stemsOfTarget = (m) => {
|
|
8637
|
-
const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
|
|
8638
|
-
return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
|
|
8639
|
-
},
|
|
8640
|
-
`), stemOf = (p) => (p.split("/").pop() ?? p).replace(/\.[^.]*$/, ""), byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.content < b.content ? -1 : a.content > b.content ? 1 : 0, REGEX_PREV = "(,=:[!&|?{;+-*%~^", isComment = (s, i) => s[i] === "/" && (s[i + 1] === "/" || s[i + 1] === "*"), MODS = "(?:(?:public|private|protected|static|readonly|abstract|async|get|set|declare|override)\\s+)*", MEMBER_GRAMMAR, NOT_MEMBER, RESERVED_AFTER_DECORATOR
|
|
8641
|
-
import ast, sys, json, re
|
|
8642
|
-
def u(n): return ast.unparse(n)
|
|
8643
|
-
def chain(n):
|
|
8644
|
-
if isinstance(n, ast.Name): return n.id
|
|
8645
|
-
if isinstance(n, ast.Attribute):
|
|
8646
|
-
c = chain(n.value)
|
|
8647
|
-
return None if c is None else c + "." + n.attr
|
|
8648
|
-
return None
|
|
8649
|
-
def deco(d):
|
|
8650
|
-
if isinstance(d, ast.Call):
|
|
8651
|
-
c = chain(d.func)
|
|
8652
|
-
return "@" + c + "(...)" if c else "@..."
|
|
8653
|
-
c = chain(d)
|
|
8654
|
-
return "@" + c if c else "@..."
|
|
8655
|
-
def lit(n):
|
|
8656
|
-
if isinstance(n, ast.Constant): return u(n)
|
|
8657
|
-
if isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub) and isinstance(n.operand, ast.Constant): return u(n)
|
|
8658
|
-
if isinstance(n, ast.Tuple):
|
|
8659
|
-
p = [lit(e) for e in n.elts]
|
|
8660
|
-
return None if None in p else ", ".join(p)
|
|
8661
|
-
return chain(n)
|
|
8662
|
-
def ann(n):
|
|
8663
|
-
if isinstance(n, (ast.Name, ast.Attribute)): return chain(n)
|
|
8664
|
-
if isinstance(n, ast.Constant): return u(n) if (n.value is None or n.value is Ellipsis) else None
|
|
8665
|
-
if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
|
|
8666
|
-
l, r = ann(n.left), ann(n.right)
|
|
8667
|
-
return None if l is None or r is None else l + " | " + r
|
|
8668
|
-
if isinstance(n, ast.List):
|
|
8669
|
-
p = [ann(e) for e in n.elts]
|
|
8670
|
-
return None if None in p else "[" + ", ".join(p) + "]"
|
|
8671
|
-
if isinstance(n, ast.Tuple):
|
|
8672
|
-
p = [ann(e) for e in n.elts]
|
|
8673
|
-
return None if None in p else ", ".join(p)
|
|
8674
|
-
if isinstance(n, ast.Subscript):
|
|
8675
|
-
v = chain(n.value)
|
|
8676
|
-
if v is None: return None
|
|
8677
|
-
last = v.split(".")[-1]
|
|
8678
|
-
if last == "Literal":
|
|
8679
|
-
s = lit(n.slice)
|
|
8680
|
-
return None if s is None else v + "[" + s + "]"
|
|
8681
|
-
if last == "Annotated" and isinstance(n.slice, ast.Tuple) and n.slice.elts:
|
|
8682
|
-
first = ann(n.slice.elts[0])
|
|
8683
|
-
return None if first is None else v + "[" + first + ", ...]"
|
|
8684
|
-
s = ann(n.slice)
|
|
8685
|
-
return None if s is None else v + "[" + s + "]"
|
|
8686
|
-
return None
|
|
8687
|
-
def a_or_dots(n):
|
|
8688
|
-
r = ann(n)
|
|
8689
|
-
return "..." if r is None else r
|
|
8690
|
-
def clean(n):
|
|
8691
|
-
r = ann(n)
|
|
8692
|
-
return "..." if r is None else r
|
|
8693
|
-
def arg(a, has_default):
|
|
8694
|
-
t = a.arg
|
|
8695
|
-
if a.annotation is not None: t += ": " + a_or_dots(a.annotation)
|
|
8696
|
-
if has_default: t += " = ..." if a.annotation is not None else "=..."
|
|
8697
|
-
return t
|
|
8698
|
-
def args(a):
|
|
8699
|
-
pos = list(a.posonlyargs) + list(a.args)
|
|
8700
|
-
first_default = len(pos) - len(a.defaults)
|
|
8701
|
-
out = []
|
|
8702
|
-
for i, x in enumerate(pos):
|
|
8703
|
-
out.append(arg(x, i >= first_default))
|
|
8704
|
-
if a.posonlyargs and i == len(a.posonlyargs) - 1: out.append("/")
|
|
8705
|
-
if a.vararg is not None: out.append("*" + arg(a.vararg, False))
|
|
8706
|
-
elif a.kwonlyargs: out.append("*")
|
|
8707
|
-
for x, d in zip(a.kwonlyargs, a.kw_defaults): out.append(arg(x, d is not None))
|
|
8708
|
-
if a.kwarg is not None: out.append("**" + arg(a.kwarg, False))
|
|
8709
|
-
return ", ".join(out)
|
|
8710
|
-
def fn(n, ind):
|
|
8711
|
-
r = [ind + deco(d) for d in n.decorator_list]
|
|
8712
|
-
h = ind + ("async def " if isinstance(n, ast.AsyncFunctionDef) else "def ") + n.name + "(" + args(n.args) + ")"
|
|
8713
|
-
if n.returns is not None: h += " -> " + a_or_dots(n.returns)
|
|
8714
|
-
return r + [h + ":"]
|
|
8715
|
-
def cls(n):
|
|
8716
|
-
r = [deco(d) for d in n.decorator_list]
|
|
8717
|
-
parts = [clean(b) for b in n.bases] + [(k.arg + "=" + clean(k.value)) if k.arg else "**..." for k in n.keywords]
|
|
8718
|
-
return r + ["class " + n.name + ("(" + ", ".join(parts) + ")" if parts else "") + ":"]
|
|
8719
|
-
tree = ast.parse(sys.stdin.read())
|
|
8720
|
-
out = []
|
|
8721
|
-
for n in tree.body:
|
|
8722
|
-
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and not n.name.startswith("_"):
|
|
8723
|
-
out += fn(n, "")
|
|
8724
|
-
elif isinstance(n, ast.ClassDef) and not n.name.startswith("_"):
|
|
8725
|
-
out += cls(n)
|
|
8726
|
-
for m in n.body:
|
|
8727
|
-
if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)) and (not m.name.startswith("_") or re.fullmatch(r"__\\w+__", m.name)):
|
|
8728
|
-
out += fn(m, " ")
|
|
8729
|
-
else:
|
|
8730
|
-
t = n.targets[0] if isinstance(n, ast.Assign) and len(n.targets) == 1 else (n.target if isinstance(n, ast.AnnAssign) else None)
|
|
8731
|
-
if isinstance(t, ast.Name) and re.fullmatch(r"[A-Z][A-Z0-9_]*", t.id): out.append(t.id + " = ...")
|
|
8732
|
-
print(json.dumps(out))
|
|
8733
|
-
`;
|
|
8795
|
+
for (const m of py.matchAll(/(?:^|:)[ \t\f]*from[ \t\f]+(\.+)([\w.]*?)[ \t\f]*\bimport\b[ \t\f]*(?:\(([^)]*)\)|([^\n]*))/gm)) {
|
|
8796
|
+
let base = dir;
|
|
8797
|
+
for (let i = 1;i < m[1].length; i++)
|
|
8798
|
+
base = dirOf(base);
|
|
8799
|
+
const r = resolveRel(base, (m[2] ?? "").replace(/\./g, "/"));
|
|
8800
|
+
if (hits(r) || names(m[3] ?? m[4] ?? "").some((n2) => hits(resolveRel(r, n2))))
|
|
8801
|
+
return true;
|
|
8802
|
+
}
|
|
8803
|
+
const lastSeg = (s) => normStem((s.trim().split(/[\\/]/).pop() ?? "").replace(/\.([cm]?[jt]sx?|py)$/i, "").replace(/^[#@~]+/, ""));
|
|
8804
|
+
return specs.some((s) => importStems.has(lastSeg(s)) || s.trim().replace(/\.(ts|js|py)$/, "").split(/[./\\]/).some((seg) => importStems.has(normStem(seg))));
|
|
8805
|
+
}
|
|
8806
|
+
function safeTs(content, path) {
|
|
8807
|
+
const jsx = /\.[jt]sx$/.test(path);
|
|
8808
|
+
try {
|
|
8809
|
+
return tsAmbiguous(norm3(content), jsx) ? [] : tsSignatures(content, jsx);
|
|
8810
|
+
} catch {
|
|
8811
|
+
return [];
|
|
8812
|
+
}
|
|
8813
|
+
}
|
|
8814
|
+
function buildWallManifest(files, taskModules, alsoNamed = []) {
|
|
8815
|
+
const all = [...files].sort(byPath).map((f) => ({ path: f.path.replace(/^\.\//, ""), content: norm3(f.content) }));
|
|
8816
|
+
const mods = [...new Set(taskModules.map((m) => m.replace(/^\.\//, "")))].sort();
|
|
8817
|
+
const excl = [...new Set([...mods, ...alsoNamed.map((m) => m.replace(/^\.\//, ""))])];
|
|
8818
|
+
const stems = new Set(excl.flatMap(stemsOfTarget));
|
|
8819
|
+
const tests = all.filter((f) => TEST_PATH.test(f.path));
|
|
8820
|
+
const out2 = ["# wall manifest (D77): signatures only, from the base tree", "", "## runner", ...runnerSection(all)];
|
|
8821
|
+
out2.push("", "## test layout", ...tests.slice(0, MAX_LAYOUT).map((f) => f.path));
|
|
8822
|
+
if (tests.length > MAX_LAYOUT)
|
|
8823
|
+
out2.push(`... ${tests.length - MAX_LAYOUT} more test files`);
|
|
8824
|
+
for (const path of mods) {
|
|
8825
|
+
const f = all.find((x) => x.path === path);
|
|
8826
|
+
if (!f)
|
|
8827
|
+
continue;
|
|
8828
|
+
out2.push("", `## signatures: ${path}`, ...path.endsWith(".py") ? pySignatures(f.content) : safeTs(f.content, path));
|
|
8829
|
+
}
|
|
8830
|
+
out2.push("", "## style examples");
|
|
8831
|
+
const named = new Set(excl.flatMap((m) => [baseOf(m).toLowerCase(), ...stemsOfTarget(m)]));
|
|
8832
|
+
const importStems = new Set(excl.flatMap((m) => {
|
|
8833
|
+
const s = stemsOfTarget(m);
|
|
8834
|
+
return s.length > 1 ? s.slice(1) : s;
|
|
8835
|
+
}));
|
|
8836
|
+
const info = excl.map((m) => ({ noext: m.replace(/\.[^./]*$/, "").toLowerCase(), dir: dirOf(m).toLowerCase(), mod: !!dirOf(m) && ["index", "__init__", "mod", "main"].includes(stemOf(m).toLowerCase()), src: !TEST_FILE.test(m) }));
|
|
8837
|
+
const hits = (r) => {
|
|
8838
|
+
const low = r.toLowerCase().replace(/\.(?:[cm]?[jt]sx?|py)$/, "");
|
|
8839
|
+
return [low, low.replace(/\/(?:index|__init__|mod|main)$/, "")].some((v) => info.some((t) => v === t.noext || t.mod && v === t.dir || v === "" || t.noext.startsWith(`${v}/`)));
|
|
8840
|
+
};
|
|
8841
|
+
const inTargetArea = (p) => {
|
|
8842
|
+
const d = dirOf(p).toLowerCase();
|
|
8843
|
+
return info.some((t) => t.src && (d === t.dir || t.mod && d.startsWith(`${t.dir}/`)));
|
|
8844
|
+
};
|
|
8845
|
+
const backstop = [...stems].filter((s) => !GENERIC_STEMS.includes(s));
|
|
8846
|
+
const eligible = (t) => TEST_FILE.test(t.path) && EXAMPLE_EXT.test(t.path) && !named.has(baseOf(t.path).toLowerCase()) && !named.has(normStem(stemOf(t.path))) && !inTargetArea(t.path) && !importsNamed(t.path, t.content, stems, importStems, hits) && !mentionsStem(t.content, backstop);
|
|
8847
|
+
for (const f of tests.filter(eligible).slice(0, MAX_EXAMPLES)) {
|
|
8848
|
+
out2.push(`--- example: ${f.path}`, ...f.content.split(`
|
|
8849
|
+
`).slice(0, EXAMPLE_LINES));
|
|
8850
|
+
}
|
|
8851
|
+
const lines = out2.join(`
|
|
8852
|
+
`).split(`
|
|
8853
|
+
`);
|
|
8854
|
+
if (lines.length <= MANIFEST_MAX_LINES)
|
|
8855
|
+
return lines.join(`
|
|
8856
|
+
`);
|
|
8857
|
+
const keep = MANIFEST_MAX_LINES - 1;
|
|
8858
|
+
return [...lines.slice(0, keep), `... truncated: ${lines.length - keep} lines omitted`].join(`
|
|
8859
|
+
`);
|
|
8860
|
+
}
|
|
8861
|
+
var MANIFEST_MAX_LINES = 400, MAX_EXAMPLES = 2, EXAMPLE_LINES = 40, MAX_LAYOUT = 60, TEST_PATH, TEST_FILE, EXAMPLE_EXT, baseOf = (p) => p.split("/").pop() ?? p, normStem = (s) => s.toLowerCase().replace(/[._-]+/g, ".").replace(/^\.|\.$/g, ""), GENERIC_STEMS, mentionsStem = (content, stems) => {
|
|
8862
|
+
const c = content.toLowerCase().replace(/[._-]+/g, ".");
|
|
8863
|
+
return stems.some((s) => new RegExp(`(?<![a-z0-9])${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![a-z0-9])`).test(c));
|
|
8864
|
+
}, dirOf = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "", resolveRel = (dir, spec) => {
|
|
8865
|
+
const out2 = dir ? dir.split("/") : [];
|
|
8866
|
+
for (const seg of spec.split("/")) {
|
|
8867
|
+
if (seg === "" || seg === ".")
|
|
8868
|
+
continue;
|
|
8869
|
+
if (seg === "..")
|
|
8870
|
+
out2.pop();
|
|
8871
|
+
else
|
|
8872
|
+
out2.push(seg);
|
|
8873
|
+
}
|
|
8874
|
+
return out2.join("/");
|
|
8875
|
+
}, stemsOfTarget = (m) => {
|
|
8876
|
+
const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
|
|
8877
|
+
return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
|
|
8878
|
+
}, norm3 = (s) => s.replace(/\r\n?/g, `
|
|
8879
|
+
`), stemOf = (p) => (p.split("/").pop() ?? p).replace(/\.[^.]*$/, ""), byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.content < b.content ? -1 : a.content > b.content ? 1 : 0, REGEX_PREV = "(,=:[!&|?{;+-*%~^", isComment = (s, i) => s[i] === "/" && (s[i + 1] === "/" || s[i + 1] === "*"), MODS = "(?:(?:public|private|protected|static|readonly|abstract|async|get|set|declare|override)\\s+)*", MEMBER_GRAMMAR, NOT_MEMBER, RESERVED_AFTER_DECORATOR;
|
|
8734
8880
|
var init_wall_manifest = __esm(() => {
|
|
8881
|
+
init_python();
|
|
8735
8882
|
TEST_PATH = /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.(py|go)$/;
|
|
8736
8883
|
TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
8737
8884
|
EXAMPLE_EXT = /\.(py|[cm]?[jt]sx?)$/;
|
|
@@ -8835,7 +8982,7 @@ Example: test('name', () => { assert.strictEqual(fn(1), 2); }); Run: node --tes
|
|
|
8835
8982
|
|
|
8836
8983
|
// ../../loki-ts/src/engine10/cache.ts
|
|
8837
8984
|
import { createHash as createHash7 } from "crypto";
|
|
8838
|
-
import { existsSync as
|
|
8985
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
8839
8986
|
import { resolve as resolve8 } from "path";
|
|
8840
8987
|
function repoKey(originUrl, repoDir) {
|
|
8841
8988
|
const basis = originUrl && originUrl.trim() !== "" ? originUrl : resolve8(repoDir);
|
|
@@ -8848,7 +8995,7 @@ function repoCacheDir(key, cacheRoot = defaultCacheRoot()) {
|
|
|
8848
8995
|
return resolve8(cacheRoot, key);
|
|
8849
8996
|
}
|
|
8850
8997
|
function readJson3(path, isValid = (_v) => true) {
|
|
8851
|
-
if (!
|
|
8998
|
+
if (!existsSync17(path))
|
|
8852
8999
|
return null;
|
|
8853
9000
|
try {
|
|
8854
9001
|
const parsed = JSON.parse(readFileSync19(path, "utf8"));
|
|
@@ -8966,7 +9113,7 @@ function routerMode(env = process.env) {
|
|
|
8966
9113
|
var routerEnabled = (env = process.env) => routerMode(env) === "on";
|
|
8967
9114
|
|
|
8968
9115
|
// ../../loki-ts/src/engine10/cost.ts
|
|
8969
|
-
import {
|
|
9116
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync21 } from "fs";
|
|
8970
9117
|
import { join as join29 } from "path";
|
|
8971
9118
|
function routerTelemetryOn(env = process.env) {
|
|
8972
9119
|
return routerEnabled(env);
|
|
@@ -9110,17 +9257,19 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9110
9257
|
var UNMETERED = "cli-invoker-unmetered";
|
|
9111
9258
|
var init_cost = __esm(() => {
|
|
9112
9259
|
init_hooks();
|
|
9260
|
+
init_provider_cost();
|
|
9261
|
+
init_provider_cost();
|
|
9113
9262
|
});
|
|
9114
9263
|
|
|
9115
9264
|
// ../../loki-ts/src/util/atomic.ts
|
|
9116
9265
|
import {
|
|
9117
|
-
closeSync as
|
|
9118
|
-
fstatSync as
|
|
9266
|
+
closeSync as closeSync6,
|
|
9267
|
+
fstatSync as fstatSync5,
|
|
9119
9268
|
fsyncSync as fsyncSync2,
|
|
9120
|
-
lstatSync as
|
|
9269
|
+
lstatSync as lstatSync6,
|
|
9121
9270
|
mkdirSync as mkdirSync8,
|
|
9122
|
-
openSync as
|
|
9123
|
-
readSync as
|
|
9271
|
+
openSync as openSync6,
|
|
9272
|
+
readSync as readSync5,
|
|
9124
9273
|
renameSync as renameSync4,
|
|
9125
9274
|
rmSync as rmSync6,
|
|
9126
9275
|
statSync as statSync13,
|
|
@@ -9132,12 +9281,12 @@ import { dirname as dirname9 } from "path";
|
|
|
9132
9281
|
function durableWriteThenRename(target, body) {
|
|
9133
9282
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
9134
9283
|
const tmp = tmpPathFor(target);
|
|
9135
|
-
const fd =
|
|
9284
|
+
const fd = openSync6(tmp, "w");
|
|
9136
9285
|
try {
|
|
9137
9286
|
writeSync3(fd, body);
|
|
9138
9287
|
fsyncSync2(fd);
|
|
9139
9288
|
} finally {
|
|
9140
|
-
|
|
9289
|
+
closeSync6(fd);
|
|
9141
9290
|
}
|
|
9142
9291
|
renameSync4(tmp, target);
|
|
9143
9292
|
fsyncParentDir(target);
|
|
@@ -9145,12 +9294,12 @@ function durableWriteThenRename(target, body) {
|
|
|
9145
9294
|
function fsyncParentDir(target) {
|
|
9146
9295
|
let dfd = null;
|
|
9147
9296
|
try {
|
|
9148
|
-
dfd =
|
|
9297
|
+
dfd = openSync6(dirname9(target), "r");
|
|
9149
9298
|
fsyncSync2(dfd);
|
|
9150
9299
|
} catch {} finally {
|
|
9151
9300
|
if (dfd !== null) {
|
|
9152
9301
|
try {
|
|
9153
|
-
|
|
9302
|
+
closeSync6(dfd);
|
|
9154
9303
|
} catch {}
|
|
9155
9304
|
}
|
|
9156
9305
|
}
|
|
@@ -9168,7 +9317,7 @@ var init_atomic = __esm(() => {
|
|
|
9168
9317
|
});
|
|
9169
9318
|
|
|
9170
9319
|
// ../../loki-ts/src/runner/budget.ts
|
|
9171
|
-
import { existsSync as
|
|
9320
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "fs";
|
|
9172
9321
|
import { dirname as dirname10, join as join30, resolve as resolve10 } from "path";
|
|
9173
9322
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9174
9323
|
function loadDefaultMaxCostUsd() {
|
|
@@ -9186,7 +9335,7 @@ function _loadPricing() {
|
|
|
9186
9335
|
try {
|
|
9187
9336
|
const moduleDir = dirname10(fileURLToPath2(import.meta.url));
|
|
9188
9337
|
const jsonPath = resolve10(moduleDir, "..", "..", "data", "model-pricing.json");
|
|
9189
|
-
if (!
|
|
9338
|
+
if (!existsSync18(jsonPath))
|
|
9190
9339
|
return _FALLBACK_PRICING;
|
|
9191
9340
|
const raw2 = JSON.parse(readFileSync22(jsonPath, "utf8"));
|
|
9192
9341
|
const pricing2 = raw2["pricing"];
|
|
@@ -9318,7 +9467,7 @@ var init_model_rank = __esm(() => {
|
|
|
9318
9467
|
});
|
|
9319
9468
|
|
|
9320
9469
|
// ../../loki-ts/src/engine10/sizing.ts
|
|
9321
|
-
import { existsSync as
|
|
9470
|
+
import { existsSync as existsSync19, readFileSync as readFileSync23 } from "fs";
|
|
9322
9471
|
import { basename as basename10 } from "path";
|
|
9323
9472
|
function namedFiles(task, map) {
|
|
9324
9473
|
return (map?.files ?? []).filter((f) => task.toLowerCase().includes(basename10(f).toLowerCase()));
|
|
@@ -9337,7 +9486,7 @@ function sizeTask(task, map, tests) {
|
|
|
9337
9486
|
return why.length ? { size: "normal", reasons: why } : { size: "small", reasons: [`task ${task.length} chars, names ${named} files`] };
|
|
9338
9487
|
}
|
|
9339
9488
|
function loadRepoMap(repomapRef) {
|
|
9340
|
-
if (!repomapRef || !
|
|
9489
|
+
if (!repomapRef || !existsSync19(repomapRef))
|
|
9341
9490
|
return null;
|
|
9342
9491
|
try {
|
|
9343
9492
|
return JSON.parse(readFileSync23(repomapRef, "utf8"));
|
|
@@ -9371,7 +9520,7 @@ var init_sizing = __esm(() => {
|
|
|
9371
9520
|
});
|
|
9372
9521
|
|
|
9373
9522
|
// ../../loki-ts/src/project_model/schema.ts
|
|
9374
|
-
import { existsSync as
|
|
9523
|
+
import { existsSync as existsSync20, lstatSync as lstatSync7, realpathSync as realpathSync15, statSync as statSync14 } from "fs";
|
|
9375
9524
|
import { isAbsolute as isAbsolute3, join as join32, normalize, sep as sep9 } from "path";
|
|
9376
9525
|
function safeRel(p) {
|
|
9377
9526
|
const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
|
|
@@ -9381,8 +9530,8 @@ function safeRel(p) {
|
|
|
9381
9530
|
}
|
|
9382
9531
|
function insideRepo(repoDir, rel) {
|
|
9383
9532
|
try {
|
|
9384
|
-
const real2 =
|
|
9385
|
-
const base =
|
|
9533
|
+
const real2 = realpathSync15(join32(repoDir, rel));
|
|
9534
|
+
const base = realpathSync15(repoDir);
|
|
9386
9535
|
return real2 === base || real2.startsWith(base + sep9);
|
|
9387
9536
|
} catch {
|
|
9388
9537
|
return false;
|
|
@@ -9395,7 +9544,7 @@ function checkCitation(repoDir, raw2) {
|
|
|
9395
9544
|
if (rel === null)
|
|
9396
9545
|
return `citation "${raw2}" is not a repo-relative path`;
|
|
9397
9546
|
const abs = join32(repoDir, rel);
|
|
9398
|
-
if (!
|
|
9547
|
+
if (!existsSync20(abs) || !statSync14(abs).isFile() || !insideRepo(repoDir, rel))
|
|
9399
9548
|
return `citation "${raw2}" names a file that does not exist`;
|
|
9400
9549
|
return null;
|
|
9401
9550
|
}
|
|
@@ -9406,7 +9555,7 @@ function checkFingerprint(repoDir, raw2) {
|
|
|
9406
9555
|
if (rel === null)
|
|
9407
9556
|
return `fingerprint "${raw2}" is not a repo-relative path`;
|
|
9408
9557
|
try {
|
|
9409
|
-
if (!
|
|
9558
|
+
if (!lstatSync7(join32(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
|
|
9410
9559
|
return `fingerprint "${raw2}" is not a regular file inside the repo`;
|
|
9411
9560
|
} catch {
|
|
9412
9561
|
return `fingerprint "${raw2}" names a file that does not exist`;
|
|
@@ -9435,7 +9584,7 @@ function checkDir(repoDir, v, where, errs) {
|
|
|
9435
9584
|
return ".";
|
|
9436
9585
|
}
|
|
9437
9586
|
const abs = join32(repoDir, rel);
|
|
9438
|
-
if (!
|
|
9587
|
+
if (!existsSync20(abs) || !statSync14(abs).isDirectory() || !insideRepo(repoDir, rel))
|
|
9439
9588
|
errs.push(`${where}: directory "${rel}" does not exist`);
|
|
9440
9589
|
return rel;
|
|
9441
9590
|
}
|
|
@@ -9538,7 +9687,7 @@ var init_schema = __esm(() => {
|
|
|
9538
9687
|
|
|
9539
9688
|
// ../../loki-ts/src/project_model/gather.ts
|
|
9540
9689
|
import { createHash as createHash8 } from "crypto";
|
|
9541
|
-
import { closeSync as
|
|
9690
|
+
import { closeSync as closeSync7, lstatSync as lstatSync8, openSync as openSync7, readFileSync as readFileSync24, readSync as readSync6 } from "fs";
|
|
9542
9691
|
import { dirname as dirname11, join as join33 } from "path";
|
|
9543
9692
|
function listShallow(repoDir) {
|
|
9544
9693
|
try {
|
|
@@ -9566,7 +9715,7 @@ function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
|
9566
9715
|
h.update("invalid");
|
|
9567
9716
|
else {
|
|
9568
9717
|
const abs = join33(repoDir, f);
|
|
9569
|
-
const size =
|
|
9718
|
+
const size = lstatSync8(abs).size;
|
|
9570
9719
|
h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync24(abs) : `too-large:${size}`);
|
|
9571
9720
|
}
|
|
9572
9721
|
} catch {
|
|
@@ -9610,7 +9759,7 @@ var init_gather = __esm(() => {
|
|
|
9610
9759
|
});
|
|
9611
9760
|
|
|
9612
9761
|
// ../../loki-ts/src/project_model/discover.ts
|
|
9613
|
-
import { existsSync as
|
|
9762
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync25, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
|
|
9614
9763
|
import { join as join34 } from "path";
|
|
9615
9764
|
function projectModelEnabled(env = process.env) {
|
|
9616
9765
|
return env["LOKI_E10_PROJECT_MODEL"] !== "0";
|
|
@@ -10324,10 +10473,10 @@ function classifyRisk(files, env = process.env) {
|
|
|
10324
10473
|
};
|
|
10325
10474
|
}
|
|
10326
10475
|
function changedFiles(repoDir, baseSha) {
|
|
10327
|
-
const
|
|
10476
|
+
const run2 = (args) => safeGit(repoDir, args).split(`
|
|
10328
10477
|
`).map((l) => l.trim()).filter(Boolean);
|
|
10329
|
-
const tracked =
|
|
10330
|
-
const untracked =
|
|
10478
|
+
const tracked = run2(["diff", "--name-only", baseSha]);
|
|
10479
|
+
const untracked = run2(["ls-files", "--others", "--exclude-standard"]);
|
|
10331
10480
|
return [...new Set([...tracked, ...untracked])].filter((f) => !f.startsWith(".loki/"));
|
|
10332
10481
|
}
|
|
10333
10482
|
function diffFiles(repoDir, baseSha) {
|
|
@@ -10401,7 +10550,7 @@ var init_check_result = __esm(() => {
|
|
|
10401
10550
|
|
|
10402
10551
|
// ../../loki-ts/src/runner/load_owner.ts
|
|
10403
10552
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10404
|
-
import { mkdtempSync as mkdtempSync5, realpathSync as
|
|
10553
|
+
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
|
|
10405
10554
|
import { tmpdir as tmpdir7 } from "os";
|
|
10406
10555
|
import { basename as basename14, extname as extname2, join as join39 } from "path";
|
|
10407
10556
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
@@ -10423,7 +10572,7 @@ function loadSignature(out2, roots) {
|
|
|
10423
10572
|
let o = out2;
|
|
10424
10573
|
for (const r of roots.flatMap((x) => {
|
|
10425
10574
|
try {
|
|
10426
|
-
return [x,
|
|
10575
|
+
return [x, realpathSync16(x)];
|
|
10427
10576
|
} catch {
|
|
10428
10577
|
return [x];
|
|
10429
10578
|
}
|
|
@@ -10610,7 +10759,7 @@ var init_state = __esm(() => {
|
|
|
10610
10759
|
});
|
|
10611
10760
|
|
|
10612
10761
|
// ../../loki-ts/src/runner/repo_profile.ts
|
|
10613
|
-
import { existsSync as
|
|
10762
|
+
import { existsSync as existsSync22, readFileSync as readFileSync28, mkdirSync as mkdirSync12, readdirSync as readdirSync11, renameSync as renameSync6, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
10614
10763
|
import { basename as basename15, join as join41, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
|
|
10615
10764
|
function loadProfile(repoDir) {
|
|
10616
10765
|
try {
|
|
@@ -10637,10 +10786,10 @@ function attribute(repoDir, baseSha, failing) {
|
|
|
10637
10786
|
}
|
|
10638
10787
|
function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
|
|
10639
10788
|
const bad = full.filter((c) => c.result === "fail"), none = { preExistingChecks: [], preExistingIds: [], introducedChecks: bad.map((c) => c.name), line: null };
|
|
10640
|
-
const
|
|
10789
|
+
const norm4 = (s) => s.replace(/\s+--silent\b/, "").trim(), p = loadProfile(repoDir);
|
|
10641
10790
|
if (!bad.length || !p || p.revision !== baseSha || !p.baseline.attributable || !p.baseline.failing_ids.length)
|
|
10642
10791
|
return none;
|
|
10643
|
-
if (bad.some((c) =>
|
|
10792
|
+
if (bad.some((c) => norm4(c.cmd) !== norm4(p.baseline.command ?? "")))
|
|
10644
10793
|
return { ...none, line: `baseline: ran "${p.baseline.command}", not the full suite command, ids not comparable, full suite failures not excused` };
|
|
10645
10794
|
const block = new Set(blocked);
|
|
10646
10795
|
return attribute(repoDir, baseSha, bad.map((c) => ({ name: c.name, ...c.ids ? { ids: c.ids } : {}, eligible: (c.ids ?? []).every((i) => !block.has(i) && !changed.some((f) => i.includes(f))) })));
|
|
@@ -10657,7 +10806,7 @@ var init_repo_profile = __esm(() => {
|
|
|
10657
10806
|
// ../../loki-ts/src/project_graph.ts
|
|
10658
10807
|
import { createHash as createHash9 } from "crypto";
|
|
10659
10808
|
import {
|
|
10660
|
-
existsSync as
|
|
10809
|
+
existsSync as existsSync23,
|
|
10661
10810
|
readdirSync as readdirSync12,
|
|
10662
10811
|
readFileSync as readFileSync29,
|
|
10663
10812
|
statSync as statSync15,
|
|
@@ -10666,7 +10815,7 @@ import {
|
|
|
10666
10815
|
} from "fs";
|
|
10667
10816
|
import { basename as basename16, dirname as dirname12, join as join42, resolve as resolve12 } from "path";
|
|
10668
10817
|
function readManifest(path) {
|
|
10669
|
-
if (!
|
|
10818
|
+
if (!existsSync23(path)) {
|
|
10670
10819
|
return null;
|
|
10671
10820
|
}
|
|
10672
10821
|
let raw2;
|
|
@@ -10751,7 +10900,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
|
|
|
10751
10900
|
}
|
|
10752
10901
|
if (!st.isDirectory())
|
|
10753
10902
|
continue;
|
|
10754
|
-
if (!
|
|
10903
|
+
if (!existsSync23(join42(full, ".loki", "app.json")))
|
|
10755
10904
|
continue;
|
|
10756
10905
|
seen.add(full);
|
|
10757
10906
|
out2.push(full);
|
|
@@ -10786,12 +10935,12 @@ function discoverProjectGraph(targetDir) {
|
|
|
10786
10935
|
const examined = [];
|
|
10787
10936
|
let chosenManifest = null;
|
|
10788
10937
|
let chosenRoot = "";
|
|
10789
|
-
if (
|
|
10938
|
+
if (existsSync23(parentManifest) && parentDir !== absTarget) {
|
|
10790
10939
|
examined.push(parentManifest);
|
|
10791
10940
|
chosenManifest = readManifest(parentManifest);
|
|
10792
10941
|
chosenRoot = parentDir;
|
|
10793
10942
|
}
|
|
10794
|
-
if (!chosenManifest &&
|
|
10943
|
+
if (!chosenManifest && existsSync23(targetManifest)) {
|
|
10795
10944
|
examined.push(targetManifest);
|
|
10796
10945
|
chosenManifest = readManifest(targetManifest);
|
|
10797
10946
|
chosenRoot = absTarget;
|
|
@@ -10803,7 +10952,7 @@ function discoverProjectGraph(targetDir) {
|
|
|
10803
10952
|
const filtered = [];
|
|
10804
10953
|
for (const m of members) {
|
|
10805
10954
|
const sibManifestPath = join42(m, ".loki", "app.json");
|
|
10806
|
-
if (
|
|
10955
|
+
if (existsSync23(sibManifestPath)) {
|
|
10807
10956
|
examined.push(sibManifestPath);
|
|
10808
10957
|
const sibManifest = readManifest(sibManifestPath);
|
|
10809
10958
|
if (sibManifest && sibManifest.app_id !== chosenManifest.app_id) {
|
|
@@ -10826,77 +10975,6 @@ var init_project_graph = __esm(() => {
|
|
|
10826
10975
|
APP_ID_REGEX = /^[a-z0-9-]{3,40}$/;
|
|
10827
10976
|
});
|
|
10828
10977
|
|
|
10829
|
-
// ../../loki-ts/src/util/shell.ts
|
|
10830
|
-
async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
|
|
10831
|
-
const reader = stream.getReader();
|
|
10832
|
-
const decoder = new TextDecoder;
|
|
10833
|
-
let out2 = "";
|
|
10834
|
-
let total = 0;
|
|
10835
|
-
try {
|
|
10836
|
-
while (total < maxBytes) {
|
|
10837
|
-
const { done, value } = await reader.read();
|
|
10838
|
-
if (done)
|
|
10839
|
-
break;
|
|
10840
|
-
if (!value)
|
|
10841
|
-
continue;
|
|
10842
|
-
total += value.byteLength;
|
|
10843
|
-
if (total > maxBytes) {
|
|
10844
|
-
const keep = value.byteLength - (total - maxBytes);
|
|
10845
|
-
out2 += decoder.decode(value.subarray(0, keep), { stream: true });
|
|
10846
|
-
break;
|
|
10847
|
-
}
|
|
10848
|
-
out2 += decoder.decode(value, { stream: true });
|
|
10849
|
-
}
|
|
10850
|
-
out2 += decoder.decode();
|
|
10851
|
-
} finally {
|
|
10852
|
-
try {
|
|
10853
|
-
await reader.cancel();
|
|
10854
|
-
} catch {}
|
|
10855
|
-
reader.releaseLock();
|
|
10856
|
-
}
|
|
10857
|
-
return out2;
|
|
10858
|
-
}
|
|
10859
|
-
async function run(argv, opts = {}) {
|
|
10860
|
-
const proc = Bun.spawn({
|
|
10861
|
-
cmd: [...argv],
|
|
10862
|
-
stdout: "pipe",
|
|
10863
|
-
stderr: "pipe",
|
|
10864
|
-
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
10865
|
-
cwd: opts.cwd
|
|
10866
|
-
});
|
|
10867
|
-
let timer;
|
|
10868
|
-
let killTimer;
|
|
10869
|
-
if (opts.timeoutMs && opts.timeoutMs > 0) {
|
|
10870
|
-
timer = setTimeout(() => {
|
|
10871
|
-
try {
|
|
10872
|
-
proc.kill("SIGTERM");
|
|
10873
|
-
} catch {}
|
|
10874
|
-
killTimer = setTimeout(() => {
|
|
10875
|
-
try {
|
|
10876
|
-
proc.kill("SIGKILL");
|
|
10877
|
-
} catch {}
|
|
10878
|
-
}, 2000);
|
|
10879
|
-
}, opts.timeoutMs);
|
|
10880
|
-
}
|
|
10881
|
-
try {
|
|
10882
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
10883
|
-
readStreamCapped(proc.stdout),
|
|
10884
|
-
new Response(proc.stderr).text(),
|
|
10885
|
-
proc.exited
|
|
10886
|
-
]);
|
|
10887
|
-
return { stdout, stderr, exitCode };
|
|
10888
|
-
} finally {
|
|
10889
|
-
if (timer)
|
|
10890
|
-
clearTimeout(timer);
|
|
10891
|
-
if (killTimer)
|
|
10892
|
-
clearTimeout(killTimer);
|
|
10893
|
-
}
|
|
10894
|
-
}
|
|
10895
|
-
var MAX_STDOUT_BYTES;
|
|
10896
|
-
var init_shell = __esm(() => {
|
|
10897
|
-
MAX_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
10898
|
-
});
|
|
10899
|
-
|
|
10900
10978
|
// ../../loki-ts/src/e10ext/budget_cap.ts
|
|
10901
10979
|
var DEFAULT_MAX_COST_USD;
|
|
10902
10980
|
var init_budget_cap = __esm(() => {
|
|
@@ -10908,71 +10986,6 @@ var init_budget_cap = __esm(() => {
|
|
|
10908
10986
|
// ../../loki-ts/src/runner/retry_class.ts
|
|
10909
10987
|
var init_retry_class = () => {};
|
|
10910
10988
|
|
|
10911
|
-
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
10912
|
-
import { closeSync as closeSync7, constants as constants2, fstatSync as fstatSync5, lstatSync as lstatSync8, openSync as openSync7, readSync as readSync6, realpathSync as realpathSync16 } from "fs";
|
|
10913
|
-
function readBounded(path) {
|
|
10914
|
-
const l = lstatSync8(path);
|
|
10915
|
-
if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
|
|
10916
|
-
return null;
|
|
10917
|
-
const fd = openSync7(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
10918
|
-
try {
|
|
10919
|
-
const st = fstatSync5(fd);
|
|
10920
|
-
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
10921
|
-
return null;
|
|
10922
|
-
const buf = Buffer.alloc(st.size + 1);
|
|
10923
|
-
let n2 = 0;
|
|
10924
|
-
for (;; ) {
|
|
10925
|
-
const r = readSync6(fd, buf, n2, buf.length - n2, null);
|
|
10926
|
-
if (r <= 0)
|
|
10927
|
-
break;
|
|
10928
|
-
n2 += r;
|
|
10929
|
-
if (n2 >= buf.length)
|
|
10930
|
-
return null;
|
|
10931
|
-
}
|
|
10932
|
-
return buf.subarray(0, n2).toString("utf8");
|
|
10933
|
-
} finally {
|
|
10934
|
-
closeSync7(fd);
|
|
10935
|
-
}
|
|
10936
|
-
}
|
|
10937
|
-
function parseText(txt) {
|
|
10938
|
-
try {
|
|
10939
|
-
const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
|
|
10940
|
-
if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
|
|
10941
|
-
return null;
|
|
10942
|
-
return { id: j.id, writeSet: ws, pack, tokenBudget: tb };
|
|
10943
|
-
} catch {
|
|
10944
|
-
return null;
|
|
10945
|
-
}
|
|
10946
|
-
}
|
|
10947
|
-
function parseSpec(path) {
|
|
10948
|
-
try {
|
|
10949
|
-
const txt = readBounded(path);
|
|
10950
|
-
return txt === null ? null : parseText(txt);
|
|
10951
|
-
} catch {
|
|
10952
|
-
return null;
|
|
10953
|
-
}
|
|
10954
|
-
}
|
|
10955
|
-
function unitSpec(env = process.env) {
|
|
10956
|
-
const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
|
|
10957
|
-
if (env["LOKI_SPEED"] === "0" || !path)
|
|
10958
|
-
return null;
|
|
10959
|
-
if (frozen !== undefined)
|
|
10960
|
-
return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
|
|
10961
|
-
if (!memo.has(path))
|
|
10962
|
-
memo.set(path, parseSpec(path));
|
|
10963
|
-
return memo.get(path) ?? null;
|
|
10964
|
-
}
|
|
10965
|
-
function unitFlags(staged, env = process.env, pre = {}) {
|
|
10966
|
-
const s = unitSpec(env);
|
|
10967
|
-
return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
|
|
10968
|
-
}
|
|
10969
|
-
var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm3 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm3) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
|
|
10970
|
-
var init_unit_mode = __esm(() => {
|
|
10971
|
-
MAX_SPEC_BYTES = 1024 * 1024;
|
|
10972
|
-
CTRL = /[\x00-\x1f\x7f]/;
|
|
10973
|
-
memo = new Map;
|
|
10974
|
-
});
|
|
10975
|
-
|
|
10976
10989
|
// ../../loki-ts/src/e10ext/repomemory.ts
|
|
10977
10990
|
var init_repomemory = __esm(() => {
|
|
10978
10991
|
init_cache();
|
|
@@ -11011,11 +11024,12 @@ var init_steer = __esm(() => {
|
|
|
11011
11024
|
});
|
|
11012
11025
|
|
|
11013
11026
|
// ../../loki-ts/src/util/pr_lessons.ts
|
|
11014
|
-
import { existsSync as
|
|
11027
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync30 } from "fs";
|
|
11015
11028
|
import { dirname as dirname13, join as join43 } from "path";
|
|
11029
|
+
import { createHash as createHash10 } from "crypto";
|
|
11016
11030
|
function loadLessons(repoDir) {
|
|
11017
11031
|
const p = lessonsPath(repoDir);
|
|
11018
|
-
if (!
|
|
11032
|
+
if (!existsSync24(p))
|
|
11019
11033
|
return [];
|
|
11020
11034
|
try {
|
|
11021
11035
|
const j = JSON.parse(readFileSync30(p, "utf-8"));
|
|
@@ -11029,6 +11043,25 @@ function save(repoDir, lessons) {
|
|
|
11029
11043
|
mkdirSync14(dirname13(p), { recursive: true });
|
|
11030
11044
|
atomicWriteJson(p, { version: 2, lessons });
|
|
11031
11045
|
}
|
|
11046
|
+
function appendReceiptFact(repoDir, f, now2 = () => new Date) {
|
|
11047
|
+
const id = `rcp-${createHash10("sha256").update(f.runId).digest("hex").slice(0, 12)}`;
|
|
11048
|
+
const lessons = loadLessons(repoDir);
|
|
11049
|
+
if (lessons.some((l) => l.id === id))
|
|
11050
|
+
return false;
|
|
11051
|
+
const cited = f.receiptSha256 !== null;
|
|
11052
|
+
const title = f.title.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
11053
|
+
lessons.push({
|
|
11054
|
+
id,
|
|
11055
|
+
kind: "receipt",
|
|
11056
|
+
text: `Run ${f.runId} sealed ${f.verdict}: ${title}`,
|
|
11057
|
+
source: { pr: `run:${f.runId}`, pr_url: "", comment_url: "", kind: "receipt", author: "loki" },
|
|
11058
|
+
learned_at: now2().toISOString(),
|
|
11059
|
+
uses: [],
|
|
11060
|
+
justified_by: { run_id: f.runId, receipt_sha256: f.receiptSha256, verdict: cited ? f.verdict : "unverified" }
|
|
11061
|
+
});
|
|
11062
|
+
save(repoDir, lessons);
|
|
11063
|
+
return true;
|
|
11064
|
+
}
|
|
11032
11065
|
function recordRunVerdict(repoDir, runId, verdict) {
|
|
11033
11066
|
const lessons = loadLessons(repoDir);
|
|
11034
11067
|
let dirty = false;
|
|
@@ -11041,9 +11074,10 @@ function recordRunVerdict(repoDir, runId, verdict) {
|
|
|
11041
11074
|
if (dirty)
|
|
11042
11075
|
save(repoDir, lessons);
|
|
11043
11076
|
}
|
|
11044
|
-
var lessonsPath = (repoDir) => join43(repoDir, ".loki", "memory", "semantic", "pr-lessons.json");
|
|
11077
|
+
var lessonsPath = (repoDir) => join43(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
|
|
11045
11078
|
var init_pr_lessons = __esm(() => {
|
|
11046
11079
|
init_atomic();
|
|
11080
|
+
KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
|
|
11047
11081
|
});
|
|
11048
11082
|
|
|
11049
11083
|
// ../../loki-ts/src/runner/model_downgrades.ts
|
|
@@ -11056,7 +11090,7 @@ var init_model_downgrades = __esm(() => {
|
|
|
11056
11090
|
var init_mcp_config = () => {};
|
|
11057
11091
|
|
|
11058
11092
|
// ../../loki-ts/src/providers/claude_flags.ts
|
|
11059
|
-
import { existsSync as
|
|
11093
|
+
import { existsSync as existsSync25, readFileSync as readFileSync31 } from "fs";
|
|
11060
11094
|
import { resolve as resolvePath, dirname as dirname14 } from "path";
|
|
11061
11095
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11062
11096
|
function designArchetypesText() {
|
|
@@ -11067,7 +11101,7 @@ function designArchetypesText() {
|
|
|
11067
11101
|
["..", "..", "references", "design-archetypes.md"]
|
|
11068
11102
|
]) {
|
|
11069
11103
|
const p = resolvePath(moduleDir, ...rel);
|
|
11070
|
-
if (
|
|
11104
|
+
if (existsSync25(p))
|
|
11071
11105
|
return readFileSync31(p, "utf8");
|
|
11072
11106
|
}
|
|
11073
11107
|
} catch {}
|
|
@@ -11146,12 +11180,12 @@ var init_providers = __esm(() => {
|
|
|
11146
11180
|
});
|
|
11147
11181
|
|
|
11148
11182
|
// ../../loki-ts/src/runner/router/route_record.ts
|
|
11149
|
-
import { existsSync as
|
|
11183
|
+
import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync15, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
|
|
11150
11184
|
import { join as join44 } from "path";
|
|
11151
11185
|
function loadRouteRecord(runDir) {
|
|
11152
11186
|
try {
|
|
11153
11187
|
const p = join44(runDir, ROUTE_RECORD_FILE);
|
|
11154
|
-
if (!
|
|
11188
|
+
if (!existsSync26(p) || !lstatSync9(p).isFile())
|
|
11155
11189
|
return;
|
|
11156
11190
|
const v = JSON.parse(readFileSync32(p, "utf8"));
|
|
11157
11191
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
@@ -11163,7 +11197,7 @@ var ROUTE_RECORD_FILE = "route.json";
|
|
|
11163
11197
|
var init_route_record = () => {};
|
|
11164
11198
|
|
|
11165
11199
|
// ../../loki-ts/src/runner/router/history_store.ts
|
|
11166
|
-
import { existsSync as
|
|
11200
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync33, renameSync as renameSync7, rmSync as rmSync11, writeFileSync as writeFileSync18 } from "fs";
|
|
11167
11201
|
import { resolve as resolve13 } from "path";
|
|
11168
11202
|
function validRun(v) {
|
|
11169
11203
|
if (typeof v !== "object" || v === null)
|
|
@@ -11176,7 +11210,7 @@ function historyPath(key, cacheRoot) {
|
|
|
11176
11210
|
}
|
|
11177
11211
|
function readRunHistory(key, cacheRoot) {
|
|
11178
11212
|
const path = historyPath(key, cacheRoot);
|
|
11179
|
-
if (!
|
|
11213
|
+
if (!existsSync27(path))
|
|
11180
11214
|
return [];
|
|
11181
11215
|
try {
|
|
11182
11216
|
const parsed = JSON.parse(readFileSync33(path, "utf8"));
|
|
@@ -11426,7 +11460,7 @@ __export(exports_deep, {
|
|
|
11426
11460
|
DEFAULT_SECRET_SCAN_SH: () => DEFAULT_SECRET_SCAN_SH,
|
|
11427
11461
|
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
|
|
11428
11462
|
});
|
|
11429
|
-
import { existsSync as
|
|
11463
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync17, writeFileSync as writeFileSync19 } from "fs";
|
|
11430
11464
|
import { join as join47 } from "path";
|
|
11431
11465
|
async function runFullSuite(ctx, signal, opts, checks, notProven) {
|
|
11432
11466
|
if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
|
|
@@ -11513,7 +11547,7 @@ async function runCouncilCheck(ctx, changed, baseSha, signal, opts, checks, notP
|
|
|
11513
11547
|
async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
11514
11548
|
const scriptPath = opts.secretScanShPath ?? DEFAULT_SECRET_SCAN_SH;
|
|
11515
11549
|
const name = "security scan";
|
|
11516
|
-
if (!
|
|
11550
|
+
if (!existsSync28(scriptPath)) {
|
|
11517
11551
|
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: 0 });
|
|
11518
11552
|
notProven.add(`not run: ${name} (secret-scan.sh not found)`);
|
|
11519
11553
|
return;
|
|
@@ -11662,7 +11696,7 @@ var init_deep = __esm(() => {
|
|
|
11662
11696
|
});
|
|
11663
11697
|
|
|
11664
11698
|
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
11665
|
-
import { existsSync as
|
|
11699
|
+
import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
|
|
11666
11700
|
import { tmpdir as tmpdir8 } from "os";
|
|
11667
11701
|
import { dirname as dirname16, isAbsolute as isAbsolute6, join as join48, relative as relative5 } from "path";
|
|
11668
11702
|
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
@@ -11675,7 +11709,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
|
11675
11709
|
}
|
|
11676
11710
|
};
|
|
11677
11711
|
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
11678
|
-
const project = [join48(repoDir, ".venv", "bin", bin), join48(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join48(venvEnv, "bin", bin)] : []].find(
|
|
11712
|
+
const project = [join48(repoDir, ".venv", "bin", bin), join48(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join48(venvEnv, "bin", bin)] : []].find(existsSync29);
|
|
11679
11713
|
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
11680
11714
|
}
|
|
11681
11715
|
function runnerCmd(t, repoDir) {
|
|
@@ -11828,7 +11862,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
11828
11862
|
return out2;
|
|
11829
11863
|
}
|
|
11830
11864
|
function isLokiModeRepo(repoDir) {
|
|
11831
|
-
return
|
|
11865
|
+
return existsSync29(join48(repoDir, "scripts", "select-tests.sh"));
|
|
11832
11866
|
}
|
|
11833
11867
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
11834
11868
|
const { api, ...runOpts } = opts;
|
|
@@ -11845,9 +11879,9 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
11845
11879
|
}
|
|
11846
11880
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
11847
11881
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
11848
|
-
if (
|
|
11882
|
+
if (existsSync29(join48(g.cwd, "tsconfig.json")))
|
|
11849
11883
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
11850
|
-
if (ESLINT_CONFIGS.some((f) =>
|
|
11884
|
+
if (ESLINT_CONFIGS.some((f) => existsSync29(join48(g.cwd, f))))
|
|
11851
11885
|
await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
11852
11886
|
}
|
|
11853
11887
|
}
|
|
@@ -11979,7 +12013,7 @@ var init_verify = __esm(() => {
|
|
|
11979
12013
|
});
|
|
11980
12014
|
|
|
11981
12015
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
11982
|
-
import { existsSync as
|
|
12016
|
+
import { existsSync as existsSync30, readdirSync as readdirSync13, readFileSync as readFileSync34 } from "fs";
|
|
11983
12017
|
import { dirname as dirname17, join as join49 } from "path";
|
|
11984
12018
|
function parseJsonc(text2) {
|
|
11985
12019
|
let out2 = "", i = 0;
|
|
@@ -12016,7 +12050,7 @@ function moduleOf(file, depth = 0) {
|
|
|
12016
12050
|
if (!ext || !ext.startsWith("."))
|
|
12017
12051
|
return null;
|
|
12018
12052
|
const next = join49(dirname17(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
12019
|
-
return
|
|
12053
|
+
return existsSync30(next) ? moduleOf(next, depth + 1) : null;
|
|
12020
12054
|
}
|
|
12021
12055
|
function readPackageConventions(pkgDir, runner = null) {
|
|
12022
12056
|
let names = [];
|
|
@@ -12065,7 +12099,7 @@ var init_conventions = () => {};
|
|
|
12065
12099
|
|
|
12066
12100
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
12067
12101
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12068
|
-
import { existsSync as
|
|
12102
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync7, readFileSync as readFileSync35, readdirSync as readdirSync14, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync20 } from "fs";
|
|
12069
12103
|
import { tmpdir as tmpdir9 } from "os";
|
|
12070
12104
|
import { dirname as dirname18, isAbsolute as isAbsolute7, join as join50, relative as relative6 } from "path";
|
|
12071
12105
|
function pytestCollectionIsRed(output, repoDir) {
|
|
@@ -12110,7 +12144,7 @@ function pytestCollectionIsRed(output, repoDir) {
|
|
|
12110
12144
|
}
|
|
12111
12145
|
function moduleUnderRepo(repoDir, name) {
|
|
12112
12146
|
const rel = name.replace(/\./g, "/");
|
|
12113
|
-
return
|
|
12147
|
+
return existsSync31(join50(repoDir, `${rel}.py`)) || existsSync31(join50(repoDir, rel, "__init__.py"));
|
|
12114
12148
|
}
|
|
12115
12149
|
function pytestExit1IsRed(output) {
|
|
12116
12150
|
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
@@ -12187,13 +12221,16 @@ ${r.stderr ?? ""}`), repoDir, interpreter);
|
|
|
12187
12221
|
return { pass, fail, not_run };
|
|
12188
12222
|
}
|
|
12189
12223
|
}
|
|
12190
|
-
function buildWallBrief(task, repomapText = "", runners = [], hasManifest = false, conventions = "") {
|
|
12224
|
+
function buildWallBrief(task, repomapText = "", runners = [], hasManifest = false, conventions = "", acceptance = []) {
|
|
12191
12225
|
return withStagePrefix([
|
|
12192
12226
|
"You are the Loki 10 Wall author.",
|
|
12193
12227
|
`You cannot see the repository. This directory holds only task.md and repomap.txt${hasManifest ? " and wall_manifest.txt (signatures, runner config, test layout and test-style examples)" : ""}.`,
|
|
12194
12228
|
...repomapText ? [`Repository paths (repomap.txt):
|
|
12195
12229
|
${repomapText}`] : [],
|
|
12196
12230
|
...taskBlock(task),
|
|
12231
|
+
...acceptance.length ? [`Acceptance criteria frozen for this unit (each must be covered by a test):
|
|
12232
|
+
${acceptance.map((a) => `Acceptance: ${a}`).join(`
|
|
12233
|
+
`)}`] : [],
|
|
12197
12234
|
"Write behavioral acceptance tests that prove the task is done. A test that errors on import, uses another framework's globals, or fails for a reason unrelated to the task is discarded.",
|
|
12198
12235
|
`Test runner: ${runners.map((r) => RUNNER_HINT[r]).find(Boolean) ?? "the framework named in repomap.txt"}`,
|
|
12199
12236
|
...conventions ? [conventions] : [],
|
|
@@ -12206,7 +12243,7 @@ function loadTaskText(ctx, fromPrior) {
|
|
|
12206
12243
|
if (fromPrior)
|
|
12207
12244
|
return fromPrior;
|
|
12208
12245
|
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join50(ctx.runDir, "issue.json");
|
|
12209
|
-
if (
|
|
12246
|
+
if (existsSync31(issueJsonPath)) {
|
|
12210
12247
|
try {
|
|
12211
12248
|
const issue = JSON.parse(readFileSync35(issueJsonPath, "utf8"));
|
|
12212
12249
|
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
@@ -12258,7 +12295,7 @@ async function wallAuthor(ctx, signal) {
|
|
|
12258
12295
|
writeFileSync20(join50(cwd, n2), c, "utf8");
|
|
12259
12296
|
const session = await ctx.sessions.run({
|
|
12260
12297
|
stage: "wall",
|
|
12261
|
-
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2)),
|
|
12298
|
+
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
|
|
12262
12299
|
tier: "development",
|
|
12263
12300
|
model: wallModel(),
|
|
12264
12301
|
iterationId: `${ctx.runId}-wall`,
|
|
@@ -12341,6 +12378,7 @@ async function runWall(ctx, signal, opts = {}) {
|
|
|
12341
12378
|
var WALL_PREFIX = "loki_wall_", BASE_RUN_TIMEOUT_MS = 60000, WALL_MAP_MAX_LINES = 200, wallStage, stage2;
|
|
12342
12379
|
var init_wall = __esm(() => {
|
|
12343
12380
|
init_types();
|
|
12381
|
+
init_unit_mode();
|
|
12344
12382
|
init_lean_prefix();
|
|
12345
12383
|
init_wall_hints();
|
|
12346
12384
|
init_sizing();
|
|
@@ -12515,7 +12553,7 @@ var init_scope2 = __esm(() => {
|
|
|
12515
12553
|
});
|
|
12516
12554
|
|
|
12517
12555
|
// ../../loki-ts/src/util/receipt_signer.ts
|
|
12518
|
-
import { createHash as
|
|
12556
|
+
import { createHash as createHash11, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
|
|
12519
12557
|
import { chmodSync as chmodSync5, linkSync, mkdirSync as mkdirSync20, readFileSync as readFileSync37, statSync as statSync16, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
|
|
12520
12558
|
import { homedir as homedir8 } from "os";
|
|
12521
12559
|
import { dirname as dirname22, join as join52 } from "path";
|
|
@@ -12577,13 +12615,13 @@ function signReceipt(runId, hash) {
|
|
|
12577
12615
|
const input = `${b64u(canonicalJson({ alg: "EdDSA", typ: "JWT", kid }))}.${b64u(canonicalJson({ job_id: runId, run_id: runId, receipt_sha256: hash, iat: Math.floor(Date.now() / 1000) }))}`;
|
|
12578
12616
|
return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
|
|
12579
12617
|
}
|
|
12580
|
-
var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(
|
|
12618
|
+
var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash11("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest());
|
|
12581
12619
|
var init_receipt_signer = () => {};
|
|
12582
12620
|
|
|
12583
12621
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
12584
12622
|
import { spawn as spawn7 } from "child_process";
|
|
12585
|
-
import { createHash as
|
|
12586
|
-
import { existsSync as
|
|
12623
|
+
import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
|
|
12624
|
+
import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
|
|
12587
12625
|
import { createServer } from "net";
|
|
12588
12626
|
import { dirname as dirname23, isAbsolute as isAbsolute8, join as join53, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
|
|
12589
12627
|
function routeFor(file) {
|
|
@@ -12615,7 +12653,7 @@ function checkScreens(runDir, screens) {
|
|
|
12615
12653
|
if (isSymlink(cur))
|
|
12616
12654
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
12617
12655
|
}
|
|
12618
|
-
if (!
|
|
12656
|
+
if (!existsSync32(file))
|
|
12619
12657
|
return `evidence screenshot is missing: ${s.path}`;
|
|
12620
12658
|
if (!isRegularFile(file))
|
|
12621
12659
|
return `evidence screenshot is not a regular file: ${s.path}`;
|
|
@@ -12659,7 +12697,7 @@ async function waitUp(url, ms, signal) {
|
|
|
12659
12697
|
function openapiPaths(repoDir) {
|
|
12660
12698
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
12661
12699
|
const p = join53(repoDir, f);
|
|
12662
|
-
if (!
|
|
12700
|
+
if (!existsSync32(p))
|
|
12663
12701
|
continue;
|
|
12664
12702
|
try {
|
|
12665
12703
|
const txt = readFileSync38(p, "utf8");
|
|
@@ -12766,7 +12804,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12766
12804
|
const pkgPath = join53(appDir, "package.json");
|
|
12767
12805
|
let script = null;
|
|
12768
12806
|
if (!boot) {
|
|
12769
|
-
if (!
|
|
12807
|
+
if (!existsSync32(pkgPath))
|
|
12770
12808
|
return skip("no package.json");
|
|
12771
12809
|
script = pickScript(JSON.parse(readFileSync38(pkgPath, "utf8")));
|
|
12772
12810
|
if (!script)
|
|
@@ -12808,7 +12846,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
12808
12846
|
break;
|
|
12809
12847
|
const rel = join53(rel0, `${screenName(route)}.png`);
|
|
12810
12848
|
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join53(runDir, rel)], appDir, left(), signal);
|
|
12811
|
-
if (ok && !aborted() && !isSymlink(join53(runDir, rel)) &&
|
|
12849
|
+
if (ok && !aborted() && !isSymlink(join53(runDir, rel)) && existsSync32(join53(runDir, rel)))
|
|
12812
12850
|
rels.push(rel);
|
|
12813
12851
|
}
|
|
12814
12852
|
if (aborted())
|
|
@@ -12860,12 +12898,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
12860
12898
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
12861
12899
|
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
|
|
12862
12900
|
}
|
|
12863
|
-
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) =>
|
|
12901
|
+
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(readFileSync38(p)).digest("hex"), playwrightBin = (repoDir) => {
|
|
12864
12902
|
const bin = join53(repoDir, "node_modules", ".bin", "playwright");
|
|
12865
|
-
return
|
|
12903
|
+
return existsSync32(bin) ? bin : null;
|
|
12866
12904
|
}, playwrightTestPkg = (repoDir) => {
|
|
12867
12905
|
const d = join53(repoDir, "node_modules", "@playwright", "test");
|
|
12868
|
-
return
|
|
12906
|
+
return existsSync32(join53(d, "package.json")) ? d : null;
|
|
12869
12907
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
12870
12908
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
12871
12909
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
@@ -12896,7 +12934,7 @@ var init_visual_evidence = __esm(() => {
|
|
|
12896
12934
|
});
|
|
12897
12935
|
|
|
12898
12936
|
// ../../loki-ts/src/council/role_agents.ts
|
|
12899
|
-
import { existsSync as
|
|
12937
|
+
import { existsSync as existsSync33, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
|
|
12900
12938
|
import { homedir as homedir9 } from "os";
|
|
12901
12939
|
import { join as join54 } from "path";
|
|
12902
12940
|
function agentDirs(repoDir, env = process.env) {
|
|
@@ -12917,7 +12955,7 @@ function parseAgent(file, stem2) {
|
|
|
12917
12955
|
}
|
|
12918
12956
|
function loadRoleAgent(repoDir, role, env = process.env) {
|
|
12919
12957
|
for (const d of agentDirs(repoDir, env)) {
|
|
12920
|
-
if (!
|
|
12958
|
+
if (!existsSync33(d))
|
|
12921
12959
|
continue;
|
|
12922
12960
|
let names;
|
|
12923
12961
|
try {
|
|
@@ -12945,7 +12983,7 @@ var init_role_agents = __esm(() => {
|
|
|
12945
12983
|
});
|
|
12946
12984
|
|
|
12947
12985
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
12948
|
-
import { existsSync as
|
|
12986
|
+
import { existsSync as existsSync34, readFileSync as readFileSync40 } from "fs";
|
|
12949
12987
|
import { join as join55 } from "path";
|
|
12950
12988
|
function minVerdict(verdict, review) {
|
|
12951
12989
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
@@ -12960,7 +12998,7 @@ function explicitReview(repoDir, env) {
|
|
|
12960
12998
|
return "off";
|
|
12961
12999
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
12962
13000
|
const p = join55(repoDir, f);
|
|
12963
|
-
if (!
|
|
13001
|
+
if (!existsSync34(p))
|
|
12964
13002
|
continue;
|
|
12965
13003
|
try {
|
|
12966
13004
|
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync40(p, "utf8"));
|
|
@@ -13181,9 +13219,9 @@ var init_cost_preview = __esm(() => {
|
|
|
13181
13219
|
});
|
|
13182
13220
|
|
|
13183
13221
|
// ../../loki-ts/src/features/contract.ts
|
|
13184
|
-
import { existsSync as
|
|
13222
|
+
import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync41, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
13185
13223
|
import { dirname as dirname24, join as join56 } from "path";
|
|
13186
|
-
import { createHash as
|
|
13224
|
+
import { createHash as createHash13 } from "crypto";
|
|
13187
13225
|
function contractEnabled(env = process.env) {
|
|
13188
13226
|
return env["LOKI_CONTRACT"] !== "0";
|
|
13189
13227
|
}
|
|
@@ -13234,7 +13272,7 @@ function readContract(repoDir) {
|
|
|
13234
13272
|
} catch {
|
|
13235
13273
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
13236
13274
|
}
|
|
13237
|
-
const sha2563 =
|
|
13275
|
+
const sha2563 = createHash13("sha256").update(raw2).digest("hex");
|
|
13238
13276
|
let j;
|
|
13239
13277
|
try {
|
|
13240
13278
|
j = JSON.parse(raw2.toString("utf8"));
|
|
@@ -13304,12 +13342,12 @@ var init_contract = __esm(() => {
|
|
|
13304
13342
|
});
|
|
13305
13343
|
|
|
13306
13344
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
13307
|
-
import { createHash as
|
|
13308
|
-
import { existsSync as
|
|
13309
|
-
import { dirname as dirname25, join as join57 } from "path";
|
|
13345
|
+
import { createHash as createHash14 } from "crypto";
|
|
13346
|
+
import { existsSync as existsSync36, readdirSync as readdirSync17, readFileSync as readFileSync42, statSync as statSync17 } from "fs";
|
|
13347
|
+
import { basename as basename19, dirname as dirname25, join as join57 } from "path";
|
|
13310
13348
|
function sealGroup(runDir, rh) {
|
|
13311
13349
|
const manifestPath = join57(runDir, "group", "manifest.json");
|
|
13312
|
-
if (!
|
|
13350
|
+
if (!existsSync36(manifestPath))
|
|
13313
13351
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
13314
13352
|
const notProven = [];
|
|
13315
13353
|
let problems = 0;
|
|
@@ -13353,7 +13391,7 @@ function sealGroup(runDir, rh) {
|
|
|
13353
13391
|
unit_id: id,
|
|
13354
13392
|
sub_receipt_sha256: sha2(bytes),
|
|
13355
13393
|
receipt_sha256: sub["receipt_sha256"],
|
|
13356
|
-
events_file_sha256:
|
|
13394
|
+
events_file_sha256: existsSync36(ep) ? sha2(readFileSync42(ep)) : null,
|
|
13357
13395
|
verdict,
|
|
13358
13396
|
goal: text2(u["goal"], 200),
|
|
13359
13397
|
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
@@ -13378,7 +13416,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13378
13416
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
13379
13417
|
const g = receipt["group"];
|
|
13380
13418
|
if (g === undefined)
|
|
13381
|
-
return
|
|
13419
|
+
return existsSync36(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
13382
13420
|
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
13383
13421
|
return bad("group section malformed");
|
|
13384
13422
|
const entries = g["units"];
|
|
@@ -13403,7 +13441,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13403
13441
|
}
|
|
13404
13442
|
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
13405
13443
|
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
13406
|
-
const evSha =
|
|
13444
|
+
const evSha = existsSync36(ep) ? sha2(readFileSync42(ep)) : null;
|
|
13407
13445
|
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
13408
13446
|
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
13409
13447
|
const sub = readJson5(rp);
|
|
@@ -13421,7 +13459,83 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
13421
13459
|
}
|
|
13422
13460
|
return unchecked ? { verdict: "UNCHECKED", reason: `group: ${unchecked}` } : null;
|
|
13423
13461
|
}
|
|
13424
|
-
|
|
13462
|
+
function compositeVerdict(childVerdicts) {
|
|
13463
|
+
const base = childVerdicts.includes("SPEC_CONFLICT") ? "SPEC_CONFLICT" : childVerdicts.length > 0 && childVerdicts.every((v) => v === "ALREADY_SATISFIED") ? "ALREADY_SATISFIED" : "VERIFIED";
|
|
13464
|
+
const failed = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
|
|
13465
|
+
return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
|
|
13466
|
+
}
|
|
13467
|
+
async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
13468
|
+
const runDir = dirname25(receiptPath);
|
|
13469
|
+
const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
|
|
13470
|
+
const bad = (reason) => ({ problem: { verdict: "TAMPERED", reason: `composite: ${reason}` }, verdict: "FAILED" });
|
|
13471
|
+
const kids = receipt["children"];
|
|
13472
|
+
if (!Array.isArray(kids) || kids.length === 0 || !ID_RE.test(String(receipt["run_id"] ?? "")))
|
|
13473
|
+
return bad("body malformed");
|
|
13474
|
+
let ibytes;
|
|
13475
|
+
try {
|
|
13476
|
+
ibytes = readFileSync42(join57(runDir, "integration.json"));
|
|
13477
|
+
} catch {
|
|
13478
|
+
return bad("integration.json missing");
|
|
13479
|
+
}
|
|
13480
|
+
if (sha2(ibytes) !== receipt["integration_sha256"])
|
|
13481
|
+
return bad("integration.json does not match its recorded hash");
|
|
13482
|
+
const integ = readJson5(join57(runDir, "integration.json"));
|
|
13483
|
+
if (!isObj10(integ) || !isObj10(integ["outcomes"]))
|
|
13484
|
+
return bad("integration.json has no outcomes");
|
|
13485
|
+
const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
|
|
13486
|
+
const order2 = Array.isArray(integ["order"]) ? integ["order"].map(String) : Object.keys(outcomes).sort();
|
|
13487
|
+
const kidRepos = kids.map((k) => isObj10(k) ? String(k["repo"] ?? "") : "");
|
|
13488
|
+
if (new Set(kidRepos).size !== kidRepos.length)
|
|
13489
|
+
return bad("children list a repo more than once");
|
|
13490
|
+
if (kidRepos.length !== order2.length || kidRepos.some((r, i) => r !== order2[i]))
|
|
13491
|
+
return bad("children do not cover the integrated repos in order (a repo is missing or extra)");
|
|
13492
|
+
let unchecked = null;
|
|
13493
|
+
const verdicts = [];
|
|
13494
|
+
for (const raw2 of kids) {
|
|
13495
|
+
const c = isObj10(raw2) ? raw2 : {}, repo = String(c["repo"] ?? ""), rel = String(c["receipt_path"] ?? "");
|
|
13496
|
+
if (!repo || !relOk(rel) || !rel.startsWith(`worktrees/${slugOf(repo)}/.loki/runs/`))
|
|
13497
|
+
return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
|
|
13498
|
+
if (outcomes[repo] !== "ok")
|
|
13499
|
+
return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
|
|
13500
|
+
const rp = join57(runDir, rel);
|
|
13501
|
+
let bytes;
|
|
13502
|
+
try {
|
|
13503
|
+
bytes = readFileSync42(rp);
|
|
13504
|
+
} catch {
|
|
13505
|
+
return bad(`child ${repo} receipt missing`);
|
|
13506
|
+
}
|
|
13507
|
+
if (sha2(bytes) !== c["receipt_file_sha256"])
|
|
13508
|
+
return bad(`child ${repo} receipt does not match its recorded hash (swapped or edited)`);
|
|
13509
|
+
const sub = readJson5(rp);
|
|
13510
|
+
if (!isObj10(sub) || sub["receipt_sha256"] !== c["receipt_sha256"] || rh(sub) !== c["receipt_sha256"])
|
|
13511
|
+
return bad(`child ${repo} receipt hash does not recompute`);
|
|
13512
|
+
if (String(sub["verdict"] ?? "") !== c["verdict"])
|
|
13513
|
+
return bad(`child ${repo} verdict differs from the composite`);
|
|
13514
|
+
if (String(sub["head_sha"] ?? "") !== c["head_sha"] || !c["head_sha"])
|
|
13515
|
+
return bad(`child ${repo} head_sha differs from the composite`);
|
|
13516
|
+
verdicts.push(String(c["verdict"]));
|
|
13517
|
+
if (typeof heads[repo] !== "string" || heads[repo] === "")
|
|
13518
|
+
unchecked ??= `child ${repo} has no integrated head to compare (freshness not checked)`;
|
|
13519
|
+
else if (heads[repo] !== c["head_sha"])
|
|
13520
|
+
unchecked ??= `child ${repo} head ${String(c["head_sha"])} is not the integrated head ${String(heads[repo])}`;
|
|
13521
|
+
const r = await verifyOne(rp, deps);
|
|
13522
|
+
if (r.verdict === "TAMPERED")
|
|
13523
|
+
return bad(`child ${repo} failed verify: ${r.reasons.join("; ")}`);
|
|
13524
|
+
if (r.verdict === "UNSIGNED" && signed)
|
|
13525
|
+
unchecked ??= `child ${repo} receipt is unsigned under a signed composite`;
|
|
13526
|
+
if (r.verdict === "UNCHECKED")
|
|
13527
|
+
unchecked ??= `child ${repo} not checked: ${r.reasons.join("; ")}`;
|
|
13528
|
+
}
|
|
13529
|
+
const expect = compositeVerdict(verdicts);
|
|
13530
|
+
if (receipt["verdict"] !== expect)
|
|
13531
|
+
return bad(`recorded verdict ${String(receipt["verdict"])} differs from the children (${expect})`);
|
|
13532
|
+
return { problem: unchecked ? { verdict: "UNCHECKED", reason: `composite: ${unchecked}` } : null, verdict: expect };
|
|
13533
|
+
}
|
|
13534
|
+
function compositeHash(r) {
|
|
13535
|
+
const { composite_sha256: _c, verification: _v, ...body } = r;
|
|
13536
|
+
return sha2(canonicalJson(body));
|
|
13537
|
+
}
|
|
13538
|
+
var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join57(runDir, "group", "units"), readJson5 = (p) => {
|
|
13425
13539
|
try {
|
|
13426
13540
|
return JSON.parse(readFileSync42(p, "utf8"));
|
|
13427
13541
|
} catch {
|
|
@@ -13433,15 +13547,16 @@ var ID_RE, PASSING, sha2 = (b) => createHash13("sha256").update(b).digest("hex")
|
|
|
13433
13547
|
} catch {
|
|
13434
13548
|
return null;
|
|
13435
13549
|
}
|
|
13436
|
-
};
|
|
13550
|
+
}, COMPOSITE_SCHEMA = "loki.v10.composite/1", slugOf = (repo) => repo.replace(/\//g, "__"), isComposite = (r) => r["schema"] === COMPOSITE_SCHEMA, relOk = (p) => p !== "" && !p.startsWith("/") && !p.split("/").includes("..");
|
|
13437
13551
|
var init_seal_group = __esm(() => {
|
|
13552
|
+
init_receipt_signer();
|
|
13438
13553
|
ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
13439
13554
|
PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
13440
13555
|
});
|
|
13441
13556
|
|
|
13442
13557
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
13443
|
-
import { existsSync as
|
|
13444
|
-
import { basename as
|
|
13558
|
+
import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
|
|
13559
|
+
import { basename as basename20, join as join58 } from "path";
|
|
13445
13560
|
function supplyEnabled(env) {
|
|
13446
13561
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
13447
13562
|
}
|
|
@@ -13453,11 +13568,11 @@ function supplyVerdict(verdict, r) {
|
|
|
13453
13568
|
return r.blocked && verdict === "VERIFIED" ? "FAILED" : verdict;
|
|
13454
13569
|
}
|
|
13455
13570
|
function isManifestPath(path) {
|
|
13456
|
-
return MANIFESTS.test(
|
|
13571
|
+
return MANIFESTS.test(basename20(path));
|
|
13457
13572
|
}
|
|
13458
13573
|
function readDeclared(repoDir) {
|
|
13459
13574
|
const p = join58(repoDir, ".loki", DECLARED_FILE);
|
|
13460
|
-
if (!
|
|
13575
|
+
if (!existsSync37(p))
|
|
13461
13576
|
return { deps: [], problem: null };
|
|
13462
13577
|
try {
|
|
13463
13578
|
if (statSync18(p).size > MAX_FILE_BYTES)
|
|
@@ -13483,7 +13598,7 @@ function readDeclared(repoDir) {
|
|
|
13483
13598
|
}
|
|
13484
13599
|
function readAllowlist(repoDir) {
|
|
13485
13600
|
const p = join58(repoDir, ".loki", "supply-allowlist");
|
|
13486
|
-
if (!
|
|
13601
|
+
if (!existsSync37(p))
|
|
13487
13602
|
return new Set;
|
|
13488
13603
|
try {
|
|
13489
13604
|
return new Set(readFileSync43(p, "utf8").split(`
|
|
@@ -13684,9 +13799,9 @@ var init_supply_guard = __esm(() => {
|
|
|
13684
13799
|
});
|
|
13685
13800
|
|
|
13686
13801
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
13687
|
-
import { createHash as
|
|
13688
|
-
import { existsSync as
|
|
13689
|
-
import { basename as
|
|
13802
|
+
import { createHash as createHash15 } from "crypto";
|
|
13803
|
+
import { existsSync as existsSync38, readdirSync as readdirSync18, mkdirSync as mkdirSync23, readFileSync as readFileSync44, writeFileSync as writeFileSync25 } from "fs";
|
|
13804
|
+
import { basename as basename21, isAbsolute as isAbsolute9, join as join59, relative as relative9 } from "path";
|
|
13690
13805
|
function sanitizeReason(s) {
|
|
13691
13806
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
13692
13807
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
@@ -13760,7 +13875,7 @@ function renderReceiptMd(r) {
|
|
|
13760
13875
|
].join(`
|
|
13761
13876
|
`);
|
|
13762
13877
|
}
|
|
13763
|
-
var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) =>
|
|
13878
|
+
var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash15("sha256").update(s).digest("hex"), str3 = (v) => typeof v === "string" && v !== "" ? v : null, strs2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : [], receiptRel = (ctx) => {
|
|
13764
13879
|
const r = relative9(ctx.repoDir, join59(ctx.runDir, "receipt.json"));
|
|
13765
13880
|
return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
13766
13881
|
}, commitStage, sealStage;
|
|
@@ -13863,7 +13978,7 @@ var init_seal = __esm(() => {
|
|
|
13863
13978
|
notProven.add(`Wall test not executed: ${wallNotRun} file(s), no reason recorded`);
|
|
13864
13979
|
if (wallNotRun > 0) {
|
|
13865
13980
|
try {
|
|
13866
|
-
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) =>
|
|
13981
|
+
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
|
|
13867
13982
|
for (const n2 of readdirSync18(join59(ctx.runDir, "wall")).sort())
|
|
13868
13983
|
if (!kept.has(n2))
|
|
13869
13984
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
@@ -13979,7 +14094,7 @@ var init_seal = __esm(() => {
|
|
|
13979
14094
|
provider: ctx.provider,
|
|
13980
14095
|
model: ctx.model,
|
|
13981
14096
|
resumed: o.intake?.resumed === true,
|
|
13982
|
-
events_sha256: sha2562(
|
|
14097
|
+
events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync44(eventsPath) : ""),
|
|
13983
14098
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
13984
14099
|
log_seal: true,
|
|
13985
14100
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -14010,6 +14125,11 @@ var init_seal = __esm(() => {
|
|
|
14010
14125
|
try {
|
|
14011
14126
|
recordRunVerdict(ctx.repoDir, ctx.runId, verdict);
|
|
14012
14127
|
} catch {}
|
|
14128
|
+
if (process.env["LOKI_AUTO_LEARN_PR"] !== "0") {
|
|
14129
|
+
try {
|
|
14130
|
+
appendReceiptFact(ctx.repoDir, { runId: ctx.runId, verdict, receiptSha256: signed ? hash : null, title: str3(ctx.outputs().intake?.title) ?? `run ${ctx.runId}`, headSha: receipt.head_sha });
|
|
14131
|
+
} catch {}
|
|
14132
|
+
}
|
|
14013
14133
|
ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} });
|
|
14014
14134
|
return { status: "completed", data: { ...data, summary: `${verdict} receipt ${hash.slice(0, 12)} ${signed ? `SIGNED kid ${sig.kid}` : "UNSIGNED"}` } };
|
|
14015
14135
|
}
|
|
@@ -14109,9 +14229,9 @@ var init_keys_cmd = __esm(() => {
|
|
|
14109
14229
|
});
|
|
14110
14230
|
|
|
14111
14231
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
14112
|
-
import { existsSync as
|
|
14113
|
-
import { dirname as dirname27, join as join60 } from "path";
|
|
14114
|
-
import { createHash as
|
|
14232
|
+
import { existsSync as existsSync39, readFileSync as readFileSync46, renameSync as renameSync8, statSync as statSync20, writeFileSync as writeFileSync26 } from "fs";
|
|
14233
|
+
import { basename as basename22, dirname as dirname27, join as join60 } from "path";
|
|
14234
|
+
import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
|
|
14115
14235
|
function pubFor(kid, given) {
|
|
14116
14236
|
if (given)
|
|
14117
14237
|
return kidOf2(given) === kid ? given : undefined;
|
|
@@ -14148,10 +14268,10 @@ function checkAttestation(jwt, expectedHash, given) {
|
|
|
14148
14268
|
}
|
|
14149
14269
|
function checkEventLog(receiptPath, receipt) {
|
|
14150
14270
|
const bound = receipt["events_sha256"];
|
|
14151
|
-
if (typeof bound !== "string" || bound ===
|
|
14271
|
+
if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
|
|
14152
14272
|
return null;
|
|
14153
14273
|
const path = join60(dirname27(receiptPath), "events.jsonl");
|
|
14154
|
-
if (!
|
|
14274
|
+
if (!existsSync39(path))
|
|
14155
14275
|
return "events.jsonl is missing";
|
|
14156
14276
|
const raw2 = readFileSync46(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
|
|
14157
14277
|
`).filter((l) => l.trim() !== "");
|
|
@@ -14159,7 +14279,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
14159
14279
|
return "events.jsonl has a forged, edited or missing line";
|
|
14160
14280
|
if (events2.some((e) => e.type === "tamper.detected"))
|
|
14161
14281
|
return "the supervisor detected events.jsonl being modified during the run";
|
|
14162
|
-
const h =
|
|
14282
|
+
const h = createHash16("sha256");
|
|
14163
14283
|
for (const l of lines) {
|
|
14164
14284
|
if (h.copy().digest("hex") === bound)
|
|
14165
14285
|
return null;
|
|
@@ -14178,7 +14298,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14178
14298
|
if (!d)
|
|
14179
14299
|
return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
|
|
14180
14300
|
const lines = readFileSync46(path, "utf8").split(`
|
|
14181
|
-
`).filter((l) => l.trim() !== ""), sha3 =
|
|
14301
|
+
`).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines.slice(0, done + 1).join(`
|
|
14182
14302
|
`) + `
|
|
14183
14303
|
`).digest("hex");
|
|
14184
14304
|
const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
|
|
@@ -14187,7 +14307,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
14187
14307
|
return null;
|
|
14188
14308
|
}
|
|
14189
14309
|
async function verifyReceipt(receiptPath, deps = {}) {
|
|
14190
|
-
if (!
|
|
14310
|
+
if (!existsSync39(receiptPath))
|
|
14191
14311
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
14192
14312
|
let receipt;
|
|
14193
14313
|
try {
|
|
@@ -14197,6 +14317,8 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14197
14317
|
}
|
|
14198
14318
|
if (isEnvelope(receipt))
|
|
14199
14319
|
return verifyDsseReceipt(receipt, { pubFor: (kid) => pubFor(kid, deps.pubkey), hash: computeReceiptHash, attest: (j, h) => checkAttestation(j, h, deps.pubkey) });
|
|
14320
|
+
if (isComposite(receipt))
|
|
14321
|
+
return verifyCompositeFile(receiptPath, receipt, deps);
|
|
14200
14322
|
const recorded = receipt["receipt_sha256"];
|
|
14201
14323
|
const computed = computeReceiptHash(receipt);
|
|
14202
14324
|
if (typeof recorded !== "string" || recorded !== computed) {
|
|
@@ -14234,6 +14356,30 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
14234
14356
|
return { verdict: gp.verdict, reasons: [gp.reason] };
|
|
14235
14357
|
return { verdict: "VERIFIED", reasons: [], receiptSha256: computed };
|
|
14236
14358
|
}
|
|
14359
|
+
async function verifyCompositeFile(receiptPath, receipt, deps) {
|
|
14360
|
+
const computed = compositeHash(receipt);
|
|
14361
|
+
if (receipt["composite_sha256"] !== computed)
|
|
14362
|
+
return { verdict: "TAMPERED", reasons: [`composite_sha256 mismatch: recorded ${JSON.stringify(receipt["composite_sha256"])}, computed ${computed}`] };
|
|
14363
|
+
const c = await verifyComposite(receiptPath, receipt, computeReceiptHash, verifyReceipt, deps);
|
|
14364
|
+
if (c.problem?.verdict === "TAMPERED")
|
|
14365
|
+
return { verdict: "TAMPERED", reasons: [c.problem.reason] };
|
|
14366
|
+
const jwt = (receipt["verification"] ?? {}).jwt ?? null;
|
|
14367
|
+
if (jwt !== null && typeof jwt !== "string")
|
|
14368
|
+
return { verdict: "UNCHECKED", reasons: ["verification.jwt is not a string"] };
|
|
14369
|
+
if (!jwt)
|
|
14370
|
+
return c.problem ? { verdict: c.problem.verdict, reasons: [c.problem.reason] } : { verdict: "UNSIGNED", reasons: [], receiptSha256: computed };
|
|
14371
|
+
const outcome = checkAttestation(jwt, computed, deps.pubkey);
|
|
14372
|
+
if (outcome.status === "unchecked")
|
|
14373
|
+
return { verdict: "UNCHECKED", reasons: [outcome.reason ?? "attestation not checked"] };
|
|
14374
|
+
if (outcome.status === "tampered")
|
|
14375
|
+
return { verdict: "TAMPERED", reasons: [outcome.reason ?? "attestation invalid"] };
|
|
14376
|
+
const shown = receipt["verification"].kid, signed = JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid;
|
|
14377
|
+
if (shown !== undefined && shown !== signed)
|
|
14378
|
+
return { verdict: "TAMPERED", reasons: ["verification.kid differs from the signing kid in the attestation header"] };
|
|
14379
|
+
if (c.problem)
|
|
14380
|
+
return { verdict: c.problem.verdict, reasons: [c.problem.reason] };
|
|
14381
|
+
return { verdict: "VERIFIED", reasons: [], receiptSha256: computed };
|
|
14382
|
+
}
|
|
14237
14383
|
var computeReceiptHash = (receipt) => receiptSha256(receipt);
|
|
14238
14384
|
var init_verify_cmd = __esm(() => {
|
|
14239
14385
|
init_paths();
|
|
@@ -14247,7 +14393,7 @@ var init_verify_cmd = __esm(() => {
|
|
|
14247
14393
|
});
|
|
14248
14394
|
|
|
14249
14395
|
// src/server/app.ts
|
|
14250
|
-
import { existsSync as
|
|
14396
|
+
import { existsSync as existsSync41, readFileSync as readFileSync49, statSync as statSync21 } from "fs";
|
|
14251
14397
|
import { join as join63, resolve as resolve15, sep as sep14 } from "path";
|
|
14252
14398
|
|
|
14253
14399
|
// node_modules/hono/dist/request/constants.js
|
|
@@ -22455,7 +22601,7 @@ var loki_yaml_schema_default = {
|
|
|
22455
22601
|
items: { type: "string", minLength: 1 }
|
|
22456
22602
|
},
|
|
22457
22603
|
workspaces: {
|
|
22458
|
-
description: "Named groups of repos for cross-repo runs (`loki workspace run`).
|
|
22604
|
+
description: "Named groups of repos for cross-repo runs (`loki workspace run`). On by default; LOKI_WORKSPACES=0 disables. Unknown or mistyped keys fail validation.",
|
|
22459
22605
|
type: "object",
|
|
22460
22606
|
additionalProperties: {
|
|
22461
22607
|
type: "object",
|
|
@@ -22479,9 +22625,12 @@ var loki_yaml_schema_default = {
|
|
|
22479
22625
|
additionalProperties: false,
|
|
22480
22626
|
properties: {
|
|
22481
22627
|
command: { type: "string", minLength: 1 },
|
|
22482
|
-
timeout_s: { type: "integer", minimum: 1 }
|
|
22628
|
+
timeout_s: { type: "integer", minimum: 1 },
|
|
22629
|
+
per_stage: { type: "boolean" }
|
|
22483
22630
|
}
|
|
22484
22631
|
},
|
|
22632
|
+
concurrency: { type: "integer", minimum: 1 },
|
|
22633
|
+
plan: { type: "boolean" },
|
|
22485
22634
|
integration_files: { type: "array", items: { type: "string", minLength: 1 } }
|
|
22486
22635
|
}
|
|
22487
22636
|
}
|
|
@@ -24609,7 +24758,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
24609
24758
|
}
|
|
24610
24759
|
|
|
24611
24760
|
// src/server/legacy/shim.ts
|
|
24612
|
-
import { existsSync as
|
|
24761
|
+
import { existsSync as existsSync40, readFileSync as readFileSync48 } from "fs";
|
|
24613
24762
|
import { join as join62 } from "path";
|
|
24614
24763
|
|
|
24615
24764
|
// src/server/legacy/routes.ts
|
|
@@ -24984,7 +25133,7 @@ var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
|
24984
25133
|
var repoVersion = () => {
|
|
24985
25134
|
for (const f of [join62(import.meta.dir, "../../../../../VERSION")]) {
|
|
24986
25135
|
try {
|
|
24987
|
-
if (
|
|
25136
|
+
if (existsSync40(f))
|
|
24988
25137
|
return readFileSync48(f, "utf8").trim();
|
|
24989
25138
|
} catch {}
|
|
24990
25139
|
}
|
|
@@ -25167,7 +25316,7 @@ function legacyShim(opts) {
|
|
|
25167
25316
|
|
|
25168
25317
|
// src/server/app.ts
|
|
25169
25318
|
var MAX_BODY2 = 1e6;
|
|
25170
|
-
var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) =>
|
|
25319
|
+
var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join63(d, "index.html")));
|
|
25171
25320
|
function createApp(opts) {
|
|
25172
25321
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
25173
25322
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -25310,7 +25459,7 @@ function createApp(opts) {
|
|
|
25310
25459
|
return c.json({ error: "not found" }, 404);
|
|
25311
25460
|
const root = resolve15(uiDir);
|
|
25312
25461
|
const f = resolve15(root, `.${p}`);
|
|
25313
|
-
if (f.startsWith(root + sep14) &&
|
|
25462
|
+
if (f.startsWith(root + sep14) && existsSync41(f) && statSync21(f).isFile())
|
|
25314
25463
|
return new Response(Bun.file(f));
|
|
25315
25464
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
25316
25465
|
return c.json({ error: "not found" }, 404);
|