loki-mode 10.10.2 → 10.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/BOARD.md +2 -0
- package/docs/v10/FAILURE-CLASSES.md +13 -0
- package/docs/v10/PROGRESS.md +9 -0
- package/docs/v10/cp-redesign/PARITY.md +188 -0
- package/docs/v10/cp-redesign/legacy/INDEX.md +1167 -0
- package/loki-ts/dist/loki.js +616 -614
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/packages/control-plane/dist/server.js +417 -336
- package/packages/control-plane/src/server/diffstat.ts +77 -0
- package/packages/control-plane/src/server/repos.ts +5 -2
- package/packages/control-plane/src/server/routes/index.ts +1 -1
- package/packages/control-plane/src/server/runs.ts +3 -1
- package/packages/control-plane/ui/dist/assets/index-D27veKDf.js +152 -0
- package/packages/control-plane/ui/dist/index.html +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-BvbxMtZO.js → AdminPage-Dyx5OHmP.js} +1 -1
- package/web-app/dist/assets/{Avatar-Dc7Scg02.js → Avatar-WVxt-kYj.js} +1 -1
- package/web-app/dist/assets/{Badge-BmVuOqbM.js → Badge-16vWJ-6u.js} +1 -1
- package/web-app/dist/assets/{Button-DvzqhxRU.js → Button-CWVPIMTW.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-D8AZo7Ha.js → CockpitPage-D5WhogJK.js} +1 -1
- package/web-app/dist/assets/{ComparePage-NmqaTXDz.js → ComparePage-CjJfjxcT.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-PGl8Kzoq.js → ErrorBoundary-CNnObh81.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-2NY4n9gv.js → EvidenceReceiptPanel-Cn1PE_JV.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-DBO_LHHM.js → GitHubIssuesPanel-CcH9neSU.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-K7m9uJ1N.js → GitHubPRsPanel-C-n6inCN.js} +1 -1
- package/web-app/dist/assets/{HomePage-qNQzAJxM.js → HomePage-BZ7PCPFR.js} +2 -2
- package/web-app/dist/assets/{LoginPage-esu3OK65.js → LoginPage-CJsigIUa.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CCdVvXrA.js → MagicPage-ClZaKmXT.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-bEvugU2t.js → MetricsPage-ELQn9-cf.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-BlEjQ1wX.js → NotFoundPage-Dc_zp3RF.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-Bnug_Yzv.js → ProjectPage-B5K1NDuH.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-M8r1vfpn.js → ProjectsPage-C2l5SiIN.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-NqAJpB48.js → SettingsPage-DkDNp6Pd.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-8FggxL1P.js → ShowcasePage-1zasFSKz.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-3Sz3kFOO.js → SystemSettingsPage-hIm_4NUx.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-nV-6vdWM.js → TeamsPage-DUb_TA22.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-Brbc_VR9.js → TemplatesPage-B1mj-eA4.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-D1yta5ac.js → TerminalOutput-BX1UwelI.js} +1 -1
- package/web-app/dist/assets/{activity-oEeKuqFe.js → activity-B8APKMuh.js} +1 -1
- package/web-app/dist/assets/{bell-3NJtqBRR.js → bell-BS8WL-tI.js} +1 -1
- package/web-app/dist/assets/{bot-CuvJZ6x1.js → bot-e9I6yy12.js} +1 -1
- package/web-app/dist/assets/{check-n4289IZw.js → check-BbgR7Ovw.js} +1 -1
- package/web-app/dist/assets/{chevron-left-CmQeio7h.js → chevron-left-BcDqv7aX.js} +1 -1
- package/web-app/dist/assets/{circle-alert-CmmVW-Pd.js → circle-alert-BnDT8wxe.js} +1 -1
- package/web-app/dist/assets/{clock-QFqk7GlA.js → clock-LPz79lks.js} +1 -1
- package/web-app/dist/assets/{cloud-BNkSokmq.js → cloud-BcgBi5T1.js} +1 -1
- package/web-app/dist/assets/{code-xml-nWeketqj.js → code-xml-5zcykgva.js} +1 -1
- package/web-app/dist/assets/{database-C83MXTmJ.js → database-D9KLqhHq.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-Blg6TO4y.js → dollar-sign-CrINoZ4o.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Tk11oUzm.js → file-code-corner-CA7G72ip.js} +1 -1
- package/web-app/dist/assets/{file-plus-DF_eyA6I.js → file-plus-D8tHdHn_.js} +1 -1
- package/web-app/dist/assets/{globe-D5hDqqmn.js → globe-CT0Ts5bO.js} +1 -1
- package/web-app/dist/assets/{hammer-C2hm9lzl.js → hammer-tUpu0LJk.js} +1 -1
- package/web-app/dist/assets/{index-pMxOIwXq.js → index-DdjeiWUI.js} +3 -3
- package/web-app/dist/assets/{layers-XWQKGXg0.js → layers-BzzJ7w3C.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BRuvorm2.js → loader-circle-BBdMdxIV.js} +1 -1
- package/web-app/dist/assets/{lock-BSGvQf5v.js → lock-Cg9DkEh7.js} +1 -1
- package/web-app/dist/assets/{package-3FJYUYgJ.js → package-DHs8IEJR.js} +1 -1
- package/web-app/dist/assets/{plus-CeIx6g9P.js → plus-70JSAqB1.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-ChutW42g.js → refresh-cw-DjggkboT.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-BdSjUAk-.js → rotate-ccw-c1NNyyL5.js} +1 -1
- package/web-app/dist/assets/{scroll-text-BjFqxpw3.js → scroll-text-B49wQrJ2.js} +1 -1
- package/web-app/dist/assets/{server-C7UEKdol.js → server-BHKG-dDi.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DY8fpEs7.js → shield-alert-Dx9MoBGm.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-Cz1san1g.js → thumbs-up-frEbS59j.js} +1 -1
- package/web-app/dist/assets/{trash-2-D2MvGYeP.js → trash-2-1Gd8pXFG.js} +1 -1
- package/web-app/dist/assets/{trending-down-CxO3OYOB.js → trending-down-BpUeO-FP.js} +1 -1
- package/web-app/dist/assets/{trending-up-DCHKPJLe.js → trending-up-CvhWp8qO.js} +1 -1
- package/web-app/dist/assets/{upload-CdPJpSHC.js → upload-DZuMugqd.js} +1 -1
- package/web-app/dist/assets/{usePolling-DAH6K3ST.js → usePolling-D5KUSkfs.js} +1 -1
- package/web-app/dist/assets/{user-Byyt01W4.js → user-BF9vws84.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/packages/control-plane/ui/dist/assets/index-BrA22FUs.js +0 -152
|
@@ -3696,10 +3696,10 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3696
3696
|
let offset = bm.offset;
|
|
3697
3697
|
let commentEnd = null;
|
|
3698
3698
|
for (const collItem of bm.items) {
|
|
3699
|
-
const { start, key, sep:
|
|
3699
|
+
const { start, key, sep: sep5, value } = collItem;
|
|
3700
3700
|
const keyProps = resolveProps.resolveProps(start, {
|
|
3701
3701
|
indicator: "explicit-key-ind",
|
|
3702
|
-
next: key ??
|
|
3702
|
+
next: key ?? sep5?.[0],
|
|
3703
3703
|
offset,
|
|
3704
3704
|
onError,
|
|
3705
3705
|
parentIndent: bm.indent,
|
|
@@ -3713,7 +3713,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3713
3713
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
3714
3714
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
3715
3715
|
}
|
|
3716
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
3716
|
+
if (!keyProps.anchor && !keyProps.tag && !sep5) {
|
|
3717
3717
|
commentEnd = keyProps.end;
|
|
3718
3718
|
if (keyProps.comment) {
|
|
3719
3719
|
if (map.comment)
|
|
@@ -3738,7 +3738,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3738
3738
|
ctx.atKey = false;
|
|
3739
3739
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
3740
3740
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
3741
|
-
const valueProps = resolveProps.resolveProps(
|
|
3741
|
+
const valueProps = resolveProps.resolveProps(sep5 ?? [], {
|
|
3742
3742
|
indicator: "map-value-ind",
|
|
3743
3743
|
next: value,
|
|
3744
3744
|
offset: keyNode.range[2],
|
|
@@ -3754,7 +3754,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
3754
3754
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
3755
3755
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
3756
3756
|
}
|
|
3757
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
3757
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep5, null, valueProps, onError);
|
|
3758
3758
|
if (ctx.schema.compat)
|
|
3759
3759
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
3760
3760
|
offset = valueNode.range[2];
|
|
@@ -3840,7 +3840,7 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
3840
3840
|
let comment = "";
|
|
3841
3841
|
if (end) {
|
|
3842
3842
|
let hasSpace = false;
|
|
3843
|
-
let
|
|
3843
|
+
let sep5 = "";
|
|
3844
3844
|
for (const token of end) {
|
|
3845
3845
|
const { source, type } = token;
|
|
3846
3846
|
switch (type) {
|
|
@@ -3854,13 +3854,13 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
3854
3854
|
if (!comment)
|
|
3855
3855
|
comment = cb;
|
|
3856
3856
|
else
|
|
3857
|
-
comment +=
|
|
3858
|
-
|
|
3857
|
+
comment += sep5 + cb;
|
|
3858
|
+
sep5 = "";
|
|
3859
3859
|
break;
|
|
3860
3860
|
}
|
|
3861
3861
|
case "newline":
|
|
3862
3862
|
if (comment)
|
|
3863
|
-
|
|
3863
|
+
sep5 += source;
|
|
3864
3864
|
hasSpace = true;
|
|
3865
3865
|
break;
|
|
3866
3866
|
default:
|
|
@@ -3900,18 +3900,18 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
3900
3900
|
let offset = fc.offset + fc.start.source.length;
|
|
3901
3901
|
for (let i = 0;i < fc.items.length; ++i) {
|
|
3902
3902
|
const collItem = fc.items[i];
|
|
3903
|
-
const { start, key, sep:
|
|
3903
|
+
const { start, key, sep: sep5, value } = collItem;
|
|
3904
3904
|
const props = resolveProps.resolveProps(start, {
|
|
3905
3905
|
flow: fcName,
|
|
3906
3906
|
indicator: "explicit-key-ind",
|
|
3907
|
-
next: key ??
|
|
3907
|
+
next: key ?? sep5?.[0],
|
|
3908
3908
|
offset,
|
|
3909
3909
|
onError,
|
|
3910
3910
|
parentIndent: fc.indent,
|
|
3911
3911
|
startOnNewline: false
|
|
3912
3912
|
});
|
|
3913
3913
|
if (!props.found) {
|
|
3914
|
-
if (!props.anchor && !props.tag && !
|
|
3914
|
+
if (!props.anchor && !props.tag && !sep5 && !value) {
|
|
3915
3915
|
if (i === 0 && props.comma)
|
|
3916
3916
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
3917
3917
|
else if (i < fc.items.length - 1)
|
|
@@ -3963,8 +3963,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
3963
3963
|
}
|
|
3964
3964
|
}
|
|
3965
3965
|
}
|
|
3966
|
-
if (!isMap && !
|
|
3967
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
3966
|
+
if (!isMap && !sep5 && !props.found) {
|
|
3967
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep5, null, props, onError);
|
|
3968
3968
|
coll.items.push(valueNode);
|
|
3969
3969
|
offset = valueNode.range[2];
|
|
3970
3970
|
if (isBlock(value))
|
|
@@ -3976,7 +3976,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
3976
3976
|
if (isBlock(key))
|
|
3977
3977
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
3978
3978
|
ctx.atKey = false;
|
|
3979
|
-
const valueProps = resolveProps.resolveProps(
|
|
3979
|
+
const valueProps = resolveProps.resolveProps(sep5 ?? [], {
|
|
3980
3980
|
flow: fcName,
|
|
3981
3981
|
indicator: "map-value-ind",
|
|
3982
3982
|
next: value,
|
|
@@ -3987,8 +3987,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
3987
3987
|
});
|
|
3988
3988
|
if (valueProps.found) {
|
|
3989
3989
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
3990
|
-
if (
|
|
3991
|
-
for (const st of
|
|
3990
|
+
if (sep5)
|
|
3991
|
+
for (const st of sep5) {
|
|
3992
3992
|
if (st === valueProps.found)
|
|
3993
3993
|
break;
|
|
3994
3994
|
if (st.type === "newline") {
|
|
@@ -4005,7 +4005,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
4005
4005
|
else
|
|
4006
4006
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
4007
4007
|
}
|
|
4008
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
4008
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep5, null, valueProps, onError) : null;
|
|
4009
4009
|
if (valueNode) {
|
|
4010
4010
|
if (isBlock(value))
|
|
4011
4011
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -4182,7 +4182,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
4182
4182
|
chompStart = i + 1;
|
|
4183
4183
|
}
|
|
4184
4184
|
let value = "";
|
|
4185
|
-
let
|
|
4185
|
+
let sep5 = "";
|
|
4186
4186
|
let prevMoreIndented = false;
|
|
4187
4187
|
for (let i = 0;i < contentStart; ++i)
|
|
4188
4188
|
value += lines[i][0].slice(trimIndent) + `
|
|
@@ -4200,33 +4200,33 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
4200
4200
|
indent = "";
|
|
4201
4201
|
}
|
|
4202
4202
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
4203
|
-
value +=
|
|
4204
|
-
|
|
4203
|
+
value += sep5 + indent.slice(trimIndent) + content;
|
|
4204
|
+
sep5 = `
|
|
4205
4205
|
`;
|
|
4206
4206
|
} else if (indent.length > trimIndent || content[0] === "\t") {
|
|
4207
|
-
if (
|
|
4208
|
-
|
|
4207
|
+
if (sep5 === " ")
|
|
4208
|
+
sep5 = `
|
|
4209
4209
|
`;
|
|
4210
|
-
else if (!prevMoreIndented &&
|
|
4210
|
+
else if (!prevMoreIndented && sep5 === `
|
|
4211
4211
|
`)
|
|
4212
|
-
|
|
4212
|
+
sep5 = `
|
|
4213
4213
|
|
|
4214
4214
|
`;
|
|
4215
|
-
value +=
|
|
4216
|
-
|
|
4215
|
+
value += sep5 + indent.slice(trimIndent) + content;
|
|
4216
|
+
sep5 = `
|
|
4217
4217
|
`;
|
|
4218
4218
|
prevMoreIndented = true;
|
|
4219
4219
|
} else if (content === "") {
|
|
4220
|
-
if (
|
|
4220
|
+
if (sep5 === `
|
|
4221
4221
|
`)
|
|
4222
4222
|
value += `
|
|
4223
4223
|
`;
|
|
4224
4224
|
else
|
|
4225
|
-
|
|
4225
|
+
sep5 = `
|
|
4226
4226
|
`;
|
|
4227
4227
|
} else {
|
|
4228
|
-
value +=
|
|
4229
|
-
|
|
4228
|
+
value += sep5 + content;
|
|
4229
|
+
sep5 = " ";
|
|
4230
4230
|
prevMoreIndented = false;
|
|
4231
4231
|
}
|
|
4232
4232
|
}
|
|
@@ -4406,27 +4406,27 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
|
|
|
4406
4406
|
trimBoth = /^[ \t]+|[ \t]+$/g;
|
|
4407
4407
|
}
|
|
4408
4408
|
let res = match2[1].replace(trimEnd, "");
|
|
4409
|
-
let
|
|
4409
|
+
let sep5 = " ";
|
|
4410
4410
|
let pos = line2.lastIndex;
|
|
4411
4411
|
while (match2 = line2.exec(source)) {
|
|
4412
4412
|
const lm = match2[1].replace(trimBoth, "");
|
|
4413
4413
|
if (lm === "") {
|
|
4414
|
-
if (
|
|
4414
|
+
if (sep5 === `
|
|
4415
4415
|
`)
|
|
4416
|
-
res +=
|
|
4416
|
+
res += sep5;
|
|
4417
4417
|
else
|
|
4418
|
-
|
|
4418
|
+
sep5 = `
|
|
4419
4419
|
`;
|
|
4420
4420
|
} else {
|
|
4421
|
-
res +=
|
|
4422
|
-
|
|
4421
|
+
res += sep5 + lm;
|
|
4422
|
+
sep5 = " ";
|
|
4423
4423
|
}
|
|
4424
4424
|
pos = line2.lastIndex;
|
|
4425
4425
|
}
|
|
4426
4426
|
const last = /[ \t]*(.*)/sy;
|
|
4427
4427
|
last.lastIndex = pos;
|
|
4428
4428
|
match2 = last.exec(source);
|
|
4429
|
-
return res +
|
|
4429
|
+
return res + sep5 + (match2?.[1] ?? "");
|
|
4430
4430
|
}
|
|
4431
4431
|
function doubleQuotedValue(source, onError) {
|
|
4432
4432
|
let res = "";
|
|
@@ -5207,14 +5207,14 @@ var require_cst_stringify = __commonJS((exports) => {
|
|
|
5207
5207
|
}
|
|
5208
5208
|
}
|
|
5209
5209
|
}
|
|
5210
|
-
function stringifyItem({ start, key, sep:
|
|
5210
|
+
function stringifyItem({ start, key, sep: sep5, value }) {
|
|
5211
5211
|
let res = "";
|
|
5212
5212
|
for (const st of start)
|
|
5213
5213
|
res += st.source;
|
|
5214
5214
|
if (key)
|
|
5215
5215
|
res += stringifyToken(key);
|
|
5216
|
-
if (
|
|
5217
|
-
for (const st of
|
|
5216
|
+
if (sep5)
|
|
5217
|
+
for (const st of sep5)
|
|
5218
5218
|
res += st.source;
|
|
5219
5219
|
if (value)
|
|
5220
5220
|
res += stringifyToken(value);
|
|
@@ -6362,18 +6362,18 @@ var require_parser = __commonJS((exports) => {
|
|
|
6362
6362
|
if (this.type === "map-value-ind") {
|
|
6363
6363
|
const prev = getPrevProps(this.peek(2));
|
|
6364
6364
|
const start = getFirstKeyStartProps(prev);
|
|
6365
|
-
let
|
|
6365
|
+
let sep5;
|
|
6366
6366
|
if (scalar.end) {
|
|
6367
|
-
|
|
6368
|
-
|
|
6367
|
+
sep5 = scalar.end;
|
|
6368
|
+
sep5.push(this.sourceToken);
|
|
6369
6369
|
delete scalar.end;
|
|
6370
6370
|
} else
|
|
6371
|
-
|
|
6371
|
+
sep5 = [this.sourceToken];
|
|
6372
6372
|
const map = {
|
|
6373
6373
|
type: "block-map",
|
|
6374
6374
|
offset: scalar.offset,
|
|
6375
6375
|
indent: scalar.indent,
|
|
6376
|
-
items: [{ start, key: scalar, sep:
|
|
6376
|
+
items: [{ start, key: scalar, sep: sep5 }]
|
|
6377
6377
|
};
|
|
6378
6378
|
this.onKeyLine = true;
|
|
6379
6379
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6527,15 +6527,15 @@ var require_parser = __commonJS((exports) => {
|
|
|
6527
6527
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
6528
6528
|
const start2 = getFirstKeyStartProps(it.start);
|
|
6529
6529
|
const key = it.key;
|
|
6530
|
-
const
|
|
6531
|
-
|
|
6530
|
+
const sep5 = it.sep;
|
|
6531
|
+
sep5.push(this.sourceToken);
|
|
6532
6532
|
delete it.key;
|
|
6533
6533
|
delete it.sep;
|
|
6534
6534
|
this.stack.push({
|
|
6535
6535
|
type: "block-map",
|
|
6536
6536
|
offset: this.offset,
|
|
6537
6537
|
indent: this.indent,
|
|
6538
|
-
items: [{ start: start2, key, sep:
|
|
6538
|
+
items: [{ start: start2, key, sep: sep5 }]
|
|
6539
6539
|
});
|
|
6540
6540
|
} else if (start.length > 0) {
|
|
6541
6541
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -6729,13 +6729,13 @@ var require_parser = __commonJS((exports) => {
|
|
|
6729
6729
|
const prev = getPrevProps(parent);
|
|
6730
6730
|
const start = getFirstKeyStartProps(prev);
|
|
6731
6731
|
fixFlowSeqItems(fc);
|
|
6732
|
-
const
|
|
6733
|
-
|
|
6732
|
+
const sep5 = fc.end.splice(1, fc.end.length);
|
|
6733
|
+
sep5.push(this.sourceToken);
|
|
6734
6734
|
const map = {
|
|
6735
6735
|
type: "block-map",
|
|
6736
6736
|
offset: fc.offset,
|
|
6737
6737
|
indent: fc.indent,
|
|
6738
|
-
items: [{ start, key: fc, sep:
|
|
6738
|
+
items: [{ start, key: fc, sep: sep5 }]
|
|
6739
6739
|
};
|
|
6740
6740
|
this.onKeyLine = true;
|
|
6741
6741
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6960,7 +6960,7 @@ var require_public_api = __commonJS((exports) => {
|
|
|
6960
6960
|
|
|
6961
6961
|
// src/server/app.ts
|
|
6962
6962
|
import { existsSync as existsSync21, statSync as statSync14 } from "fs";
|
|
6963
|
-
import { join as
|
|
6963
|
+
import { join as join34, resolve as resolve7, sep as sep13 } from "path";
|
|
6964
6964
|
|
|
6965
6965
|
// node_modules/hono/dist/request/constants.js
|
|
6966
6966
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -12779,12 +12779,89 @@ function writeAnswer(dir, source, run, answer) {
|
|
|
12779
12779
|
return { status: 200, body: { ok: true, path, resume: `loki answer ${run}` } };
|
|
12780
12780
|
}
|
|
12781
12781
|
|
|
12782
|
+
// src/server/diffstat.ts
|
|
12783
|
+
import { execFileSync } from "child_process";
|
|
12784
|
+
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, realpathSync as realpathSync2 } from "fs";
|
|
12785
|
+
import { join as join3, sep as sep2 } from "path";
|
|
12786
|
+
var ID2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
12787
|
+
var SHA = /^[0-9a-f]{40,64}$/;
|
|
12788
|
+
var MAX_RECEIPT_BYTES = 1024 * 1024;
|
|
12789
|
+
var GIT_TIMEOUT_MS = 5000;
|
|
12790
|
+
var cache = new Map;
|
|
12791
|
+
var gitEnv = () => ({ PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" });
|
|
12792
|
+
function parseNumstat(out) {
|
|
12793
|
+
const files = [];
|
|
12794
|
+
for (const line of out.split(`
|
|
12795
|
+
`)) {
|
|
12796
|
+
const m = /^(\d+|-)\t(\d+|-)\t(.+)$/.exec(line);
|
|
12797
|
+
if (!m)
|
|
12798
|
+
continue;
|
|
12799
|
+
files.push({ path: m[3], added: m[1] === "-" ? null : Number(m[1]), removed: m[2] === "-" ? null : Number(m[2]) });
|
|
12800
|
+
}
|
|
12801
|
+
return files;
|
|
12802
|
+
}
|
|
12803
|
+
function readReceipt(runDir) {
|
|
12804
|
+
let fd;
|
|
12805
|
+
try {
|
|
12806
|
+
fd = openSync2(join3(runDir, "receipt.json"), "r");
|
|
12807
|
+
} catch {
|
|
12808
|
+
return null;
|
|
12809
|
+
}
|
|
12810
|
+
try {
|
|
12811
|
+
const st = fstatSync2(fd);
|
|
12812
|
+
if (!st.isFile() || st.size > MAX_RECEIPT_BYTES)
|
|
12813
|
+
return null;
|
|
12814
|
+
const buf = Buffer.alloc(st.size);
|
|
12815
|
+
readSync2(fd, buf, 0, st.size, 0);
|
|
12816
|
+
const j = JSON.parse(buf.toString("utf8"));
|
|
12817
|
+
return j && typeof j === "object" ? j : null;
|
|
12818
|
+
} catch {
|
|
12819
|
+
return null;
|
|
12820
|
+
} finally {
|
|
12821
|
+
closeSync2(fd);
|
|
12822
|
+
}
|
|
12823
|
+
}
|
|
12824
|
+
function diffStatFor(db, source, run) {
|
|
12825
|
+
if (!ID2.test(source) || !ID2.test(run) || source.includes("..") || run.includes(".."))
|
|
12826
|
+
return null;
|
|
12827
|
+
const key = `${source}/${run}`;
|
|
12828
|
+
const hit = cache.get(key);
|
|
12829
|
+
if (hit)
|
|
12830
|
+
return hit;
|
|
12831
|
+
const repo = db.select({ p: localRepos.realpath }).from(localRepos).where(and(eq(localRepos.sourceId, source))).get();
|
|
12832
|
+
if (!repo)
|
|
12833
|
+
return null;
|
|
12834
|
+
let repoReal, runsRoot, runDir;
|
|
12835
|
+
try {
|
|
12836
|
+
repoReal = realpathSync2(repo.p);
|
|
12837
|
+
runsRoot = realpathSync2(join3(repoReal, ".loki", "runs"));
|
|
12838
|
+
runDir = realpathSync2(join3(runsRoot, run));
|
|
12839
|
+
} catch {
|
|
12840
|
+
return null;
|
|
12841
|
+
}
|
|
12842
|
+
if (runDir !== join3(runsRoot, run) || !runDir.startsWith(runsRoot + sep2))
|
|
12843
|
+
return null;
|
|
12844
|
+
const r = readReceipt(runDir);
|
|
12845
|
+
const base = typeof r?.base_sha === "string" ? r.base_sha : "", head = typeof r?.head_sha === "string" ? r.head_sha : "";
|
|
12846
|
+
if (!SHA.test(base) || !SHA.test(head))
|
|
12847
|
+
return null;
|
|
12848
|
+
try {
|
|
12849
|
+
const out = execFileSync("git", ["-C", repoReal, "-c", "core.quotePath=false", "diff", "--numstat", "--no-renames", `${base}..${head}`, "--"], { env: gitEnv(), timeout: GIT_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
12850
|
+
const files = parseNumstat(out);
|
|
12851
|
+
const stat = { base, head, files, added: files.reduce((n2, f) => n2 + (f.added ?? 0), 0), removed: files.reduce((n2, f) => n2 + (f.removed ?? 0), 0), source: "git" };
|
|
12852
|
+
cache.set(key, stat);
|
|
12853
|
+
return stat;
|
|
12854
|
+
} catch {
|
|
12855
|
+
return null;
|
|
12856
|
+
}
|
|
12857
|
+
}
|
|
12858
|
+
|
|
12782
12859
|
// src/server/integrity.ts
|
|
12783
12860
|
import { createHash, createPublicKey, verify } from "crypto";
|
|
12784
12861
|
import { readFileSync as readFileSync2 } from "fs";
|
|
12785
12862
|
import { homedir as homedir2 } from "os";
|
|
12786
|
-
import { join as
|
|
12787
|
-
var
|
|
12863
|
+
import { join as join4 } from "path";
|
|
12864
|
+
var SHA2 = /^[0-9a-f]{64}$/;
|
|
12788
12865
|
var SEAL_LINE = "log.sealed";
|
|
12789
12866
|
var REDACTED = /\[REDACTED(?::[A-Z_]+)?\]/;
|
|
12790
12867
|
var REDACTED_NOTE = "log redacted before ingest; seal not checkable";
|
|
@@ -12801,7 +12878,7 @@ function localKeyInfo(env = process.env) {
|
|
|
12801
12878
|
if (inline)
|
|
12802
12879
|
pub = createPublicKey(inline);
|
|
12803
12880
|
else {
|
|
12804
|
-
const file = given ||
|
|
12881
|
+
const file = given || join4(env["HOME"] || homedir2(), ".loki", "keys", "receipt-ed25519.pem");
|
|
12805
12882
|
try {
|
|
12806
12883
|
pub = createPublicKey(readFileSync2(file));
|
|
12807
12884
|
} catch (e) {
|
|
@@ -12866,7 +12943,7 @@ function verifyRunIntegrity(evs, opts = {}) {
|
|
|
12866
12943
|
const redacted = REDACTED.test(JSON.stringify(doneAt >= 0 ? evs.slice(0, doneAt + 2).map((e) => e.data) : evs.map((e) => e.data)));
|
|
12867
12944
|
for (const i of sealedAt) {
|
|
12868
12945
|
const d = evs[i].data;
|
|
12869
|
-
if (typeof d["receipt_sha256"] !== "string" || !
|
|
12946
|
+
if (typeof d["receipt_sha256"] !== "string" || !SHA2.test(d["receipt_sha256"]))
|
|
12870
12947
|
hashBad.push(`receipt.sealed at seq ${evs[i].seq} has no valid receipt_sha256`);
|
|
12871
12948
|
const bound = d["events_sha256"];
|
|
12872
12949
|
if (typeof bound === "string") {
|
|
@@ -13054,7 +13131,7 @@ function taskTitle(db, sourceId, runId) {
|
|
|
13054
13131
|
}
|
|
13055
13132
|
var withLive = (db, r) => {
|
|
13056
13133
|
const p = { ...parseRun(r), title: taskTitle(db, r.sourceId, r.runId) };
|
|
13057
|
-
return r.endedAt ? { ...p, current_stage: null, files_touched: [] } : { ...p, ...liveInfo(loadEvents(db, r.sourceId, r.runId)) };
|
|
13134
|
+
return r.endedAt ? { ...p, current_stage: null, files_touched: [], diff_stat: diffStatFor(db, r.sourceId, r.runId) } : { ...p, ...liveInfo(loadEvents(db, r.sourceId, r.runId)), diff_stat: null };
|
|
13058
13135
|
};
|
|
13059
13136
|
function liveInfo(evs) {
|
|
13060
13137
|
const open = [];
|
|
@@ -13261,19 +13338,19 @@ function tokenGuard(token) {
|
|
|
13261
13338
|
|
|
13262
13339
|
// src/shipper/backfill.ts
|
|
13263
13340
|
import { readdirSync } from "fs";
|
|
13264
|
-
import { join as
|
|
13341
|
+
import { join as join6 } from "path";
|
|
13265
13342
|
|
|
13266
13343
|
// src/shipper/ship.ts
|
|
13267
13344
|
import { createHash as createHash4 } from "crypto";
|
|
13268
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as
|
|
13345
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync3, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
13269
13346
|
import { hostname } from "os";
|
|
13270
|
-
import { join as
|
|
13347
|
+
import { join as join5 } from "path";
|
|
13271
13348
|
var BATCH = 200;
|
|
13272
13349
|
var MAX_BYTES = 800000;
|
|
13273
13350
|
function sourceId(repoDir) {
|
|
13274
13351
|
let real2 = repoDir;
|
|
13275
13352
|
try {
|
|
13276
|
-
real2 =
|
|
13353
|
+
real2 = realpathSync3(repoDir);
|
|
13277
13354
|
} catch {}
|
|
13278
13355
|
return createHash4("sha256").update(`${hostname()}\x00${real2}`).digest("hex").slice(0, 16);
|
|
13279
13356
|
}
|
|
@@ -13282,24 +13359,24 @@ var redactEvent = (e) => ({ ...e, data: redactDeep(e.data) });
|
|
|
13282
13359
|
var backoffMs = (failures, rnd = Math.random) => Math.min(60000, 1000 * 2 ** Math.max(0, failures - 1)) * (1 + 0.25 * rnd());
|
|
13283
13360
|
function readShip(runDir, url) {
|
|
13284
13361
|
try {
|
|
13285
|
-
const s = JSON.parse(readFileSync3(
|
|
13362
|
+
const s = JSON.parse(readFileSync3(join5(runDir, "ship.json"), "utf8"));
|
|
13286
13363
|
if (s.url === url && Number.isInteger(s.acked_seq))
|
|
13287
13364
|
return { acked_seq: s.acked_seq, url };
|
|
13288
13365
|
} catch {}
|
|
13289
13366
|
return { acked_seq: -1, url };
|
|
13290
13367
|
}
|
|
13291
13368
|
function writeShip(runDir, s) {
|
|
13292
|
-
const tmp =
|
|
13369
|
+
const tmp = join5(runDir, `ship.json.${process.pid}.tmp`);
|
|
13293
13370
|
writeFileSync2(tmp, JSON.stringify(s) + `
|
|
13294
13371
|
`);
|
|
13295
|
-
renameSync(tmp,
|
|
13372
|
+
renameSync(tmp, join5(runDir, "ship.json"));
|
|
13296
13373
|
}
|
|
13297
13374
|
async function shipRun(o) {
|
|
13298
13375
|
const st = readShip(o.runDir, o.url);
|
|
13299
|
-
const all = readEvents(
|
|
13376
|
+
const all = readEvents(join5(o.runDir, "events.jsonl"));
|
|
13300
13377
|
let lines = 0;
|
|
13301
13378
|
try {
|
|
13302
|
-
lines = readFileSync3(
|
|
13379
|
+
lines = readFileSync3(join5(o.runDir, "events.jsonl"), "utf8").split(`
|
|
13303
13380
|
`).filter((l) => l.trim() !== "").length;
|
|
13304
13381
|
} catch {}
|
|
13305
13382
|
const skipped = lines - all.length;
|
|
@@ -13342,7 +13419,7 @@ async function shipRun(o) {
|
|
|
13342
13419
|
return { ok: true, sent, acked_seq: st.acked_seq, pending: 0 };
|
|
13343
13420
|
}
|
|
13344
13421
|
var shipEnabled = (env) => env.LOKI_CONTROL_URL || null;
|
|
13345
|
-
var hasSpool = (runDir) => existsSync3(
|
|
13422
|
+
var hasSpool = (runDir) => existsSync3(join5(runDir, "events.jsonl"));
|
|
13346
13423
|
|
|
13347
13424
|
// src/shipper/backfill.ts
|
|
13348
13425
|
async function backfill(o) {
|
|
@@ -13352,7 +13429,7 @@ async function backfill(o) {
|
|
|
13352
13429
|
return { runs: 0, sent: 0, failed: [] };
|
|
13353
13430
|
const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
13354
13431
|
const attempts = o.attempts ?? 3;
|
|
13355
|
-
const root =
|
|
13432
|
+
const root = join6(o.repoDir, ".loki", "runs");
|
|
13356
13433
|
let ids = [];
|
|
13357
13434
|
try {
|
|
13358
13435
|
ids = readdirSync(root).sort();
|
|
@@ -13363,7 +13440,7 @@ async function backfill(o) {
|
|
|
13363
13440
|
let runs2 = 0, sent = 0;
|
|
13364
13441
|
const failed = [];
|
|
13365
13442
|
for (const id of ids) {
|
|
13366
|
-
const runDir =
|
|
13443
|
+
const runDir = join6(root, id);
|
|
13367
13444
|
if (!hasSpool(runDir))
|
|
13368
13445
|
continue;
|
|
13369
13446
|
runs2++;
|
|
@@ -13382,17 +13459,20 @@ async function backfill(o) {
|
|
|
13382
13459
|
return { runs: runs2, sent, failed };
|
|
13383
13460
|
}
|
|
13384
13461
|
|
|
13462
|
+
// src/server/repos.ts
|
|
13463
|
+
import { basename as basename2, resolve as resolve2 } from "path";
|
|
13464
|
+
|
|
13385
13465
|
// src/shipper/discover.ts
|
|
13386
|
-
import { readFileSync as readFileSync5, realpathSync as
|
|
13387
|
-
import { basename, dirname, join as
|
|
13466
|
+
import { readFileSync as readFileSync5, realpathSync as realpathSync4, statSync as statSync2 } from "fs";
|
|
13467
|
+
import { basename, dirname, join as join8, resolve } from "path";
|
|
13388
13468
|
|
|
13389
13469
|
// src/shipper/watch.ts
|
|
13390
13470
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
|
|
13391
13471
|
import { homedir as homedir3 } from "os";
|
|
13392
|
-
import { join as
|
|
13472
|
+
import { join as join7 } from "path";
|
|
13393
13473
|
function registryRepos(env) {
|
|
13394
13474
|
try {
|
|
13395
|
-
const reg = JSON.parse(readFileSync4(
|
|
13475
|
+
const reg = JSON.parse(readFileSync4(join7(env.HOME || homedir3(), ".loki", "dashboard", "projects.json"), "utf8"));
|
|
13396
13476
|
return Object.values(reg.projects ?? {}).map((p) => p.path).filter((p) => typeof p === "string" && p.startsWith("/"));
|
|
13397
13477
|
} catch {
|
|
13398
13478
|
return [];
|
|
@@ -13404,7 +13484,7 @@ function discoverLocalRepos(repoDir, env = process.env) {
|
|
|
13404
13484
|
const out = new Map;
|
|
13405
13485
|
for (const dir of [repoDir, ...registryRepos(env)]) {
|
|
13406
13486
|
try {
|
|
13407
|
-
const real2 =
|
|
13487
|
+
const real2 = realpathSync4(dir);
|
|
13408
13488
|
if (!statSync2(real2).isDirectory())
|
|
13409
13489
|
continue;
|
|
13410
13490
|
const id = sourceId(real2);
|
|
@@ -13427,17 +13507,18 @@ function syncLocalRepos(db, repoDir, env = process.env) {
|
|
|
13427
13507
|
function repoNames(db) {
|
|
13428
13508
|
return [...new Set(db.select({ name: localRepos.name }).from(localRepos).all().map((r) => r.name))].sort();
|
|
13429
13509
|
}
|
|
13430
|
-
function mountRepos(act, db, peerIsLoopback2) {
|
|
13431
|
-
|
|
13510
|
+
function mountRepos(act, db, peerIsLoopback2, repoDir) {
|
|
13511
|
+
const defaultRepo = repoDir ? basename2(resolve2(repoDir)) || null : null;
|
|
13512
|
+
act.get("/v1/repos", (c) => peerIsLoopback2(c) ? c.json({ repos: repoNames(db), default_repo: defaultRepo }) : c.json({ error: "loopback only" }, 403));
|
|
13432
13513
|
}
|
|
13433
13514
|
|
|
13434
13515
|
// src/server/routes/artifacts.ts
|
|
13435
|
-
import { closeSync as
|
|
13436
|
-
import { join as
|
|
13516
|
+
import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readSync as readSync3, realpathSync as realpathSync5 } from "fs";
|
|
13517
|
+
import { join as join9, sep as sep3 } from "path";
|
|
13437
13518
|
var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
|
|
13438
13519
|
var EVENTS_DEFAULT = 500;
|
|
13439
13520
|
var EVENTS_MAX = 1000;
|
|
13440
|
-
var
|
|
13521
|
+
var ID3 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
13441
13522
|
var NAME = /^(?:issue\.json|plan\.json|receipt\.json|receipt\.md|report\.md|task\.md|diff\.patch|evidence\/[A-Za-z0-9][A-Za-z0-9._-]{0,99}\.png)$/;
|
|
13442
13523
|
var TYPES = {
|
|
13443
13524
|
json: "application/json; charset=utf-8",
|
|
@@ -13445,16 +13526,16 @@ var TYPES = {
|
|
|
13445
13526
|
patch: "text/plain; charset=utf-8",
|
|
13446
13527
|
png: "image/png"
|
|
13447
13528
|
};
|
|
13448
|
-
var safeId = (s) =>
|
|
13529
|
+
var safeId = (s) => ID3.test(s) && !s.includes("..");
|
|
13449
13530
|
function readCapped(path) {
|
|
13450
13531
|
let fd;
|
|
13451
13532
|
try {
|
|
13452
|
-
fd =
|
|
13533
|
+
fd = openSync3(path, "r");
|
|
13453
13534
|
} catch {
|
|
13454
13535
|
return { status: 404 };
|
|
13455
13536
|
}
|
|
13456
13537
|
try {
|
|
13457
|
-
const st =
|
|
13538
|
+
const st = fstatSync3(fd);
|
|
13458
13539
|
if (!st.isFile())
|
|
13459
13540
|
return { status: 404 };
|
|
13460
13541
|
if (st.size > MAX_ARTIFACT_BYTES)
|
|
@@ -13462,14 +13543,14 @@ function readCapped(path) {
|
|
|
13462
13543
|
const buf = Buffer.alloc(st.size);
|
|
13463
13544
|
let off = 0;
|
|
13464
13545
|
while (off < st.size) {
|
|
13465
|
-
const n2 =
|
|
13546
|
+
const n2 = readSync3(fd, buf, off, st.size - off, off);
|
|
13466
13547
|
if (n2 <= 0)
|
|
13467
13548
|
break;
|
|
13468
13549
|
off += n2;
|
|
13469
13550
|
}
|
|
13470
13551
|
return { status: 200, body: buf.subarray(0, off) };
|
|
13471
13552
|
} finally {
|
|
13472
|
-
|
|
13553
|
+
closeSync3(fd);
|
|
13473
13554
|
}
|
|
13474
13555
|
}
|
|
13475
13556
|
function mount(ctx) {
|
|
@@ -13514,19 +13595,19 @@ function mount(ctx) {
|
|
|
13514
13595
|
return c.json({ error: "artifact not available" }, 404);
|
|
13515
13596
|
let runsRoot, runDir, file;
|
|
13516
13597
|
try {
|
|
13517
|
-
runsRoot =
|
|
13518
|
-
runDir =
|
|
13598
|
+
runsRoot = realpathSync5(join9(repo.p, ".loki", "runs"));
|
|
13599
|
+
runDir = realpathSync5(join9(runsRoot, run));
|
|
13519
13600
|
} catch {
|
|
13520
13601
|
return c.json({ error: "artifact not found" }, 404);
|
|
13521
13602
|
}
|
|
13522
|
-
if (runDir !==
|
|
13603
|
+
if (runDir !== join9(runsRoot, run))
|
|
13523
13604
|
return c.json({ error: "artifact not found" }, 404);
|
|
13524
13605
|
try {
|
|
13525
|
-
file =
|
|
13606
|
+
file = realpathSync5(join9(runDir, name));
|
|
13526
13607
|
} catch {
|
|
13527
13608
|
return c.json({ error: "artifact not found" }, 404);
|
|
13528
13609
|
}
|
|
13529
|
-
if (!file.startsWith(runDir +
|
|
13610
|
+
if (!file.startsWith(runDir + sep3))
|
|
13530
13611
|
return c.json({ error: "artifact not found" }, 404);
|
|
13531
13612
|
const r = readCapped(file);
|
|
13532
13613
|
if (r.status === 413)
|
|
@@ -13563,9 +13644,9 @@ function mount2(ctx) {
|
|
|
13563
13644
|
}
|
|
13564
13645
|
|
|
13565
13646
|
// src/server/routes/checkpoints.ts
|
|
13566
|
-
import { execFileSync } from "child_process";
|
|
13567
|
-
import { appendFileSync, copyFileSync, cpSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync7, realpathSync as
|
|
13568
|
-
import { join as
|
|
13647
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
13648
|
+
import { appendFileSync, copyFileSync, cpSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync7, realpathSync as realpathSync6, rmSync, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13649
|
+
import { join as join11, sep as sep4 } from "path";
|
|
13569
13650
|
|
|
13570
13651
|
// src/server/audit.ts
|
|
13571
13652
|
var clip = (s, n2) => s === undefined ? null : s.slice(0, n2);
|
|
@@ -13580,7 +13661,7 @@ function audit3(db, e) {
|
|
|
13580
13661
|
|
|
13581
13662
|
// src/server/routes/cost_ledger.ts
|
|
13582
13663
|
import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
|
|
13583
|
-
import { join as
|
|
13664
|
+
import { join as join10 } from "path";
|
|
13584
13665
|
var isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
13585
13666
|
var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
13586
13667
|
var r6 = (n2) => Math.round(n2 * 1e6) / 1e6;
|
|
@@ -13611,7 +13692,7 @@ var DEFAULT_PRICING = {
|
|
|
13611
13692
|
"gpt-5.6-luna": { input: 0.5, output: 4 }
|
|
13612
13693
|
};
|
|
13613
13694
|
function pricing(lokiDir) {
|
|
13614
|
-
const d = readJson(
|
|
13695
|
+
const d = readJson(join10(lokiDir, "pricing.json"));
|
|
13615
13696
|
const models = isObj2(d) && isObj2(d.models) ? d.models : {};
|
|
13616
13697
|
return { ...DEFAULT_PRICING, ...models };
|
|
13617
13698
|
}
|
|
@@ -13624,7 +13705,7 @@ function modelCost(table, model, inp, out, cr = 0, cw = 0) {
|
|
|
13624
13705
|
return r6((inp * ir + out * or2 + cr * crr + cw * cwr) / 1e6);
|
|
13625
13706
|
}
|
|
13626
13707
|
function iterationRecords(lokiDir) {
|
|
13627
|
-
const dir =
|
|
13708
|
+
const dir = join10(lokiDir, "metrics", "efficiency");
|
|
13628
13709
|
let names = [];
|
|
13629
13710
|
try {
|
|
13630
13711
|
names = readdirSync3(dir).filter((n2) => /^iteration-.*\.json$/.test(n2)).sort();
|
|
@@ -13633,7 +13714,7 @@ function iterationRecords(lokiDir) {
|
|
|
13633
13714
|
}
|
|
13634
13715
|
const out = [];
|
|
13635
13716
|
for (const n2 of names) {
|
|
13636
|
-
const d = readJson(
|
|
13717
|
+
const d = readJson(join10(dir, n2));
|
|
13637
13718
|
if (isObj2(d))
|
|
13638
13719
|
out.push(d);
|
|
13639
13720
|
}
|
|
@@ -13670,7 +13751,7 @@ function costSnapshot(lokiDir) {
|
|
|
13670
13751
|
}
|
|
13671
13752
|
}
|
|
13672
13753
|
if (tIn === 0 && tOut === 0) {
|
|
13673
|
-
const ctx = readJson(
|
|
13754
|
+
const ctx = readJson(join10(lokiDir, "context", "tracking.json"));
|
|
13674
13755
|
const totals = isObj2(ctx) && isObj2(ctx.totals) ? ctx.totals : {};
|
|
13675
13756
|
tIn = num0(totals.total_input);
|
|
13676
13757
|
tOut = num0(totals.total_output);
|
|
@@ -13696,7 +13777,7 @@ function costSnapshot(lokiDir) {
|
|
|
13696
13777
|
}
|
|
13697
13778
|
}
|
|
13698
13779
|
let limit = null, used = null, remaining = null;
|
|
13699
|
-
const bd = readJson(
|
|
13780
|
+
const bd = readJson(join10(lokiDir, "metrics", "budget.json"));
|
|
13700
13781
|
if (isObj2(bd) && isNum(bd.limit)) {
|
|
13701
13782
|
limit = bd.limit;
|
|
13702
13783
|
if (recorded && !unknownUsd) {
|
|
@@ -13747,7 +13828,7 @@ function budgetSnapshot(lokiDir) {
|
|
|
13747
13828
|
total += priced(table, d);
|
|
13748
13829
|
}
|
|
13749
13830
|
let limit = null;
|
|
13750
|
-
const bd = readJson(
|
|
13831
|
+
const bd = readJson(join10(lokiDir, "metrics", "budget.json"));
|
|
13751
13832
|
if (isObj2(bd)) {
|
|
13752
13833
|
const l = bd.limit || bd.budget_limit;
|
|
13753
13834
|
limit = isNum(l) ? l : null;
|
|
@@ -13798,19 +13879,19 @@ function costTimeline(lokiDir) {
|
|
|
13798
13879
|
}
|
|
13799
13880
|
const runs2 = [];
|
|
13800
13881
|
let projectTotal = 0, runsMeasured = 0;
|
|
13801
|
-
const proofs =
|
|
13882
|
+
const proofs = join10(lokiDir, "proofs");
|
|
13802
13883
|
let entries = [];
|
|
13803
13884
|
try {
|
|
13804
13885
|
entries = readdirSync3(proofs).sort();
|
|
13805
13886
|
} catch {}
|
|
13806
13887
|
for (const e of entries) {
|
|
13807
13888
|
try {
|
|
13808
|
-
if (!statSync3(
|
|
13889
|
+
if (!statSync3(join10(proofs, e)).isDirectory())
|
|
13809
13890
|
continue;
|
|
13810
13891
|
} catch {
|
|
13811
13892
|
continue;
|
|
13812
13893
|
}
|
|
13813
|
-
const p = readJson(
|
|
13894
|
+
const p = readJson(join10(proofs, e, "proof.json"));
|
|
13814
13895
|
if (!isObj2(p))
|
|
13815
13896
|
continue;
|
|
13816
13897
|
const cost = isObj2(p.cost) ? p.cost : {};
|
|
@@ -13840,7 +13921,7 @@ function costTimeline(lokiDir) {
|
|
|
13840
13921
|
...recorded || runsMeasured ? {} : { not_measured: NOT_MEASURED }
|
|
13841
13922
|
};
|
|
13842
13923
|
}
|
|
13843
|
-
var lokiDirOf = (repoDir) =>
|
|
13924
|
+
var lokiDirOf = (repoDir) => join10(repoDir, ".loki");
|
|
13844
13925
|
var lokiDirExists = (repoDir) => existsSync4(lokiDirOf(repoDir));
|
|
13845
13926
|
function mount3(ctx) {
|
|
13846
13927
|
const deny = { error: "loopback only without a token" };
|
|
@@ -13854,8 +13935,8 @@ var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
|
|
|
13854
13935
|
var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
13855
13936
|
var MAX_CHECKPOINTS = 50;
|
|
13856
13937
|
var validCheckpointId = (id) => SAFE_ID.test(id);
|
|
13857
|
-
var storeOf = (repoDir) =>
|
|
13858
|
-
var lokiOf = (repoDir) =>
|
|
13938
|
+
var storeOf = (repoDir) => join11(repoDir, ".loki", "state", "checkpoints");
|
|
13939
|
+
var lokiOf = (repoDir) => join11(repoDir, ".loki");
|
|
13859
13940
|
var readObj = (f) => {
|
|
13860
13941
|
try {
|
|
13861
13942
|
const d = JSON.parse(readFileSync7(f, "utf8"));
|
|
@@ -13869,7 +13950,7 @@ function listCheckpoints(repoDir, limit = 20) {
|
|
|
13869
13950
|
const store = storeOf(repoDir);
|
|
13870
13951
|
let lines = [];
|
|
13871
13952
|
try {
|
|
13872
|
-
lines = readFileSync7(
|
|
13953
|
+
lines = readFileSync7(join11(store, "index.jsonl"), "utf8").split(`
|
|
13873
13954
|
`);
|
|
13874
13955
|
} catch {
|
|
13875
13956
|
return [];
|
|
@@ -13897,7 +13978,7 @@ function listCheckpoints(repoDir, limit = 20) {
|
|
|
13897
13978
|
message: str2(raw2.task) ?? str2(raw2.message) ?? str2(raw2.task_description),
|
|
13898
13979
|
iteration: Number.isInteger(it) ? it : null
|
|
13899
13980
|
};
|
|
13900
|
-
const meta = readObj(
|
|
13981
|
+
const meta = readObj(join11(store, id, "metadata.json"));
|
|
13901
13982
|
if (meta) {
|
|
13902
13983
|
cp.git_branch = str2(meta.git_branch);
|
|
13903
13984
|
cp.provider = str2(meta.provider);
|
|
@@ -13905,7 +13986,7 @@ function listCheckpoints(repoDir, limit = 20) {
|
|
|
13905
13986
|
cp.message ??= str2(meta.task_description);
|
|
13906
13987
|
cp.git_sha ??= str2(meta.git_sha);
|
|
13907
13988
|
cp.created_at ??= str2(meta.timestamp);
|
|
13908
|
-
cp.files_count = countFiles(
|
|
13989
|
+
cp.files_count = countFiles(join11(store, id));
|
|
13909
13990
|
}
|
|
13910
13991
|
out.push(cp);
|
|
13911
13992
|
}
|
|
@@ -13916,7 +13997,7 @@ function countFiles(dir) {
|
|
|
13916
13997
|
const walk = (d, top) => {
|
|
13917
13998
|
for (const e of readdirSync4(d, { withFileTypes: true })) {
|
|
13918
13999
|
if (e.isDirectory())
|
|
13919
|
-
walk(
|
|
14000
|
+
walk(join11(d, e.name), false);
|
|
13920
14001
|
else if (e.isFile() && !(top && e.name === "metadata.json"))
|
|
13921
14002
|
n2++;
|
|
13922
14003
|
}
|
|
@@ -13927,11 +14008,11 @@ function countFiles(dir) {
|
|
|
13927
14008
|
return n2;
|
|
13928
14009
|
}
|
|
13929
14010
|
function getCheckpoint(repoDir, id) {
|
|
13930
|
-
return validCheckpointId(id) ? readObj(
|
|
14011
|
+
return validCheckpointId(id) ? readObj(join11(storeOf(repoDir), id, "metadata.json")) : null;
|
|
13931
14012
|
}
|
|
13932
14013
|
var gitHead = (repoDir) => {
|
|
13933
14014
|
try {
|
|
13934
|
-
return
|
|
14015
|
+
return execFileSync2("git", ["rev-parse", "HEAD"], { cwd: repoDir, timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).toString().trim() || null;
|
|
13935
14016
|
} catch {
|
|
13936
14017
|
return null;
|
|
13937
14018
|
}
|
|
@@ -13939,69 +14020,69 @@ var gitHead = (repoDir) => {
|
|
|
13939
14020
|
function createCheckpoint(repoDir, message, now = new Date) {
|
|
13940
14021
|
const store = storeOf(repoDir), loki = lokiOf(repoDir);
|
|
13941
14022
|
mkdirSync3(store, { recursive: true });
|
|
13942
|
-
const id = `chk-${stamp(now)}`, dir =
|
|
14023
|
+
const id = `chk-${stamp(now)}`, dir = join11(store, id);
|
|
13943
14024
|
mkdirSync3(dir, { recursive: true });
|
|
13944
14025
|
for (const f of ["dashboard-state.json", "session.json"]) {
|
|
13945
14026
|
try {
|
|
13946
|
-
if (existsSync5(
|
|
13947
|
-
copyFileSync(
|
|
14027
|
+
if (existsSync5(join11(loki, f)))
|
|
14028
|
+
copyFileSync(join11(loki, f), join11(dir, f));
|
|
13948
14029
|
} catch {}
|
|
13949
14030
|
}
|
|
13950
14031
|
try {
|
|
13951
|
-
if (existsSync5(
|
|
13952
|
-
cpSync(
|
|
14032
|
+
if (existsSync5(join11(loki, "queue")))
|
|
14033
|
+
cpSync(join11(loki, "queue"), join11(dir, "queue"), { recursive: true });
|
|
13953
14034
|
} catch {}
|
|
13954
14035
|
const meta = { id, created_at: now.toISOString(), git_sha: gitHead(repoDir), message: message || null, files: readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name) };
|
|
13955
|
-
writeFileSync3(
|
|
13956
|
-
appendFileSync(
|
|
14036
|
+
writeFileSync3(join11(dir, "metadata.json"), JSON.stringify(meta, null, 2));
|
|
14037
|
+
appendFileSync(join11(store, "index.jsonl"), `${JSON.stringify(meta)}
|
|
13957
14038
|
`);
|
|
13958
14039
|
const mine = readdirSync4(store, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("chk-")).map((e) => e.name).sort();
|
|
13959
14040
|
while (mine.length > MAX_CHECKPOINTS)
|
|
13960
|
-
rmSync(
|
|
14041
|
+
rmSync(join11(store, mine.shift()), { recursive: true, force: true });
|
|
13961
14042
|
return meta;
|
|
13962
14043
|
}
|
|
13963
14044
|
function rollbackCheckpoint(repoDir, id, now = new Date) {
|
|
13964
14045
|
const store = storeOf(repoDir), loki = lokiOf(repoDir);
|
|
13965
14046
|
let real2 = "", realStore = "";
|
|
13966
14047
|
try {
|
|
13967
|
-
real2 =
|
|
13968
|
-
realStore =
|
|
14048
|
+
real2 = realpathSync6(join11(store, id));
|
|
14049
|
+
realStore = realpathSync6(store);
|
|
13969
14050
|
} catch {
|
|
13970
14051
|
return null;
|
|
13971
14052
|
}
|
|
13972
|
-
if (!real2.startsWith(`${realStore}${
|
|
14053
|
+
if (!real2.startsWith(`${realStore}${sep4}`) || !statSync4(real2).isDirectory())
|
|
13973
14054
|
return null;
|
|
13974
|
-
const preId = `rb-pre-${stamp(now)}`, pre =
|
|
14055
|
+
const preId = `rb-pre-${stamp(now)}`, pre = join11(store, preId);
|
|
13975
14056
|
mkdirSync3(pre, { recursive: true });
|
|
13976
14057
|
const notStore = (src) => src !== store;
|
|
13977
14058
|
for (const n2 of ["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json"]) {
|
|
13978
14059
|
try {
|
|
13979
|
-
if (existsSync5(
|
|
13980
|
-
copyFileSync(
|
|
14060
|
+
if (existsSync5(join11(loki, n2)))
|
|
14061
|
+
copyFileSync(join11(loki, n2), join11(pre, n2));
|
|
13981
14062
|
} catch {}
|
|
13982
14063
|
}
|
|
13983
14064
|
for (const d of ["state", "queue"]) {
|
|
13984
14065
|
try {
|
|
13985
|
-
if (existsSync5(
|
|
13986
|
-
cpSync(
|
|
14066
|
+
if (existsSync5(join11(loki, d)))
|
|
14067
|
+
cpSync(join11(loki, d), join11(pre, d), { recursive: true, filter: notStore });
|
|
13987
14068
|
} catch {}
|
|
13988
14069
|
}
|
|
13989
14070
|
const preMeta = { id: preId, created_at: now.toISOString(), message: `pre-rollback snapshot (before restoring ${id})`, created_by: "control plane rollback" };
|
|
13990
|
-
writeFileSync3(
|
|
13991
|
-
appendFileSync(
|
|
14071
|
+
writeFileSync3(join11(pre, "metadata.json"), JSON.stringify(preMeta, null, 2));
|
|
14072
|
+
appendFileSync(join11(store, "index.jsonl"), `${JSON.stringify(preMeta)}
|
|
13992
14073
|
`);
|
|
13993
14074
|
let restored = 0;
|
|
13994
14075
|
const errors = [];
|
|
13995
14076
|
for (const e of readdirSync4(real2, { withFileTypes: true })) {
|
|
13996
14077
|
if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
|
|
13997
14078
|
continue;
|
|
13998
|
-
const src =
|
|
14079
|
+
const src = join11(real2, e.name);
|
|
13999
14080
|
try {
|
|
14000
14081
|
if (lstatSync(src).isSymbolicLink()) {
|
|
14001
14082
|
errors.push(`${e.name}: symlink skipped`);
|
|
14002
14083
|
continue;
|
|
14003
14084
|
}
|
|
14004
|
-
cpSync(src,
|
|
14085
|
+
cpSync(src, join11(loki, e.name), { recursive: true, filter: (s) => s !== join11(real2, "state", "checkpoints") });
|
|
14005
14086
|
restored++;
|
|
14006
14087
|
} catch (err) {
|
|
14007
14088
|
errors.push(`${e.name}: ${err.message}`);
|
|
@@ -14084,8 +14165,8 @@ function mount4(ctx) {
|
|
|
14084
14165
|
|
|
14085
14166
|
// src/server/routes/config.ts
|
|
14086
14167
|
import { createHash as createHash5, randomBytes } from "crypto";
|
|
14087
|
-
import { closeSync as
|
|
14088
|
-
import { basename as
|
|
14168
|
+
import { closeSync as closeSync4, fchmodSync, fsyncSync, lstatSync as lstatSync2, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync7, renameSync as renameSync2, statSync as statSync6, unlinkSync, writeSync as writeSync2 } from "fs";
|
|
14169
|
+
import { basename as basename4, dirname as dirname2, join as join13, sep as sep5 } from "path";
|
|
14089
14170
|
|
|
14090
14171
|
// node_modules/yaml/dist/index.js
|
|
14091
14172
|
var composer = require_composer();
|
|
@@ -14137,7 +14218,7 @@ var $visitAsync = visit.visitAsync;
|
|
|
14137
14218
|
import { spawn } from "child_process";
|
|
14138
14219
|
import { existsSync as existsSync6, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
14139
14220
|
import { homedir as homedir4 } from "os";
|
|
14140
|
-
import { basename as
|
|
14221
|
+
import { basename as basename3, join as join12, resolve as resolve3 } from "path";
|
|
14141
14222
|
var ISSUE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}\/[A-Za-z0-9._-]{1,100}#[1-9][0-9]{0,8}$/;
|
|
14142
14223
|
var TASK = /^[A-Za-z0-9][A-Za-z0-9 .,_/#@+=-]{0,499}$/;
|
|
14143
14224
|
var hasDotDot = (t) => t.split(/[\\/ ]/).some((seg) => seg.startsWith(".."));
|
|
@@ -14153,7 +14234,7 @@ var BUDGET = /^(?:[1-9][0-9]{0,5}|0)(?:\.[0-9]{1,2})?$/;
|
|
|
14153
14234
|
var WORKSPACE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
14154
14235
|
function registryRepos2(env = process.env) {
|
|
14155
14236
|
try {
|
|
14156
|
-
const j = JSON.parse(readFileSync8(
|
|
14237
|
+
const j = JSON.parse(readFileSync8(join12(env.HOME || homedir4(), ".loki", "dashboard", "projects.json"), "utf8"));
|
|
14157
14238
|
return Object.values(j.projects ?? {}).map((p) => typeof p?.path === "string" ? p.path : "").filter((p) => p && existsSync6(p) && statSync5(p).isDirectory());
|
|
14158
14239
|
} catch {
|
|
14159
14240
|
return [];
|
|
@@ -14173,9 +14254,9 @@ function planStart(body, known, bin = "loki") {
|
|
|
14173
14254
|
if (b.repo !== undefined && b.repo !== "") {
|
|
14174
14255
|
if (typeof b.repo !== "string" || b.repo.includes("\x00"))
|
|
14175
14256
|
return { ok: false, error: "repo must be a path string" };
|
|
14176
|
-
const want =
|
|
14177
|
-
const paths = known.map((k) =>
|
|
14178
|
-
const named = paths.filter((k) =>
|
|
14257
|
+
const want = resolve3(b.repo);
|
|
14258
|
+
const paths = known.map((k) => resolve3(k));
|
|
14259
|
+
const named = paths.filter((k) => basename3(k) === b.repo);
|
|
14179
14260
|
const hit = paths.find((k) => k === want) ?? (named.length === 1 ? named[0] : undefined);
|
|
14180
14261
|
if (!hit)
|
|
14181
14262
|
return { ok: false, error: named.length > 1 ? "repo name is ambiguous" : "repo is not a known project" };
|
|
@@ -14598,11 +14679,11 @@ function renderVerified(doc, next, quote = needsQuote) {
|
|
|
14598
14679
|
function resolveConfigFile(repoDir) {
|
|
14599
14680
|
let root;
|
|
14600
14681
|
try {
|
|
14601
|
-
root =
|
|
14682
|
+
root = realpathSync7(repoDir);
|
|
14602
14683
|
} catch {
|
|
14603
14684
|
return { ok: false, status: 400, error: "repo directory not found" };
|
|
14604
14685
|
}
|
|
14605
|
-
const p =
|
|
14686
|
+
const p = join13(root, "loki.yaml");
|
|
14606
14687
|
let st;
|
|
14607
14688
|
try {
|
|
14608
14689
|
st = lstatSync2(p);
|
|
@@ -14612,11 +14693,11 @@ function resolveConfigFile(repoDir) {
|
|
|
14612
14693
|
if (st.isSymbolicLink()) {
|
|
14613
14694
|
let real2;
|
|
14614
14695
|
try {
|
|
14615
|
-
real2 =
|
|
14696
|
+
real2 = realpathSync7(p);
|
|
14616
14697
|
} catch {
|
|
14617
14698
|
return { ok: false, status: 400, error: "loki.yaml is a broken symlink" };
|
|
14618
14699
|
}
|
|
14619
|
-
if (!real2.startsWith(root +
|
|
14700
|
+
if (!real2.startsWith(root + sep5))
|
|
14620
14701
|
return { ok: false, status: 400, error: "loki.yaml is a symlink that resolves outside the repo" };
|
|
14621
14702
|
if (!statSync6(real2).isFile())
|
|
14622
14703
|
return { ok: false, status: 422, error: "loki.yaml is not a regular file" };
|
|
@@ -14627,20 +14708,20 @@ function resolveConfigFile(repoDir) {
|
|
|
14627
14708
|
return { ok: true, file: p, exists: true };
|
|
14628
14709
|
}
|
|
14629
14710
|
function atomicWrite(file, text2, mode) {
|
|
14630
|
-
const tmp =
|
|
14631
|
-
const fd =
|
|
14711
|
+
const tmp = join13(dirname2(file), `.${basename4(file)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
|
|
14712
|
+
const fd = openSync4(tmp, "wx", mode);
|
|
14632
14713
|
try {
|
|
14633
14714
|
writeSync2(fd, text2);
|
|
14634
14715
|
fchmodSync(fd, mode);
|
|
14635
14716
|
fsyncSync(fd);
|
|
14636
14717
|
} catch (e) {
|
|
14637
|
-
|
|
14718
|
+
closeSync4(fd);
|
|
14638
14719
|
try {
|
|
14639
14720
|
unlinkSync(tmp);
|
|
14640
14721
|
} catch {}
|
|
14641
14722
|
throw e;
|
|
14642
14723
|
}
|
|
14643
|
-
|
|
14724
|
+
closeSync4(fd);
|
|
14644
14725
|
try {
|
|
14645
14726
|
renameSync2(tmp, file);
|
|
14646
14727
|
} catch (e) {
|
|
@@ -14777,19 +14858,19 @@ function mount6(ctx) {
|
|
|
14777
14858
|
}
|
|
14778
14859
|
|
|
14779
14860
|
// src/server/routes/control.ts
|
|
14780
|
-
import { existsSync as existsSync7, realpathSync as
|
|
14781
|
-
import { join as
|
|
14861
|
+
import { existsSync as existsSync7, realpathSync as realpathSync8 } from "fs";
|
|
14862
|
+
import { join as join15, resolve as resolve4, sep as sep6 } from "path";
|
|
14782
14863
|
|
|
14783
14864
|
// ../../loki-ts/src/util/run_pid.ts
|
|
14784
|
-
import { execFileSync as
|
|
14865
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
14785
14866
|
import { chmodSync as chmodSync2, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
14786
|
-
import { join as
|
|
14787
|
-
var runPidPath = (runDir) =>
|
|
14867
|
+
import { join as join14 } from "path";
|
|
14868
|
+
var runPidPath = (runDir) => join14(runDir, "run.pid");
|
|
14788
14869
|
function processStartTime(pid) {
|
|
14789
14870
|
if (!Number.isInteger(pid) || pid <= 1)
|
|
14790
14871
|
return null;
|
|
14791
14872
|
try {
|
|
14792
|
-
const out =
|
|
14873
|
+
const out = execFileSync3("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 5000, env: { ...process.env, LC_ALL: "C" }, stdio: ["ignore", "pipe", "ignore"] });
|
|
14793
14874
|
return out.trim().replace(/\s+/g, " ") || null;
|
|
14794
14875
|
} catch {
|
|
14795
14876
|
return null;
|
|
@@ -14822,7 +14903,7 @@ function verifyRunPid(runDir, runId, startOf = processStartTime) {
|
|
|
14822
14903
|
}
|
|
14823
14904
|
|
|
14824
14905
|
// src/server/routes/control.ts
|
|
14825
|
-
var
|
|
14906
|
+
var ID4 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
14826
14907
|
var busy = new Set;
|
|
14827
14908
|
function mount7(ctx) {
|
|
14828
14909
|
const { act, db } = ctx;
|
|
@@ -14835,19 +14916,19 @@ function mount7(ctx) {
|
|
|
14835
14916
|
audit3(db, { kind: `run.${kind}`, target, result, detail });
|
|
14836
14917
|
return c.json(body, status);
|
|
14837
14918
|
};
|
|
14838
|
-
if (!
|
|
14919
|
+
if (!ID4.test(source) || !ID4.test(id) || source.includes("..") || id.includes(".."))
|
|
14839
14920
|
return done(400, "refused", { error: "invalid run id" }, "invalid id");
|
|
14840
14921
|
const repoRow = db.select().from(localRepos).where(eq(localRepos.sourceId, source)).get();
|
|
14841
14922
|
let repo = "";
|
|
14842
14923
|
try {
|
|
14843
|
-
repo = repoRow ?
|
|
14924
|
+
repo = repoRow ? realpathSync8(repoRow.realpath) : "";
|
|
14844
14925
|
} catch {
|
|
14845
14926
|
repo = "";
|
|
14846
14927
|
}
|
|
14847
14928
|
if (!repo)
|
|
14848
14929
|
return done(404, "refused", { error: "this run's repo is not known on this machine" });
|
|
14849
|
-
const runsRoot =
|
|
14850
|
-
if (!runDir.startsWith(runsRoot +
|
|
14930
|
+
const runsRoot = join15(repo, ".loki", "runs"), runDir = resolve4(runsRoot, id);
|
|
14931
|
+
if (!runDir.startsWith(runsRoot + sep6))
|
|
14851
14932
|
return done(400, "refused", { error: "invalid run id" }, "path escape");
|
|
14852
14933
|
return handler({ source, run: id, repo, runDir }, done);
|
|
14853
14934
|
});
|
|
@@ -14894,7 +14975,7 @@ function mount7(ctx) {
|
|
|
14894
14975
|
return done(404, "refused", { error: "run not found" });
|
|
14895
14976
|
if (!blockedQuestion(loadEvents(db, a.source, a.run), row.verdict))
|
|
14896
14977
|
return done(409, "refused", { error: "run is not BLOCKED, so there is nothing to resume" });
|
|
14897
|
-
if (!existsSync7(
|
|
14978
|
+
if (!existsSync7(join15(answerDir, a.source, `${a.run}.answer.txt`)))
|
|
14898
14979
|
return done(409, "refused", { error: "no answer recorded for this run; answer it first" });
|
|
14899
14980
|
return launch(a, [bin, "answer", a.run], done);
|
|
14900
14981
|
});
|
|
@@ -14974,10 +15055,10 @@ function mount8(ctx) {
|
|
|
14974
15055
|
// src/server/routes/fleet.ts
|
|
14975
15056
|
import { readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
|
|
14976
15057
|
import { homedir as homedir5 } from "os";
|
|
14977
|
-
import { basename as
|
|
15058
|
+
import { basename as basename5, join as join16 } from "path";
|
|
14978
15059
|
var isNum2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
14979
15060
|
var isObj5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
14980
|
-
var registryFile = () =>
|
|
15061
|
+
var registryFile = () => join16(process.env.HOME ?? homedir5(), ".loki", "dashboard", "projects.json");
|
|
14981
15062
|
function pidAlive(pid) {
|
|
14982
15063
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
14983
15064
|
return false;
|
|
@@ -14992,33 +15073,33 @@ function projectSnapshot(path) {
|
|
|
14992
15073
|
const snap = { phase: "", iteration: null, cost_usd: null, started_at: null, ended_at: null };
|
|
14993
15074
|
if (!path)
|
|
14994
15075
|
return snap;
|
|
14995
|
-
const loki =
|
|
14996
|
-
const st = readJson(
|
|
15076
|
+
const loki = join16(path, ".loki");
|
|
15077
|
+
const st = readJson(join16(loki, "dashboard-state.json"));
|
|
14997
15078
|
if (isObj5(st)) {
|
|
14998
15079
|
snap.phase = typeof st.phase === "string" ? st.phase : "";
|
|
14999
15080
|
snap.iteration = Number.isInteger(st.iteration) ? st.iteration : null;
|
|
15000
15081
|
}
|
|
15001
|
-
const ss = readJson(
|
|
15082
|
+
const ss = readJson(join16(loki, "session.json"));
|
|
15002
15083
|
if (isObj5(ss)) {
|
|
15003
15084
|
const a = ss.startedAt || ss.started_at, b = ss.endedAt || ss.ended_at;
|
|
15004
15085
|
snap.started_at = typeof a === "string" ? a : null;
|
|
15005
15086
|
snap.ended_at = typeof b === "string" ? b : null;
|
|
15006
15087
|
}
|
|
15007
15088
|
let cost = 0, found = false;
|
|
15008
|
-
const eff =
|
|
15089
|
+
const eff = join16(loki, "metrics", "efficiency");
|
|
15009
15090
|
let names = [];
|
|
15010
15091
|
try {
|
|
15011
15092
|
names = readdirSync5(eff).filter((n2) => n2.endsWith(".json"));
|
|
15012
15093
|
} catch {}
|
|
15013
15094
|
for (const n2 of names) {
|
|
15014
|
-
const d = readJson(
|
|
15095
|
+
const d = readJson(join16(eff, n2));
|
|
15015
15096
|
if (isObj5(d) && isNum2(d.cost_usd) && recordIsMeasured(d)) {
|
|
15016
15097
|
cost += d.cost_usd;
|
|
15017
15098
|
found = true;
|
|
15018
15099
|
}
|
|
15019
15100
|
}
|
|
15020
15101
|
if (!found) {
|
|
15021
|
-
const t = readJson(
|
|
15102
|
+
const t = readJson(join16(loki, "context", "tracking.json"));
|
|
15022
15103
|
const totals = isObj5(t) && isObj5(t.totals) ? t.totals : null;
|
|
15023
15104
|
if (totals && isNum2(totals.total_cost_usd) && recordIsMeasured({ cost_usd: totals.total_cost_usd, input_tokens: totals.total_input, output_tokens: totals.total_output })) {
|
|
15024
15105
|
cost = totals.total_cost_usd;
|
|
@@ -15055,7 +15136,7 @@ function fleetRuns(includeInactive = true, file = registryFile()) {
|
|
|
15055
15136
|
}
|
|
15056
15137
|
return {
|
|
15057
15138
|
id: p.id ?? null,
|
|
15058
|
-
name: p.name || (path ?
|
|
15139
|
+
name: p.name || (path ? basename5(path) : "project"),
|
|
15059
15140
|
path,
|
|
15060
15141
|
status,
|
|
15061
15142
|
running,
|
|
@@ -15091,8 +15172,8 @@ function mount9(ctx) {
|
|
|
15091
15172
|
}
|
|
15092
15173
|
|
|
15093
15174
|
// src/server/routes/memory.ts
|
|
15094
|
-
import { existsSync as existsSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as
|
|
15095
|
-
import { join as
|
|
15175
|
+
import { existsSync as existsSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as realpathSync9, statSync as statSync7 } from "fs";
|
|
15176
|
+
import { join as join17, sep as sep7 } from "path";
|
|
15096
15177
|
var isObj6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15097
15178
|
var nonEmpty = (v) => !(v === undefined || v === null || v === "" || Array.isArray(v) && v.length === 0 || isObj6(v) && Object.keys(v).length === 0);
|
|
15098
15179
|
var focus = { dir: null };
|
|
@@ -15123,7 +15204,7 @@ function jsonFiles(dir, cap = Infinity) {
|
|
|
15123
15204
|
for (const e of ents.sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
15124
15205
|
if (out2.length >= cap)
|
|
15125
15206
|
return;
|
|
15126
|
-
const p =
|
|
15207
|
+
const p = join17(d, e.name);
|
|
15127
15208
|
if (e.isDirectory())
|
|
15128
15209
|
walk(p);
|
|
15129
15210
|
else if (e.isFile() && e.name.endsWith(".json"))
|
|
@@ -15144,24 +15225,24 @@ function setFocus(repoDir, raw2, registry = registryFile()) {
|
|
|
15144
15225
|
return { ok: false, error: "project_dir must be an absolute path without .. segments" };
|
|
15145
15226
|
let real2 = "";
|
|
15146
15227
|
try {
|
|
15147
|
-
real2 =
|
|
15228
|
+
real2 = realpathSync9(s);
|
|
15148
15229
|
if (!statSync7(real2).isDirectory())
|
|
15149
15230
|
throw new Error("not a dir");
|
|
15150
15231
|
} catch {
|
|
15151
15232
|
return { ok: false, error: `Directory does not exist: ${s}` };
|
|
15152
15233
|
}
|
|
15153
|
-
if (!existsSync8(
|
|
15234
|
+
if (!existsSync8(join17(real2, ".loki")) || !statSync7(join17(real2, ".loki")).isDirectory())
|
|
15154
15235
|
return { ok: false, error: `Directory does not contain a .loki/ subdirectory: ${s}` };
|
|
15155
15236
|
const allowed = new Set;
|
|
15156
15237
|
try {
|
|
15157
|
-
allowed.add(
|
|
15238
|
+
allowed.add(realpathSync9(repoDir));
|
|
15158
15239
|
} catch {}
|
|
15159
15240
|
const reg = readJson(registry);
|
|
15160
15241
|
if (isObj6(reg) && isObj6(reg.projects))
|
|
15161
15242
|
for (const p of Object.values(reg.projects)) {
|
|
15162
15243
|
if (isObj6(p) && typeof p.path === "string") {
|
|
15163
15244
|
try {
|
|
15164
|
-
allowed.add(
|
|
15245
|
+
allowed.add(realpathSync9(p.path));
|
|
15165
15246
|
} catch {}
|
|
15166
15247
|
}
|
|
15167
15248
|
}
|
|
@@ -15175,7 +15256,7 @@ var QUEUES = [["pending.json", "pending"], ["in-progress.json", "in_progress"],
|
|
|
15175
15256
|
var RANK = { done: 3, in_progress: 2, review: 1, pending: 0 };
|
|
15176
15257
|
function tasksList(loki, status) {
|
|
15177
15258
|
const all = [];
|
|
15178
|
-
const st = readJson(
|
|
15259
|
+
const st = readJson(join17(loki, "dashboard-state.json"));
|
|
15179
15260
|
const groups = isObj6(st) && isObj6(st.tasks) ? st.tasks : {};
|
|
15180
15261
|
for (const [key, mapped] of GROUPS) {
|
|
15181
15262
|
const g = groups[key];
|
|
@@ -15197,7 +15278,7 @@ function tasksList(loki, status) {
|
|
|
15197
15278
|
});
|
|
15198
15279
|
}
|
|
15199
15280
|
for (const [file, qs] of QUEUES) {
|
|
15200
|
-
const raw2 = readJson(
|
|
15281
|
+
const raw2 = readJson(join17(loki, "queue", file));
|
|
15201
15282
|
const items = isObj6(raw2) ? raw2.tasks : raw2;
|
|
15202
15283
|
if (!Array.isArray(items))
|
|
15203
15284
|
continue;
|
|
@@ -15251,7 +15332,7 @@ function tasksList(loki, status) {
|
|
|
15251
15332
|
}
|
|
15252
15333
|
return status ? out2.filter((t) => t.status === status) : out2;
|
|
15253
15334
|
}
|
|
15254
|
-
var memDir = (loki, sub) =>
|
|
15335
|
+
var memDir = (loki, sub) => join17(loki, "memory", sub);
|
|
15255
15336
|
function memoryEpisodes(loki, limit = 50) {
|
|
15256
15337
|
const eps = jsonFiles(memDir(loki, "episodic")).map(readJson).filter(isObj6);
|
|
15257
15338
|
eps.sort((a, b) => String(b.timestamp ?? "").localeCompare(String(a.timestamp ?? "")));
|
|
@@ -15260,19 +15341,19 @@ function memoryEpisodes(loki, limit = 50) {
|
|
|
15260
15341
|
function memoryEpisode(loki, id) {
|
|
15261
15342
|
for (const f of jsonFiles(memDir(loki, "episodic"))) {
|
|
15262
15343
|
const d = readJson(f);
|
|
15263
|
-
if (isObj6(d) && (d.id === id || f.split(
|
|
15344
|
+
if (isObj6(d) && (d.id === id || f.split(sep7).pop() === `${id}.json`))
|
|
15264
15345
|
return d;
|
|
15265
15346
|
}
|
|
15266
15347
|
return null;
|
|
15267
15348
|
}
|
|
15268
15349
|
function memoryPatterns(loki) {
|
|
15269
|
-
const d = readJson(
|
|
15350
|
+
const d = readJson(join17(memDir(loki, "semantic"), "patterns.json"));
|
|
15270
15351
|
const list = Array.isArray(d) ? d : isObj6(d) && Array.isArray(d.patterns) ? d.patterns : [];
|
|
15271
15352
|
return list.filter(isObj6);
|
|
15272
15353
|
}
|
|
15273
15354
|
var memorySkills = (loki) => jsonFiles(memDir(loki, "skills")).map(readJson).filter(isObj6);
|
|
15274
15355
|
function memoryEconomics(loki) {
|
|
15275
|
-
const rawV = readJson(
|
|
15356
|
+
const rawV = readJson(join17(loki, "memory", "token_economics.json"));
|
|
15276
15357
|
const raw2 = isObj6(rawV) ? rawV : {};
|
|
15277
15358
|
const m = isObj6(raw2.metrics) ? raw2.metrics : {};
|
|
15278
15359
|
const n2 = (v) => typeof v === "number" && Number.isFinite(v) ? Math.trunc(v) : 0;
|
|
@@ -15308,7 +15389,7 @@ function memoryEconomics(loki) {
|
|
|
15308
15389
|
};
|
|
15309
15390
|
}
|
|
15310
15391
|
function contextTracking(loki) {
|
|
15311
|
-
const f =
|
|
15392
|
+
const f = join17(loki, "context", "tracking.json");
|
|
15312
15393
|
if (!existsSync8(f)) {
|
|
15313
15394
|
return { status: 200, body: {
|
|
15314
15395
|
session_id: null,
|
|
@@ -15370,7 +15451,7 @@ function mount10(ctx) {
|
|
|
15370
15451
|
|
|
15371
15452
|
// src/server/routes/metrics.ts
|
|
15372
15453
|
import { readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
|
|
15373
|
-
import { join as
|
|
15454
|
+
import { join as join18 } from "path";
|
|
15374
15455
|
var isObj7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15375
15456
|
var arr = (v) => Array.isArray(v) ? v : [];
|
|
15376
15457
|
function buildMetricsText(repoDir, now = Date.now()) {
|
|
@@ -15387,7 +15468,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15387
15468
|
L.push(...samples);
|
|
15388
15469
|
L.push("");
|
|
15389
15470
|
};
|
|
15390
|
-
const stRaw = readJson(
|
|
15471
|
+
const stRaw = readJson(join18(loki, "dashboard-state.json"));
|
|
15391
15472
|
const state = isObj7(stRaw) ? stRaw : null;
|
|
15392
15473
|
let status = 0;
|
|
15393
15474
|
if (state?.mode === "paused")
|
|
@@ -15396,7 +15477,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15396
15477
|
status = 1;
|
|
15397
15478
|
else {
|
|
15398
15479
|
try {
|
|
15399
|
-
if (pidAlive(parseInt(readFileSync13(
|
|
15480
|
+
if (pidAlive(parseInt(readFileSync13(join18(loki, "loki.pid"), "utf8").trim(), 10)))
|
|
15400
15481
|
status = 1;
|
|
15401
15482
|
} catch {}
|
|
15402
15483
|
}
|
|
@@ -15412,7 +15493,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15412
15493
|
`loki_tasks_total{status="failed"} ${arr(tasks.failed).length}`
|
|
15413
15494
|
] : null);
|
|
15414
15495
|
let agents = null;
|
|
15415
|
-
const af = readJson(
|
|
15496
|
+
const af = readJson(join18(loki, "state", "agents.json"));
|
|
15416
15497
|
if (Array.isArray(af) && af.length)
|
|
15417
15498
|
agents = af;
|
|
15418
15499
|
else if (state && Array.isArray(state.agents) && state.agents.length)
|
|
@@ -15424,7 +15505,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15424
15505
|
gauge("loki_cost_usd", "Estimated total cost in USD", "gauge", cost.estimated_cost_usd !== null ? [`loki_cost_usd ${cost.estimated_cost_usd}`] : null);
|
|
15425
15506
|
let events2 = null;
|
|
15426
15507
|
try {
|
|
15427
|
-
events2 = readFileSync13(
|
|
15508
|
+
events2 = readFileSync13(join18(loki, "events.jsonl"), "utf8").split(`
|
|
15428
15509
|
`).filter((l) => l.trim()).length;
|
|
15429
15510
|
} catch {}
|
|
15430
15511
|
gauge("loki_events_total", "Total number of events recorded", "counter", events2 !== null ? [`loki_events_total ${events2}`] : null);
|
|
@@ -15436,19 +15517,19 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15436
15517
|
}
|
|
15437
15518
|
gauge("loki_uptime_seconds", "Seconds since session started", "gauge", uptime !== null ? [`loki_uptime_seconds ${uptime}`] : null);
|
|
15438
15519
|
let total = 0, ver = 0, gaps = 0, notver = 0, unk = 0, att = 0, sig = 0;
|
|
15439
|
-
const pdir =
|
|
15520
|
+
const pdir = join18(loki, "proofs");
|
|
15440
15521
|
let entries = [];
|
|
15441
15522
|
try {
|
|
15442
15523
|
entries = readdirSync7(pdir).sort();
|
|
15443
15524
|
} catch {}
|
|
15444
15525
|
for (const e of entries) {
|
|
15445
15526
|
try {
|
|
15446
|
-
if (!statSync8(
|
|
15527
|
+
if (!statSync8(join18(pdir, e)).isDirectory())
|
|
15447
15528
|
continue;
|
|
15448
15529
|
} catch {
|
|
15449
15530
|
continue;
|
|
15450
15531
|
}
|
|
15451
|
-
const d = readJson(
|
|
15532
|
+
const d = readJson(join18(pdir, e, "proof.json"));
|
|
15452
15533
|
if (!isObj7(d))
|
|
15453
15534
|
continue;
|
|
15454
15535
|
total++;
|
|
@@ -15493,7 +15574,7 @@ function mount11(ctx) {
|
|
|
15493
15574
|
|
|
15494
15575
|
// src/server/routes/session_control.ts
|
|
15495
15576
|
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync8, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
15496
|
-
import { dirname as dirname3, join as
|
|
15577
|
+
import { dirname as dirname3, join as join19 } from "path";
|
|
15497
15578
|
var isObj8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15498
15579
|
var ACTIONS = ["pause", "resume", "stop", "council-review"];
|
|
15499
15580
|
var isAction = (v) => ACTIONS.includes(v);
|
|
@@ -15518,7 +15599,7 @@ var readJson2 = (p) => {
|
|
|
15518
15599
|
};
|
|
15519
15600
|
function runnerPid(loki) {
|
|
15520
15601
|
try {
|
|
15521
|
-
const pid = parseInt(readFileSync14(
|
|
15602
|
+
const pid = parseInt(readFileSync14(join19(loki, "loki.pid"), "utf8").trim(), 10);
|
|
15522
15603
|
return pidAlive(pid) ? pid : null;
|
|
15523
15604
|
} catch {
|
|
15524
15605
|
return null;
|
|
@@ -15528,18 +15609,18 @@ function applyControl(repoDir, action) {
|
|
|
15528
15609
|
const loki = lokiDirOf(repoDir);
|
|
15529
15610
|
const running = runnerPid(loki) !== null;
|
|
15530
15611
|
const put = (rel) => {
|
|
15531
|
-
const f =
|
|
15612
|
+
const f = join19(loki, rel);
|
|
15532
15613
|
mkdirSync4(dirname3(f), { recursive: true });
|
|
15533
15614
|
writeFileSync5(f, new Date().toISOString());
|
|
15534
15615
|
};
|
|
15535
15616
|
if (action === "council-review") {
|
|
15536
|
-
put(
|
|
15617
|
+
put(join19("signals", "COUNCIL_REVIEW_REQUESTED"));
|
|
15537
15618
|
return { status: 200, body: { success: true, message: "Council review requested" } };
|
|
15538
15619
|
}
|
|
15539
15620
|
if (action === "resume") {
|
|
15540
15621
|
for (const n2 of ["PAUSE", "STOP"]) {
|
|
15541
15622
|
try {
|
|
15542
|
-
unlinkSync2(
|
|
15623
|
+
unlinkSync2(join19(loki, n2));
|
|
15543
15624
|
} catch {}
|
|
15544
15625
|
}
|
|
15545
15626
|
return running ? { status: 200, body: { success: true, message: "Session resumed", process_verified: true } } : { status: 503, body: { success: false, message: "Session process is not running; the signal files were cleared" } };
|
|
@@ -15559,16 +15640,16 @@ function runControl(db, repoDir, action, c, via) {
|
|
|
15559
15640
|
return c.json(r.body, r.status);
|
|
15560
15641
|
}
|
|
15561
15642
|
function councilState(loki) {
|
|
15562
|
-
const f =
|
|
15643
|
+
const f = join19(loki, "council", "state.json");
|
|
15563
15644
|
if (!existsSync9(f))
|
|
15564
15645
|
return { enabled: false, total_votes: 0, verdicts: [] };
|
|
15565
15646
|
const d = readJson2(f);
|
|
15566
15647
|
return isObj8(d) ? d : { enabled: null, total_votes: null, verdicts: null, error: "unreadable_state" };
|
|
15567
15648
|
}
|
|
15568
15649
|
function councilVerdicts(loki, limit = 20) {
|
|
15569
|
-
const st = readJson2(
|
|
15650
|
+
const st = readJson2(join19(loki, "council", "state.json"));
|
|
15570
15651
|
const verdicts = isObj8(st) && Array.isArray(st.verdicts) ? st.verdicts : [];
|
|
15571
|
-
const votes =
|
|
15652
|
+
const votes = join19(loki, "council", "votes");
|
|
15572
15653
|
const details = [];
|
|
15573
15654
|
let names = [];
|
|
15574
15655
|
try {
|
|
@@ -15582,8 +15663,8 @@ function councilVerdicts(loki, limit = 20) {
|
|
|
15582
15663
|
}
|
|
15583
15664
|
};
|
|
15584
15665
|
for (const n2 of names.slice(0, limit)) {
|
|
15585
|
-
const dir =
|
|
15586
|
-
const ev = text2(
|
|
15666
|
+
const dir = join19(votes, n2), d = { iteration: n2 };
|
|
15667
|
+
const ev = text2(join19(dir, "evidence.md"));
|
|
15587
15668
|
if (ev !== null)
|
|
15588
15669
|
d.evidence_preview = ev.slice(0, 500);
|
|
15589
15670
|
let files = [];
|
|
@@ -15591,10 +15672,10 @@ function councilVerdicts(loki, limit = 20) {
|
|
|
15591
15672
|
files = readdirSync8(dir).filter((f) => /^member-.*\.txt$/.test(f)).sort();
|
|
15592
15673
|
} catch {}
|
|
15593
15674
|
d.members = files.flatMap((f) => {
|
|
15594
|
-
const t = text2(
|
|
15675
|
+
const t = text2(join19(dir, f));
|
|
15595
15676
|
return t === null ? [] : [{ member: f.replace(/\.txt$/, ""), content: t.trim() }];
|
|
15596
15677
|
});
|
|
15597
|
-
const con = text2(
|
|
15678
|
+
const con = text2(join19(dir, "contrarian.txt"));
|
|
15598
15679
|
if (con !== null)
|
|
15599
15680
|
d.contrarian = con.trim();
|
|
15600
15681
|
details.push(d);
|
|
@@ -15605,7 +15686,7 @@ function councilConvergence(loki) {
|
|
|
15605
15686
|
const dataPoints = [];
|
|
15606
15687
|
let raw2 = "";
|
|
15607
15688
|
try {
|
|
15608
|
-
raw2 = readFileSync14(
|
|
15689
|
+
raw2 = readFileSync14(join19(loki, "council", "convergence.log"), "utf8");
|
|
15609
15690
|
} catch {}
|
|
15610
15691
|
for (const line2 of raw2.trim().split(`
|
|
15611
15692
|
`)) {
|
|
@@ -15621,7 +15702,7 @@ function councilConvergence(loki) {
|
|
|
15621
15702
|
}
|
|
15622
15703
|
function councilReport(loki) {
|
|
15623
15704
|
try {
|
|
15624
|
-
return { report: readFileSync14(
|
|
15705
|
+
return { report: readFileSync14(join19(loki, "council", "report.md"), "utf8") };
|
|
15625
15706
|
} catch {
|
|
15626
15707
|
return { report: null };
|
|
15627
15708
|
}
|
|
@@ -15634,7 +15715,7 @@ function councilTranscripts(loki, q) {
|
|
|
15634
15715
|
if (Number.isNaN(since))
|
|
15635
15716
|
return { error: "Invalid 'since' timestamp format; expected ISO8601" };
|
|
15636
15717
|
}
|
|
15637
|
-
const dir =
|
|
15718
|
+
const dir = join19(loki, "council", "transcripts");
|
|
15638
15719
|
let names = [];
|
|
15639
15720
|
try {
|
|
15640
15721
|
names = readdirSync8(dir).filter((f) => /^iter-.*\.json$/.test(f)).sort().reverse();
|
|
@@ -15643,7 +15724,7 @@ function councilTranscripts(loki, q) {
|
|
|
15643
15724
|
}
|
|
15644
15725
|
const out2 = [];
|
|
15645
15726
|
for (const n2 of names) {
|
|
15646
|
-
const rec = readJson2(
|
|
15727
|
+
const rec = readJson2(join19(dir, n2));
|
|
15647
15728
|
if (!isObj8(rec) || typeof rec.iteration_id !== "string")
|
|
15648
15729
|
continue;
|
|
15649
15730
|
if (since !== null) {
|
|
@@ -15696,28 +15777,28 @@ function mount12(ctx) {
|
|
|
15696
15777
|
// src/server/routes/integrations.ts
|
|
15697
15778
|
import { spawn as spawn2 } from "child_process";
|
|
15698
15779
|
import { existsSync as existsSync10, readFileSync as readFileSync15, statSync as statSync9 } from "fs";
|
|
15699
|
-
import { delimiter, join as
|
|
15780
|
+
import { delimiter, join as join20 } from "path";
|
|
15700
15781
|
var GH_TIMEOUT_MS = 3000;
|
|
15701
15782
|
var isSet = (n2) => !!n2 && (process.env[n2] ?? "") !== "";
|
|
15702
15783
|
function ghAuthed(pathVar = process.env.PATH ?? "", timeoutMs = GH_TIMEOUT_MS) {
|
|
15703
|
-
const exe = pathVar.split(delimiter).filter(Boolean).map((d) =>
|
|
15784
|
+
const exe = pathVar.split(delimiter).filter(Boolean).map((d) => join20(d, "gh")).find((p) => existsSync10(p));
|
|
15704
15785
|
if (!exe)
|
|
15705
15786
|
return Promise.resolve(null);
|
|
15706
|
-
return new Promise((
|
|
15787
|
+
return new Promise((resolve5) => {
|
|
15707
15788
|
let done = false;
|
|
15708
15789
|
let timer;
|
|
15709
15790
|
const finish = (v) => {
|
|
15710
15791
|
if (!done) {
|
|
15711
15792
|
done = true;
|
|
15712
15793
|
clearTimeout(timer);
|
|
15713
|
-
|
|
15794
|
+
resolve5(v);
|
|
15714
15795
|
}
|
|
15715
15796
|
};
|
|
15716
15797
|
let child;
|
|
15717
15798
|
try {
|
|
15718
15799
|
child = spawn2(exe, ["auth", "status"], { stdio: "ignore", env: { ...process.env, NO_COLOR: "1" } });
|
|
15719
15800
|
} catch {
|
|
15720
|
-
return
|
|
15801
|
+
return resolve5(null);
|
|
15721
15802
|
}
|
|
15722
15803
|
timer = setTimeout(() => {
|
|
15723
15804
|
try {
|
|
@@ -15745,7 +15826,7 @@ function configuredName(repoDir, path) {
|
|
|
15745
15826
|
}
|
|
15746
15827
|
function mcpServers(repoDir) {
|
|
15747
15828
|
try {
|
|
15748
|
-
const p =
|
|
15829
|
+
const p = join20(repoDir, ".mcp.json");
|
|
15749
15830
|
if (!existsSync10(p) || statSync9(p).size > 1e6)
|
|
15750
15831
|
return null;
|
|
15751
15832
|
const j = JSON.parse(readFileSync15(p, "utf8"));
|
|
@@ -15775,18 +15856,18 @@ function conv(s) {
|
|
|
15775
15856
|
}
|
|
15776
15857
|
async function listIntegrations(repoDir, gh = () => ghAuthed()) {
|
|
15777
15858
|
const rows = [];
|
|
15778
|
-
const
|
|
15859
|
+
const gitEnv2 = configuredName(repoDir, ["git", "token_env"]);
|
|
15779
15860
|
const ghOk = await gh();
|
|
15780
|
-
const gitEnvSet = isSet(
|
|
15861
|
+
const gitEnvSet = isSet(gitEnv2);
|
|
15781
15862
|
rows.push({
|
|
15782
15863
|
id: "github",
|
|
15783
15864
|
label: "GitHub",
|
|
15784
|
-
status: gitEnvSet || ghOk === true ? "connected" : ghOk === null && !
|
|
15865
|
+
status: gitEnvSet || ghOk === true ? "connected" : ghOk === null && !gitEnv2 ? "not_measured" : "not_connected",
|
|
15785
15866
|
method: gitEnvSet ? "env" : ghOk !== null ? "gh_cli" : "none",
|
|
15786
|
-
env_name:
|
|
15867
|
+
env_name: gitEnv2,
|
|
15787
15868
|
env_set: gitEnvSet,
|
|
15788
15869
|
config_path: "git.token_env",
|
|
15789
|
-
detail: gitEnvSet ? "git.token_env is set in the environment" : ghOk === true ? "gh auth status succeeded" : ghOk === false ? "gh is installed but not authenticated" :
|
|
15870
|
+
detail: gitEnvSet ? "git.token_env is set in the environment" : ghOk === true ? "gh auth status succeeded" : ghOk === false ? "gh is installed but not authenticated" : gitEnv2 ? "git.token_env names a variable that is not set" : "gh not found and git.token_env is not configured"
|
|
15790
15871
|
});
|
|
15791
15872
|
rows.push(conv(CONVENTIONAL[0]));
|
|
15792
15873
|
const slackEnv = configuredName(repoDir, ["notifications", "slack_webhook_env"]);
|
|
@@ -15826,8 +15907,8 @@ function mount13(ctx) {
|
|
|
15826
15907
|
|
|
15827
15908
|
// src/server/routes/merge_risk.ts
|
|
15828
15909
|
import { spawn as spawn3 } from "child_process";
|
|
15829
|
-
import { realpathSync as
|
|
15830
|
-
import { basename as
|
|
15910
|
+
import { realpathSync as realpathSync10 } from "fs";
|
|
15911
|
+
import { basename as basename6, resolve as resolve5 } from "path";
|
|
15831
15912
|
var PR = /^[0-9]{1,7}$/;
|
|
15832
15913
|
var REF = /^[A-Za-z0-9._/][A-Za-z0-9._/-]{0,99}$/;
|
|
15833
15914
|
var MAX_OUT = 256 * 1024;
|
|
@@ -15925,7 +16006,7 @@ function mount14(ctx) {
|
|
|
15925
16006
|
const active = { run: 0, queue: 0, risk: 0 };
|
|
15926
16007
|
const real2 = (p) => {
|
|
15927
16008
|
try {
|
|
15928
|
-
return
|
|
16009
|
+
return realpathSync10(p);
|
|
15929
16010
|
} catch {
|
|
15930
16011
|
return null;
|
|
15931
16012
|
}
|
|
@@ -15939,12 +16020,12 @@ function mount14(ctx) {
|
|
|
15939
16020
|
for (const k of [ctx.repoDir, ...registryRepos2()]) {
|
|
15940
16021
|
const r = real2(k);
|
|
15941
16022
|
if (r)
|
|
15942
|
-
known.set(r,
|
|
16023
|
+
known.set(r, basename6(resolve5(k)));
|
|
15943
16024
|
}
|
|
15944
16025
|
const wantReal = real2(want);
|
|
15945
16026
|
if (wantReal && known.has(wantReal))
|
|
15946
16027
|
return wantReal;
|
|
15947
|
-
const named = [...known].filter(([r, label]) => label === want ||
|
|
16028
|
+
const named = [...known].filter(([r, label]) => label === want || basename6(r) === want).map(([r]) => r);
|
|
15948
16029
|
return named.length === 1 ? named[0] : null;
|
|
15949
16030
|
};
|
|
15950
16031
|
const slot = async (c, kind, auditKind, fn) => {
|
|
@@ -16137,7 +16218,7 @@ function mount15(ctx) {
|
|
|
16137
16218
|
// src/server/routes/providers.ts
|
|
16138
16219
|
import { spawn as spawn4 } from "child_process";
|
|
16139
16220
|
import { existsSync as existsSync11, readFileSync as readFileSync16 } from "fs";
|
|
16140
|
-
import { delimiter as delimiter2, join as
|
|
16221
|
+
import { delimiter as delimiter2, join as join21 } from "path";
|
|
16141
16222
|
var ORDER = ["claude", "codex", "cline", "aider", "opencode"];
|
|
16142
16223
|
var DEPRECATED = ["gemini"];
|
|
16143
16224
|
var AUTH_ENV = {
|
|
@@ -16153,7 +16234,7 @@ function findOnPath(bin, pathVar) {
|
|
|
16153
16234
|
for (const d of pathVar.split(delimiter2)) {
|
|
16154
16235
|
if (!d)
|
|
16155
16236
|
continue;
|
|
16156
|
-
const p =
|
|
16237
|
+
const p = join21(d, bin);
|
|
16157
16238
|
if (existsSync11(p))
|
|
16158
16239
|
return p;
|
|
16159
16240
|
}
|
|
@@ -16163,7 +16244,7 @@ function probeCli(bin, timeoutMs, pathVar = process.env.PATH ?? "") {
|
|
|
16163
16244
|
const exe = findOnPath(bin, pathVar);
|
|
16164
16245
|
if (!exe)
|
|
16165
16246
|
return Promise.resolve({ installed: false, version: null, probe: "not_found" });
|
|
16166
|
-
return new Promise((
|
|
16247
|
+
return new Promise((resolve6) => {
|
|
16167
16248
|
let out2 = "";
|
|
16168
16249
|
let done = false;
|
|
16169
16250
|
let timer;
|
|
@@ -16171,14 +16252,14 @@ function probeCli(bin, timeoutMs, pathVar = process.env.PATH ?? "") {
|
|
|
16171
16252
|
if (!done) {
|
|
16172
16253
|
done = true;
|
|
16173
16254
|
clearTimeout(timer);
|
|
16174
|
-
|
|
16255
|
+
resolve6(r);
|
|
16175
16256
|
}
|
|
16176
16257
|
};
|
|
16177
16258
|
let child;
|
|
16178
16259
|
try {
|
|
16179
16260
|
child = spawn4(exe, ["--version"], { stdio: ["ignore", "pipe", "ignore"], env: { ...process.env, NO_COLOR: "1" } });
|
|
16180
16261
|
} catch {
|
|
16181
|
-
return
|
|
16262
|
+
return resolve6({ installed: true, version: null, probe: "error" });
|
|
16182
16263
|
}
|
|
16183
16264
|
timer = setTimeout(() => {
|
|
16184
16265
|
try {
|
|
@@ -16198,14 +16279,14 @@ function probeCli(bin, timeoutMs, pathVar = process.env.PATH ?? "") {
|
|
|
16198
16279
|
});
|
|
16199
16280
|
}
|
|
16200
16281
|
function providersDir() {
|
|
16201
|
-
const cands = [process.env.LOKI_PROVIDERS_DIR,
|
|
16202
|
-
return cands.find((d) => !!d && existsSync11(
|
|
16282
|
+
const cands = [process.env.LOKI_PROVIDERS_DIR, join21(import.meta.dir, "../../../../../providers"), join21(import.meta.dir, "../../../providers")];
|
|
16283
|
+
return cands.find((d) => !!d && existsSync11(join21(d, "model_catalog.json"))) ?? null;
|
|
16203
16284
|
}
|
|
16204
16285
|
function loadCatalog(dir) {
|
|
16205
16286
|
if (!dir)
|
|
16206
16287
|
return { providers: {}, updated: null };
|
|
16207
16288
|
try {
|
|
16208
|
-
const j = JSON.parse(readFileSync16(
|
|
16289
|
+
const j = JSON.parse(readFileSync16(join21(dir, "model_catalog.json"), "utf8"));
|
|
16209
16290
|
return { providers: j.providers ?? {}, updated: typeof j.updated === "string" ? j.updated : null };
|
|
16210
16291
|
} catch {
|
|
16211
16292
|
return { providers: {}, updated: null };
|
|
@@ -16448,24 +16529,24 @@ data: ${JSON.stringify({ source: k.slice(0, i), run: k.slice(i + 1) })}
|
|
|
16448
16529
|
}
|
|
16449
16530
|
|
|
16450
16531
|
// src/server/routes/verify.ts
|
|
16451
|
-
import { readFileSync as readFileSync26, realpathSync as
|
|
16532
|
+
import { readFileSync as readFileSync26, realpathSync as realpathSync14 } from "fs";
|
|
16452
16533
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
16453
|
-
import { join as
|
|
16534
|
+
import { join as join32, sep as sep12 } from "path";
|
|
16454
16535
|
|
|
16455
16536
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
16456
16537
|
import { existsSync as existsSync19, readdirSync as readdirSync12, readFileSync as readFileSync25, statSync as statSync13 } from "fs";
|
|
16457
|
-
import { dirname as dirname12, join as
|
|
16538
|
+
import { dirname as dirname12, join as join31 } from "path";
|
|
16458
16539
|
import { createHash as createHash10, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
|
|
16459
16540
|
|
|
16460
16541
|
// ../../loki-ts/src/util/paths.ts
|
|
16461
|
-
import { resolve as
|
|
16542
|
+
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
16462
16543
|
import { fileURLToPath } from "url";
|
|
16463
16544
|
import { existsSync as existsSync12 } from "fs";
|
|
16464
16545
|
var HERE = dirname4(fileURLToPath(import.meta.url));
|
|
16465
16546
|
function findRepoRoot() {
|
|
16466
16547
|
let dir = HERE;
|
|
16467
16548
|
for (let i = 0;i < 6; i++) {
|
|
16468
|
-
if (existsSync12(
|
|
16549
|
+
if (existsSync12(resolve6(dir, "VERSION")) && existsSync12(resolve6(dir, "autonomy/run.sh"))) {
|
|
16469
16550
|
return dir;
|
|
16470
16551
|
}
|
|
16471
16552
|
const parent = dirname4(dir);
|
|
@@ -16473,7 +16554,7 @@ function findRepoRoot() {
|
|
|
16473
16554
|
break;
|
|
16474
16555
|
dir = parent;
|
|
16475
16556
|
}
|
|
16476
|
-
return
|
|
16557
|
+
return resolve6(HERE, "..", "..", "..");
|
|
16477
16558
|
}
|
|
16478
16559
|
var REPO_ROOT = findRepoRoot();
|
|
16479
16560
|
|
|
@@ -16481,14 +16562,14 @@ var REPO_ROOT = findRepoRoot();
|
|
|
16481
16562
|
import { createHash as createHash9, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
|
|
16482
16563
|
import { chmodSync as chmodSync4, existsSync as existsSync18, readdirSync as readdirSync11, linkSync, mkdirSync as mkdirSync9, readFileSync as readFileSync23, statSync as statSync12, unlinkSync as unlinkSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
16483
16564
|
import { homedir as homedir6 } from "os";
|
|
16484
|
-
import { basename as
|
|
16565
|
+
import { basename as basename10, dirname as dirname11, join as join30 } from "path";
|
|
16485
16566
|
|
|
16486
16567
|
// ../../loki-ts/src/e10ext/assert_delta.ts
|
|
16487
|
-
import { execFileSync as
|
|
16568
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
16488
16569
|
import { mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
|
|
16489
16570
|
import { tmpdir as tmpdir2 } from "os";
|
|
16490
|
-
import { join as
|
|
16491
|
-
import { basename as
|
|
16571
|
+
import { join as join22 } from "path";
|
|
16572
|
+
import { basename as basename7 } from "path";
|
|
16492
16573
|
var WEAKENED = { verdict: "weakened", changes: [] };
|
|
16493
16574
|
var PY = String.raw`
|
|
16494
16575
|
import ast, copy, io, json, re, sys, tokenize
|
|
@@ -16639,15 +16720,15 @@ out("value-change" if ok and items else "weakened", items if ok else [])
|
|
|
16639
16720
|
`;
|
|
16640
16721
|
var okStr = (v) => typeof v === "string" && v.length <= 200 && !/[\x00-\x1f\x7f]/.test(v);
|
|
16641
16722
|
function classifyAssertDelta(inp) {
|
|
16642
|
-
const f =
|
|
16723
|
+
const f = basename7(inp.path);
|
|
16643
16724
|
if (!/^(test_.*|.*_test)\.py$/.test(f))
|
|
16644
16725
|
return WEAKENED;
|
|
16645
16726
|
if (inp.headCounts.run !== inp.baseCounts.run || inp.headCounts.skipped !== inp.baseCounts.skipped)
|
|
16646
16727
|
return WEAKENED;
|
|
16647
16728
|
let cwd = "";
|
|
16648
16729
|
try {
|
|
16649
|
-
cwd = mkdtempSync(
|
|
16650
|
-
const raw2 =
|
|
16730
|
+
cwd = mkdtempSync(join22(tmpdir2(), "loki-ad-"));
|
|
16731
|
+
const raw2 = execFileSync4("python3", ["-I", "-c", PY], {
|
|
16651
16732
|
input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
|
|
16652
16733
|
cwd,
|
|
16653
16734
|
env: process.env,
|
|
@@ -16676,8 +16757,8 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
|
|
|
16676
16757
|
if (!baseCounts || !headCounts)
|
|
16677
16758
|
return null;
|
|
16678
16759
|
try {
|
|
16679
|
-
const show = (ref) =>
|
|
16680
|
-
const head = headRef ? show(headRef) : readFileSync17(
|
|
16760
|
+
const show = (ref) => execFileSync4("git", ["show", `${ref}:${path}`], { cwd: repoDir, encoding: "utf8", env: process.env, stdio: ["ignore", "pipe", "ignore"] });
|
|
16761
|
+
const head = headRef ? show(headRef) : readFileSync17(join22(repoDir, path), "utf8");
|
|
16681
16762
|
const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
|
|
16682
16763
|
return r[0]?.kind === "literal-only" ? r.map((e) => `assertion value changed (not shown to be required by the task): ${e.file}:${e.line} ${e.old} -> ${e.new}`) : null;
|
|
16683
16764
|
} catch {
|
|
@@ -16693,16 +16774,16 @@ function classifyTestEdit(file, oldText, newText, taskText, baseCounts, headCoun
|
|
|
16693
16774
|
}
|
|
16694
16775
|
|
|
16695
16776
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
16696
|
-
import { execFileSync as
|
|
16697
|
-
import { chmodSync as chmodSync3, closeSync as
|
|
16698
|
-
import { dirname as dirname5, join as
|
|
16777
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
16778
|
+
import { chmodSync as chmodSync3, closeSync as closeSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync5, readFileSync as readFileSync18, realpathSync as realpathSync11, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
16779
|
+
import { dirname as dirname5, join as join23, relative, sep as sep8 } from "path";
|
|
16699
16780
|
function safeRestore(repoDir, f, want, mode) {
|
|
16700
|
-
const root =
|
|
16781
|
+
const root = realpathSync11(repoDir), fp = join23(root, f), rel = relative(root, dirname5(fp));
|
|
16701
16782
|
if (rel.startsWith(".."))
|
|
16702
16783
|
throw new Error("outside repo");
|
|
16703
16784
|
let cur = root;
|
|
16704
|
-
for (const part of rel === "" ? [] : rel.split(
|
|
16705
|
-
cur =
|
|
16785
|
+
for (const part of rel === "" ? [] : rel.split(sep8)) {
|
|
16786
|
+
cur = join23(cur, part);
|
|
16706
16787
|
let st2;
|
|
16707
16788
|
try {
|
|
16708
16789
|
st2 = lstatSync3(cur);
|
|
@@ -16729,11 +16810,11 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
16729
16810
|
else
|
|
16730
16811
|
unlinkSync3(fp);
|
|
16731
16812
|
}
|
|
16732
|
-
const fd =
|
|
16813
|
+
const fd = openSync5(fp, "wx", mode);
|
|
16733
16814
|
try {
|
|
16734
16815
|
writeFileSync6(fd, want);
|
|
16735
16816
|
} finally {
|
|
16736
|
-
|
|
16817
|
+
closeSync5(fd);
|
|
16737
16818
|
}
|
|
16738
16819
|
chmodSync3(fp, mode);
|
|
16739
16820
|
}
|
|
@@ -16751,7 +16832,7 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
|
|
|
16751
16832
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
16752
16833
|
const notProven = [];
|
|
16753
16834
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
16754
|
-
const fp =
|
|
16835
|
+
const fp = join23(repoDir, f);
|
|
16755
16836
|
try {
|
|
16756
16837
|
if (v.startsWith("!")) {
|
|
16757
16838
|
const st = lstatSync3(fp);
|
|
@@ -16759,7 +16840,7 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
|
|
|
16759
16840
|
throw new Error("changed, not snapshotted");
|
|
16760
16841
|
continue;
|
|
16761
16842
|
}
|
|
16762
|
-
const [sha2, mode] = v.split(" "), want =
|
|
16843
|
+
const [sha2, mode] = v.split(" "), want = execFileSync5("git", ["cat-file", "blob", sha2], { cwd: repoDir, env: process.env, maxBuffer: 1 << 28 });
|
|
16763
16844
|
safeRestore(repoDir, f, want, parseInt(mode ?? "644", 8));
|
|
16764
16845
|
} catch {
|
|
16765
16846
|
notProven.push(f);
|
|
@@ -16770,11 +16851,11 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
|
|
|
16770
16851
|
try {
|
|
16771
16852
|
let m = 420;
|
|
16772
16853
|
try {
|
|
16773
|
-
const l = lstatSync3(
|
|
16854
|
+
const l = lstatSync3(join23(repoDir, f));
|
|
16774
16855
|
if (l.isFile())
|
|
16775
16856
|
m = l.mode & 511;
|
|
16776
16857
|
} catch {}
|
|
16777
|
-
safeRestore(repoDir, f,
|
|
16858
|
+
safeRestore(repoDir, f, execFileSync5("git", ["cat-file", "blob", pre[f]], { cwd: repoDir, env: process.env, maxBuffer: 1 << 28 }), m);
|
|
16778
16859
|
} catch {
|
|
16779
16860
|
notProven.push(f);
|
|
16780
16861
|
}
|
|
@@ -16786,8 +16867,8 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
|
|
|
16786
16867
|
import { dirname as dirname6 } from "path";
|
|
16787
16868
|
|
|
16788
16869
|
// ../../loki-ts/src/e10ext/preexisting_dirty.ts
|
|
16789
|
-
import { execFileSync as
|
|
16790
|
-
var blob2 = (repoDir, path, env = process.env) =>
|
|
16870
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
16871
|
+
var blob2 = (repoDir, path, env = process.env) => execFileSync6("git", ["hash-object", "-w", "--no-filters", "--", path], { cwd: repoDir, encoding: "utf8", env, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
16791
16872
|
function untouchedSinceIntake(repoDir, recorded, env = process.env) {
|
|
16792
16873
|
if (!recorded || typeof recorded !== "object")
|
|
16793
16874
|
return [];
|
|
@@ -16807,10 +16888,10 @@ function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
|
|
|
16807
16888
|
}
|
|
16808
16889
|
|
|
16809
16890
|
// ../../loki-ts/src/e10ext/scope.ts
|
|
16810
|
-
import { basename as
|
|
16891
|
+
import { basename as basename9, dirname as dirname7 } from "path";
|
|
16811
16892
|
|
|
16812
16893
|
// ../../loki-ts/src/engine10/testmap.ts
|
|
16813
|
-
import { basename as
|
|
16894
|
+
import { basename as basename8, extname, join as join24, relative as relative2, sep as sep9 } from "path";
|
|
16814
16895
|
var SKIP_DIRS = new Set([
|
|
16815
16896
|
".git",
|
|
16816
16897
|
"node_modules",
|
|
@@ -16830,12 +16911,12 @@ var JS_TEST_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
|
16830
16911
|
var PY_TEST_RE = /^(test_.+|.+_test)\.py$/;
|
|
16831
16912
|
var GO_TEST_RE = /_test\.go$/;
|
|
16832
16913
|
function isTestFile(rel) {
|
|
16833
|
-
const name =
|
|
16914
|
+
const name = basename8(rel);
|
|
16834
16915
|
return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
|
|
16835
16916
|
}
|
|
16836
16917
|
|
|
16837
16918
|
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
16838
|
-
import { closeSync as
|
|
16919
|
+
import { closeSync as closeSync6, constants, fstatSync as fstatSync4, lstatSync as lstatSync4, openSync as openSync6, readSync as readSync4, realpathSync as realpathSync12 } from "fs";
|
|
16839
16920
|
var MAX_SPEC_BYTES = 1024 * 1024;
|
|
16840
16921
|
var MAX_TOKEN_BUDGET = 1e9;
|
|
16841
16922
|
var norm2 = (p) => p.replace(/^\.\//, "");
|
|
@@ -16845,15 +16926,15 @@ function readBounded(path) {
|
|
|
16845
16926
|
const l = lstatSync4(path);
|
|
16846
16927
|
if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
|
|
16847
16928
|
return null;
|
|
16848
|
-
const fd =
|
|
16929
|
+
const fd = openSync6(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
16849
16930
|
try {
|
|
16850
|
-
const st =
|
|
16931
|
+
const st = fstatSync4(fd);
|
|
16851
16932
|
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
16852
16933
|
return null;
|
|
16853
16934
|
const buf = Buffer.alloc(st.size + 1);
|
|
16854
16935
|
let n2 = 0;
|
|
16855
16936
|
for (;; ) {
|
|
16856
|
-
const r =
|
|
16937
|
+
const r = readSync4(fd, buf, n2, buf.length - n2, null);
|
|
16857
16938
|
if (r <= 0)
|
|
16858
16939
|
break;
|
|
16859
16940
|
n2 += r;
|
|
@@ -16862,7 +16943,7 @@ function readBounded(path) {
|
|
|
16862
16943
|
}
|
|
16863
16944
|
return buf.subarray(0, n2).toString("utf8");
|
|
16864
16945
|
} finally {
|
|
16865
|
-
|
|
16946
|
+
closeSync6(fd);
|
|
16866
16947
|
}
|
|
16867
16948
|
}
|
|
16868
16949
|
function parseText(txt) {
|
|
@@ -16923,7 +17004,7 @@ function outsideScope(o, staged) {
|
|
|
16923
17004
|
${typeof o.intake?.task === "string" ? o.intake.task : ""}
|
|
16924
17005
|
${contractText(o)}`, pre = o.intake?.preexisting_dirty ?? {};
|
|
16925
17006
|
const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) => dirname7(r)).filter((d) => d !== "."));
|
|
16926
|
-
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2,
|
|
17007
|
+
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename9(f)) && !plannedDirs.has(dirname7(f)) && !dirSegs(f).some((d) => toks.has(d))).map(({ f }) => f);
|
|
16927
17008
|
}
|
|
16928
17009
|
function flagOutsideScope(o, staged) {
|
|
16929
17010
|
const unit = unitFlags(staged, process.env, o.intake?.preexisting_dirty ?? {});
|
|
@@ -17007,7 +17088,7 @@ import { spawn as spawn5 } from "child_process";
|
|
|
17007
17088
|
|
|
17008
17089
|
// ../../loki-ts/src/project_model/discover.ts
|
|
17009
17090
|
import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync19, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
17010
|
-
import { join as
|
|
17091
|
+
import { join as join26 } from "path";
|
|
17011
17092
|
|
|
17012
17093
|
// ../../loki-ts/src/engine10/types.ts
|
|
17013
17094
|
var STAGE_BUDGETS = {
|
|
@@ -17024,23 +17105,23 @@ var STAGE_BUDGETS = {
|
|
|
17024
17105
|
};
|
|
17025
17106
|
|
|
17026
17107
|
// ../../loki-ts/src/project_model/schema.ts
|
|
17027
|
-
import { existsSync as existsSync13, lstatSync as lstatSync5, realpathSync as
|
|
17028
|
-
import { isAbsolute, join as
|
|
17108
|
+
import { existsSync as existsSync13, lstatSync as lstatSync5, realpathSync as realpathSync13, statSync as statSync10 } from "fs";
|
|
17109
|
+
import { isAbsolute, join as join25, normalize, sep as sep10 } from "path";
|
|
17029
17110
|
var PROJECT_MODEL_SCHEMA = "loki.v10.project/1";
|
|
17030
17111
|
var COMMAND_KINDS = ["test", "lint", "build", "start"];
|
|
17031
17112
|
var isRec = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
17032
17113
|
var isStr = (v) => typeof v === "string" && v.trim() !== "";
|
|
17033
17114
|
function safeRel(p) {
|
|
17034
|
-
const n2 = normalize(p.trim()).split(
|
|
17115
|
+
const n2 = normalize(p.trim()).split(sep10).join("/").replace(/\/$/, "");
|
|
17035
17116
|
if (n2 === "" || isAbsolute(n2) || n2 === ".." || n2.startsWith("../"))
|
|
17036
17117
|
return null;
|
|
17037
17118
|
return n2;
|
|
17038
17119
|
}
|
|
17039
17120
|
function insideRepo(repoDir, rel) {
|
|
17040
17121
|
try {
|
|
17041
|
-
const real2 =
|
|
17042
|
-
const base =
|
|
17043
|
-
return real2 === base || real2.startsWith(base +
|
|
17122
|
+
const real2 = realpathSync13(join25(repoDir, rel));
|
|
17123
|
+
const base = realpathSync13(repoDir);
|
|
17124
|
+
return real2 === base || real2.startsWith(base + sep10);
|
|
17044
17125
|
} catch {
|
|
17045
17126
|
return false;
|
|
17046
17127
|
}
|
|
@@ -17051,7 +17132,7 @@ function checkCitation(repoDir, raw2) {
|
|
|
17051
17132
|
const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
|
|
17052
17133
|
if (rel === null)
|
|
17053
17134
|
return `citation "${raw2}" is not a repo-relative path`;
|
|
17054
|
-
const abs =
|
|
17135
|
+
const abs = join25(repoDir, rel);
|
|
17055
17136
|
if (!existsSync13(abs) || !statSync10(abs).isFile() || !insideRepo(repoDir, rel))
|
|
17056
17137
|
return `citation "${raw2}" names a file that does not exist`;
|
|
17057
17138
|
return null;
|
|
@@ -17063,7 +17144,7 @@ function checkFingerprint(repoDir, raw2) {
|
|
|
17063
17144
|
if (rel === null)
|
|
17064
17145
|
return `fingerprint "${raw2}" is not a repo-relative path`;
|
|
17065
17146
|
try {
|
|
17066
|
-
if (!lstatSync5(
|
|
17147
|
+
if (!lstatSync5(join25(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
|
|
17067
17148
|
return `fingerprint "${raw2}" is not a regular file inside the repo`;
|
|
17068
17149
|
} catch {
|
|
17069
17150
|
return `fingerprint "${raw2}" names a file that does not exist`;
|
|
@@ -17091,7 +17172,7 @@ function checkDir(repoDir, v, where, errs) {
|
|
|
17091
17172
|
errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
|
|
17092
17173
|
return ".";
|
|
17093
17174
|
}
|
|
17094
|
-
const abs =
|
|
17175
|
+
const abs = join25(repoDir, rel);
|
|
17095
17176
|
if (!existsSync13(abs) || !statSync10(abs).isDirectory() || !insideRepo(repoDir, rel))
|
|
17096
17177
|
errs.push(`${where}: directory "${rel}" does not exist`);
|
|
17097
17178
|
return rel;
|
|
@@ -17186,7 +17267,7 @@ var PROJECT_FILE = ".loki/project.json";
|
|
|
17186
17267
|
var UNKNOWN_TTL_MS = 60 * 60 * 1000;
|
|
17187
17268
|
function loadCached(repoDir, now = Date.now()) {
|
|
17188
17269
|
try {
|
|
17189
|
-
return parseCached(repoDir, JSON.parse(readFileSync19(
|
|
17270
|
+
return parseCached(repoDir, JSON.parse(readFileSync19(join26(repoDir, PROJECT_FILE), "utf8")), now);
|
|
17190
17271
|
} catch {
|
|
17191
17272
|
return null;
|
|
17192
17273
|
}
|
|
@@ -17254,7 +17335,7 @@ function isMultiRoot(api) {
|
|
|
17254
17335
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
|
|
17255
17336
|
import { existsSync as existsSync15, lstatSync as lstatSync6, mkdirSync as mkdirSync7, readdirSync as readdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "fs";
|
|
17256
17337
|
import { createServer } from "net";
|
|
17257
|
-
import { dirname as dirname8, isAbsolute as isAbsolute2, join as
|
|
17338
|
+
import { dirname as dirname8, isAbsolute as isAbsolute2, join as join27, normalize as normalize2, relative as relative3, sep as sep11 } from "path";
|
|
17258
17339
|
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0";
|
|
17259
17340
|
var PAGE_RE = /^(?:.*\/)?(?:app|pages|src|public)\/.*\.(?:html|jsx|tsx|vue|svelte)$/;
|
|
17260
17341
|
var isPageFile = (p) => PAGE_RE.test(p);
|
|
@@ -17269,7 +17350,7 @@ function hashScreens(runDir, rels) {
|
|
|
17269
17350
|
const out2 = [];
|
|
17270
17351
|
for (const rel of rels) {
|
|
17271
17352
|
try {
|
|
17272
|
-
out2.push({ path: rel, sha256: sha256File(
|
|
17353
|
+
out2.push({ path: rel, sha256: sha256File(join27(runDir, rel)) });
|
|
17273
17354
|
} catch {}
|
|
17274
17355
|
}
|
|
17275
17356
|
return out2;
|
|
@@ -17282,10 +17363,10 @@ function checkScreens(runDir, screens) {
|
|
|
17282
17363
|
return "evidence_screens entry is malformed";
|
|
17283
17364
|
if (isAbsolute2(s.path) || normalize2(s.path).startsWith(".."))
|
|
17284
17365
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
17285
|
-
const file =
|
|
17366
|
+
const file = join27(runDir, s.path);
|
|
17286
17367
|
let cur = runDir;
|
|
17287
|
-
for (const seg of normalize2(s.path).split(
|
|
17288
|
-
cur =
|
|
17368
|
+
for (const seg of normalize2(s.path).split(sep11)) {
|
|
17369
|
+
cur = join27(cur, seg);
|
|
17289
17370
|
if (isSymlink(cur))
|
|
17290
17371
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
17291
17372
|
}
|
|
@@ -17331,12 +17412,12 @@ async function waitUp(url, ms, signal) {
|
|
|
17331
17412
|
return false;
|
|
17332
17413
|
}
|
|
17333
17414
|
var playwrightBin = (repoDir) => {
|
|
17334
|
-
const bin =
|
|
17415
|
+
const bin = join27(repoDir, "node_modules", ".bin", "playwright");
|
|
17335
17416
|
return existsSync15(bin) ? bin : null;
|
|
17336
17417
|
};
|
|
17337
17418
|
function openapiPaths(repoDir) {
|
|
17338
17419
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
17339
|
-
const p =
|
|
17420
|
+
const p = join27(repoDir, f);
|
|
17340
17421
|
if (!existsSync15(p))
|
|
17341
17422
|
continue;
|
|
17342
17423
|
try {
|
|
@@ -17350,8 +17431,8 @@ function openapiPaths(repoDir) {
|
|
|
17350
17431
|
return [];
|
|
17351
17432
|
}
|
|
17352
17433
|
var playwrightTestPkg = (repoDir) => {
|
|
17353
|
-
const d =
|
|
17354
|
-
return existsSync15(
|
|
17434
|
+
const d = join27(repoDir, "node_modules", "@playwright", "test");
|
|
17435
|
+
return existsSync15(join27(d, "package.json")) ? d : null;
|
|
17355
17436
|
};
|
|
17356
17437
|
var e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
17357
17438
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
@@ -17363,24 +17444,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
17363
17444
|
if (!pw || !pkg)
|
|
17364
17445
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
17365
17446
|
try {
|
|
17366
|
-
const dir =
|
|
17447
|
+
const dir = join27(runDir, relDir, "e2e");
|
|
17367
17448
|
mkdirSync7(dir, { recursive: true });
|
|
17368
|
-
writeFileSync8(
|
|
17369
|
-
writeFileSync8(
|
|
17449
|
+
writeFileSync8(join27(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
17450
|
+
writeFileSync8(join27(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
17370
17451
|
`);
|
|
17371
|
-
if (!await shoot(pw, ["test", "--config",
|
|
17452
|
+
if (!await shoot(pw, ["test", "--config", join27(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
17372
17453
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
17373
17454
|
const rels = [];
|
|
17374
17455
|
const walk = (d) => {
|
|
17375
17456
|
for (const e of readdirSync9(d, { withFileTypes: true })) {
|
|
17376
|
-
const full =
|
|
17457
|
+
const full = join27(d, e.name);
|
|
17377
17458
|
if (e.isDirectory())
|
|
17378
17459
|
walk(full);
|
|
17379
17460
|
else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
|
|
17380
|
-
rels.push(
|
|
17461
|
+
rels.push(join27(relDir, "e2e", relative3(dir, full)));
|
|
17381
17462
|
}
|
|
17382
17463
|
};
|
|
17383
|
-
walk(
|
|
17464
|
+
walk(join27(dir, "out"));
|
|
17384
17465
|
return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
|
|
17385
17466
|
} catch (e) {
|
|
17386
17467
|
return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
|
|
@@ -17474,8 +17555,8 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
17474
17555
|
if (!visualEvidenceEnabled(opts.env))
|
|
17475
17556
|
return skip("LOKI_VISUAL_EVIDENCE is 0");
|
|
17476
17557
|
const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
|
|
17477
|
-
const appDir = boot ?
|
|
17478
|
-
const pkgPath =
|
|
17558
|
+
const appDir = boot ? join27(repoDir, boot.boot.cwd) : repoDir;
|
|
17559
|
+
const pkgPath = join27(appDir, "package.json");
|
|
17479
17560
|
let script = null;
|
|
17480
17561
|
if (!boot) {
|
|
17481
17562
|
if (!existsSync15(pkgPath))
|
|
@@ -17507,20 +17588,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
17507
17588
|
}
|
|
17508
17589
|
if (!await waitUp(base, left(), signal))
|
|
17509
17590
|
return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
|
|
17510
|
-
const evRoot =
|
|
17591
|
+
const evRoot = join27(runDir, "evidence");
|
|
17511
17592
|
if (isSymlink(evRoot))
|
|
17512
17593
|
return skip("evidence dir is a symlink");
|
|
17513
17594
|
mkdirSync7(evRoot, { recursive: true });
|
|
17514
17595
|
if (pages.length > 0) {
|
|
17515
|
-
const rel0 =
|
|
17516
|
-
mkdirSync7(
|
|
17596
|
+
const rel0 = join27("evidence", randomBytes2(6).toString("hex"));
|
|
17597
|
+
mkdirSync7(join27(runDir, rel0));
|
|
17517
17598
|
const rels = [];
|
|
17518
17599
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
17519
17600
|
if (aborted() || left() <= 0)
|
|
17520
17601
|
break;
|
|
17521
|
-
const rel =
|
|
17522
|
-
const ok = await shoot(pw, ["screenshot", `${base}${route}`,
|
|
17523
|
-
if (ok && !aborted() && !isSymlink(
|
|
17602
|
+
const rel = join27(rel0, `${screenName(route)}.png`);
|
|
17603
|
+
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join27(runDir, rel)], appDir, left(), signal);
|
|
17604
|
+
if (ok && !aborted() && !isSymlink(join27(runDir, rel)) && existsSync15(join27(runDir, rel)))
|
|
17524
17605
|
rels.push(rel);
|
|
17525
17606
|
}
|
|
17526
17607
|
if (aborted())
|
|
@@ -17545,9 +17626,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
17545
17626
|
}
|
|
17546
17627
|
if (aborted())
|
|
17547
17628
|
return skip("aborted");
|
|
17548
|
-
const hdir =
|
|
17629
|
+
const hdir = join27(evRoot, randomBytes2(6).toString("hex"));
|
|
17549
17630
|
mkdirSync7(hdir);
|
|
17550
|
-
writeFileSync8(
|
|
17631
|
+
writeFileSync8(join27(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
17551
17632
|
`);
|
|
17552
17633
|
return { screens: [], http: true, skipped: null };
|
|
17553
17634
|
} catch (e) {
|
|
@@ -17575,7 +17656,7 @@ function receiptScreensProblem(receiptPath, receipt) {
|
|
|
17575
17656
|
|
|
17576
17657
|
// ../../loki-ts/src/features/contract.ts
|
|
17577
17658
|
import { existsSync as existsSync16, lstatSync as lstatSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync21, writeFileSync as writeFileSync9 } from "fs";
|
|
17578
|
-
import { dirname as dirname9, join as
|
|
17659
|
+
import { dirname as dirname9, join as join28 } from "path";
|
|
17579
17660
|
import { createHash as createHash7 } from "crypto";
|
|
17580
17661
|
var MAX_CRITERIA = 50;
|
|
17581
17662
|
var MAX_TEXT = 500;
|
|
@@ -17610,7 +17691,7 @@ function untracedLines(trace) {
|
|
|
17610
17691
|
return trace.criteria.filter((c) => c.status === "no_match").map((c) => `contract ${sanitizeCriterion(c.id)} untraced (no keyword match in changed files): ${sanitizeCriterion(c.text)}`);
|
|
17611
17692
|
}
|
|
17612
17693
|
function contractPath(repoDir) {
|
|
17613
|
-
return
|
|
17694
|
+
return join28(repoDir, ".loki", "contract.json");
|
|
17614
17695
|
}
|
|
17615
17696
|
var MAX_CONTRACT_BYTES = 1024 * 1024;
|
|
17616
17697
|
function readContract(repoDir) {
|
|
@@ -17692,14 +17773,14 @@ function sealContract(repoDir, body, rawDiff, checks, env = process.env, snap) {
|
|
|
17692
17773
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
17693
17774
|
import { createHash as createHash8 } from "crypto";
|
|
17694
17775
|
import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync22, statSync as statSync11 } from "fs";
|
|
17695
|
-
import { dirname as dirname10, join as
|
|
17776
|
+
import { dirname as dirname10, join as join29 } from "path";
|
|
17696
17777
|
var ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
17697
17778
|
var PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
17698
17779
|
var sha2 = (b) => createHash8("sha256").update(b).digest("hex");
|
|
17699
17780
|
var isObj9 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
17700
17781
|
var text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "";
|
|
17701
17782
|
var num2 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
17702
|
-
var unitsDir = (runDir) =>
|
|
17783
|
+
var unitsDir = (runDir) => join29(runDir, "group", "units");
|
|
17703
17784
|
var readJson3 = (p) => {
|
|
17704
17785
|
try {
|
|
17705
17786
|
return JSON.parse(readFileSync22(p, "utf8"));
|
|
@@ -17715,7 +17796,7 @@ var dirNames = (d) => {
|
|
|
17715
17796
|
}
|
|
17716
17797
|
};
|
|
17717
17798
|
function sealGroup(runDir, rh) {
|
|
17718
|
-
const manifestPath =
|
|
17799
|
+
const manifestPath = join29(runDir, "group", "manifest.json");
|
|
17719
17800
|
if (!existsSync17(manifestPath))
|
|
17720
17801
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
17721
17802
|
const notProven = [];
|
|
@@ -17740,7 +17821,7 @@ function sealGroup(runDir, rh) {
|
|
|
17740
17821
|
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
17741
17822
|
seen.add(id);
|
|
17742
17823
|
seenLower.add(id.toLowerCase());
|
|
17743
|
-
const dir =
|
|
17824
|
+
const dir = join29(unitsDir(runDir), id), rp = join29(dir, "receipt.json"), ep = join29(dir, "events.jsonl");
|
|
17744
17825
|
let bytes;
|
|
17745
17826
|
try {
|
|
17746
17827
|
bytes = readFileSync22(rp);
|
|
@@ -17785,7 +17866,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
17785
17866
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
17786
17867
|
const g = receipt["group"];
|
|
17787
17868
|
if (g === undefined)
|
|
17788
|
-
return existsSync17(
|
|
17869
|
+
return existsSync17(join29(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
17789
17870
|
if (!isObj9(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
17790
17871
|
return bad("group section malformed");
|
|
17791
17872
|
const entries = g["units"];
|
|
@@ -17797,7 +17878,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
17797
17878
|
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
17798
17879
|
let unchecked = null;
|
|
17799
17880
|
for (let i = 0;i < entries.length; i++) {
|
|
17800
|
-
const e = entries[i], id = ids[i], dir =
|
|
17881
|
+
const e = entries[i], id = ids[i], dir = join29(unitsDir(runDir), id), rp = join29(dir, "receipt.json"), ep = join29(dir, "events.jsonl");
|
|
17801
17882
|
if (e["index"] !== i)
|
|
17802
17883
|
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
17803
17884
|
let bytes;
|
|
@@ -17865,7 +17946,7 @@ function loadSigningKey(generate = true) {
|
|
|
17865
17946
|
let pem = inline ?? "";
|
|
17866
17947
|
if (!inline) {
|
|
17867
17948
|
const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
17868
|
-
const file = given ||
|
|
17949
|
+
const file = given || join30(process.env["HOME"] || homedir6(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
|
|
17869
17950
|
try {
|
|
17870
17951
|
pem = readFileSync23(file);
|
|
17871
17952
|
if (!given) {
|
|
@@ -18014,8 +18095,8 @@ var sealStage = {
|
|
|
18014
18095
|
notProven.add(`wall base run not_run: ${wallNotRun}`);
|
|
18015
18096
|
if (wallNotRun > 0) {
|
|
18016
18097
|
try {
|
|
18017
|
-
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) =>
|
|
18018
|
-
for (const n2 of readdirSync11(
|
|
18098
|
+
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename10(String(f.path))));
|
|
18099
|
+
for (const n2 of readdirSync11(join30(ctx.runDir, "wall")).sort())
|
|
18019
18100
|
if (!kept.has(n2))
|
|
18020
18101
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
18021
18102
|
} catch {}
|
|
@@ -18075,7 +18156,7 @@ var sealStage = {
|
|
|
18075
18156
|
const cost = ctx.cost.read(ctx.repoDir, iterIds);
|
|
18076
18157
|
if (cost.unmetered)
|
|
18077
18158
|
notProven.add("cost unmetered (CLI invoker; recorded as 0)");
|
|
18078
|
-
const eventsPath =
|
|
18159
|
+
const eventsPath = join30(ctx.runDir, "events.jsonl");
|
|
18079
18160
|
const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
|
|
18080
18161
|
const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
|
|
18081
18162
|
if (wallFiles.length > 0 && wallPassed === null)
|
|
@@ -18126,10 +18207,10 @@ var sealStage = {
|
|
|
18126
18207
|
}
|
|
18127
18208
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
18128
18209
|
mkdirSync9(ctx.runDir, { recursive: true });
|
|
18129
|
-
const path =
|
|
18210
|
+
const path = join30(ctx.runDir, "receipt.json");
|
|
18130
18211
|
writeFileSync10(path, JSON.stringify(receipt, null, 2) + `
|
|
18131
18212
|
`);
|
|
18132
|
-
writeFileSync10(
|
|
18213
|
+
writeFileSync10(join30(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
18133
18214
|
const signed = sig.jwt !== null;
|
|
18134
18215
|
const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven };
|
|
18135
18216
|
ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven });
|
|
@@ -18266,7 +18347,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
18266
18347
|
const bound = receipt["events_sha256"];
|
|
18267
18348
|
if (typeof bound !== "string" || bound === createHash10("sha256").digest("hex"))
|
|
18268
18349
|
return null;
|
|
18269
|
-
const path =
|
|
18350
|
+
const path = join31(dirname12(receiptPath), "events.jsonl");
|
|
18270
18351
|
if (!existsSync19(path))
|
|
18271
18352
|
return "events.jsonl is missing";
|
|
18272
18353
|
const raw2 = readFileSync25(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
|
|
@@ -18285,7 +18366,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
18285
18366
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
18286
18367
|
}
|
|
18287
18368
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
18288
|
-
const path =
|
|
18369
|
+
const path = join31(dirname12(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
18289
18370
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
18290
18371
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
18291
18372
|
if (done < 0)
|
|
@@ -18352,8 +18433,8 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
18352
18433
|
}
|
|
18353
18434
|
|
|
18354
18435
|
// src/server/routes/verify.ts
|
|
18355
|
-
var
|
|
18356
|
-
var safeId2 = (s) =>
|
|
18436
|
+
var ID5 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
18437
|
+
var safeId2 = (s) => ID5.test(s) && !s.includes("..");
|
|
18357
18438
|
function mount19(ctx) {
|
|
18358
18439
|
const { act, db } = ctx;
|
|
18359
18440
|
act.post("/v1/runs/:source/:run/verify", async (c) => {
|
|
@@ -18375,15 +18456,15 @@ function mount19(ctx) {
|
|
|
18375
18456
|
return done(404, "receipt not available", "not_found");
|
|
18376
18457
|
let runDir, file;
|
|
18377
18458
|
try {
|
|
18378
|
-
const root =
|
|
18379
|
-
runDir =
|
|
18380
|
-
if (runDir !==
|
|
18459
|
+
const root = realpathSync14(join32(repo.p, ".loki", "runs"));
|
|
18460
|
+
runDir = realpathSync14(join32(root, run2));
|
|
18461
|
+
if (runDir !== join32(root, run2))
|
|
18381
18462
|
return done(404, "receipt not found", "not_found");
|
|
18382
|
-
file =
|
|
18463
|
+
file = realpathSync14(join32(runDir, "receipt.json"));
|
|
18383
18464
|
} catch {
|
|
18384
18465
|
return done(404, "receipt not found", "not_found");
|
|
18385
18466
|
}
|
|
18386
|
-
if (!file.startsWith(runDir +
|
|
18467
|
+
if (!file.startsWith(runDir + sep12))
|
|
18387
18468
|
return done(404, "receipt not found", "not_found");
|
|
18388
18469
|
let integrity, reasons, sha3 = null, outcome = "UNREADABLE";
|
|
18389
18470
|
try {
|
|
@@ -18435,7 +18516,7 @@ function mount19(ctx) {
|
|
|
18435
18516
|
// src/server/routes/index.ts
|
|
18436
18517
|
var routeModules = [mount, mount18, mount5, mount7, mount17, mount8, mount3, mount9, mount11, mount4, mount10, mount12, mount6, mount16, mount19, mount13, mount15, mount2, mount14];
|
|
18437
18518
|
function registerRoutes(ctx) {
|
|
18438
|
-
mountRepos(ctx.act, ctx.db, ctx.peerIsLoopback);
|
|
18519
|
+
mountRepos(ctx.act, ctx.db, ctx.peerIsLoopback, ctx.repoDir);
|
|
18439
18520
|
for (const m of routeModules)
|
|
18440
18521
|
m(ctx);
|
|
18441
18522
|
}
|
|
@@ -18461,7 +18542,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now = Date.now) {
|
|
|
18461
18542
|
|
|
18462
18543
|
// src/server/legacy/shim.ts
|
|
18463
18544
|
import { existsSync as existsSync20, readFileSync as readFileSync27 } from "fs";
|
|
18464
|
-
import { join as
|
|
18545
|
+
import { join as join33 } from "path";
|
|
18465
18546
|
|
|
18466
18547
|
// src/server/legacy/routes.ts
|
|
18467
18548
|
var LEGACY_ROUTES = [
|
|
@@ -18833,7 +18914,7 @@ var logged = new Set;
|
|
|
18833
18914
|
var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
|
|
18834
18915
|
var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
18835
18916
|
var repoVersion = () => {
|
|
18836
|
-
for (const f of [
|
|
18917
|
+
for (const f of [join33(import.meta.dir, "../../../../../VERSION")]) {
|
|
18837
18918
|
try {
|
|
18838
18919
|
if (existsSync20(f))
|
|
18839
18920
|
return readFileSync27(f, "utf8").trim();
|
|
@@ -19018,7 +19099,7 @@ function legacyShim(opts) {
|
|
|
19018
19099
|
|
|
19019
19100
|
// src/server/app.ts
|
|
19020
19101
|
var MAX_BODY2 = 1e6;
|
|
19021
|
-
var defaultUiDir = () => [
|
|
19102
|
+
var defaultUiDir = () => [join34(import.meta.dir, "../../ui/dist"), join34(import.meta.dir, "../ui/dist")].find((d) => existsSync21(join34(d, "index.html")));
|
|
19022
19103
|
function createApp(opts) {
|
|
19023
19104
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
19024
19105
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -19142,13 +19223,13 @@ function createApp(opts) {
|
|
|
19142
19223
|
}
|
|
19143
19224
|
if (!uiDir || p.startsWith("/v1/"))
|
|
19144
19225
|
return c.json({ error: "not found" }, 404);
|
|
19145
|
-
const root =
|
|
19146
|
-
const f =
|
|
19147
|
-
if (f.startsWith(root +
|
|
19226
|
+
const root = resolve7(uiDir);
|
|
19227
|
+
const f = resolve7(root, `.${p}`);
|
|
19228
|
+
if (f.startsWith(root + sep13) && existsSync21(f) && statSync14(f).isFile())
|
|
19148
19229
|
return new Response(Bun.file(f));
|
|
19149
19230
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
19150
19231
|
return c.json({ error: "not found" }, 404);
|
|
19151
|
-
return new Response(Bun.file(
|
|
19232
|
+
return new Response(Bun.file(join34(root, "index.html")));
|
|
19152
19233
|
});
|
|
19153
19234
|
return { app, db, close: () => sqlite.close() };
|
|
19154
19235
|
}
|