loki-mode 10.9.1 → 10.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/issue-to-pr/action.yml +2 -2
- package/README.md +9 -3
- package/SKILL.md +12 -2
- package/VERSION +1 -1
- package/autonomy/lib/proof-generator.py +1 -0
- package/autonomy/loki +39 -9
- package/autonomy/receipt_jwt.py +46 -1
- package/autonomy/run.sh +1 -1
- package/autonomy/telemetry.sh +1 -1
- package/autonomy/trigger-server.py +1 -0
- package/bin/loki +12 -1
- package/bin/loki-postinstall.js +12 -0
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +139 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/LOKI-10-BUILD-PROMPT.md +2 -2
- package/docs/PLANS-INDEX.md +2 -3
- package/docs/SLO.md +3 -3
- package/docs/alternative-installations.md +1 -1
- package/docs/bug-fixes/agent-04-memory-fixes.md +1 -1
- package/docs/dev/release-checklist.md +1 -1
- package/{.github/workflows/loki-issue-to-pr.yml → docs/examples/loki-issue-to-pr.yml.example} +2 -0
- package/docs/{ONE-RUN-AUDIT.md → history/one-run-audit.md} +6 -0
- package/docs/{BUG-AUDIT-v6.61.0.md → history/v6.61.0-audit.md} +6 -0
- package/docs/{V8-AGENT-SDK-PLAN.md → history/v8-agent-sdk-plan.md} +6 -0
- package/docs/{CONFIG-FILE-PLAN.md → history/v8-config-plan.md} +6 -0
- package/docs/test-scenarios/enterprise-scenarios.md +1 -1
- package/docs/v10/BOARD.md +3 -0
- package/docs/v10/CLI-MODERN.md +190 -0
- package/docs/v10/DEPS.md +148 -169
- package/docs/v10/FAILURE-CLASSES.md +18 -0
- package/docs/v10/LEGACY-REMOVAL.md +334 -25
- package/docs/v10/PROGRESS.md +15 -0
- package/loki-ts/dist/loki.js +1570 -1790
- package/mcp/__init__.py +1 -1
- package/package.json +3 -2
- package/packages/control-plane/dist/server.js +424 -327
- package/packages/control-plane/src/db/fixture-cleanup.ts +47 -0
- package/packages/control-plane/src/server/app.ts +2 -0
- package/packages/control-plane/src/server/integrity.ts +48 -9
- package/packages/control-plane/src/server/routes/stats.ts +16 -5
- package/packages/control-plane/src/server/runs.ts +16 -4
- package/packages/control-plane/ui/dist/assets/index-NAMkcA9N.js +165 -0
- package/packages/control-plane/ui/dist/index.html +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/schemas/status-result.schema.json +13 -22
- package/skills/release-cadence.md +5 -5
- package/web-app/dist/assets/{AdminPage-DqMnfZrH.js → AdminPage-B8RjG3sy.js} +1 -1
- package/web-app/dist/assets/{Avatar-BEof29du.js → Avatar-Dttmvqc-.js} +1 -1
- package/web-app/dist/assets/{Badge-WD53OL5m.js → Badge-Bvyp8VrD.js} +1 -1
- package/web-app/dist/assets/{Button-Rzk5flhU.js → Button-DtY5ZrZw.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-Bqz3PCMh.js → CockpitPage-B2gbOwRp.js} +1 -1
- package/web-app/dist/assets/{ComparePage-Byo2NzAZ.js → ComparePage-CHi5nZTU.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-BuE0kH3W.js → ErrorBoundary-BUuTj-OQ.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CKFHOPBx.js → EvidenceReceiptPanel-C5aOn4ES.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Df_kTMqI.js → GitHubIssuesPanel-isVxxHXb.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CouvcKrZ.js → GitHubPRsPanel-DtDpxyQl.js} +1 -1
- package/web-app/dist/assets/{HomePage-dEpLzj0x.js → HomePage-Casf-LUB.js} +2 -2
- package/web-app/dist/assets/{LoginPage-6DVjUND5.js → LoginPage-DvB78691.js} +1 -1
- package/web-app/dist/assets/{MagicPage-BDQ9MOfY.js → MagicPage-DU6hmbnC.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-CXiJ04G_.js → MetricsPage-DcPwzoS8.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-BaXhZvyj.js → NotFoundPage-Bs6WxAZ8.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BzE4V7Ti.js → ProjectPage-RBIqC-DJ.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-ipf6TLMX.js → ProjectsPage-CogXD7jM.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-BDxkUKN6.js → SettingsPage-pAyJAfPt.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-YQXD72SM.js → ShowcasePage-D6bT2bDd.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-Dou7zHS0.js → SystemSettingsPage-CE8xiJRh.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-CQarfcYr.js → TeamsPage-wMS55TDT.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CPi1R8Xj.js → TemplatesPage-COJNVQqu.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-WiGhWdYD.js → TerminalOutput-Cc2XASxd.js} +1 -1
- package/web-app/dist/assets/{activity-DOa7ejQA.js → activity-C6eHFDjL.js} +1 -1
- package/web-app/dist/assets/{bell-CX-VAlYN.js → bell-BJpVYj4n.js} +1 -1
- package/web-app/dist/assets/{bot-EDXaZmHc.js → bot-BwRVVUQL.js} +1 -1
- package/web-app/dist/assets/{check-CnBdKk3L.js → check-CGDwoKy8.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BJ_ydRQW.js → chevron-left-CF2Nilj6.js} +1 -1
- package/web-app/dist/assets/{circle-alert-tz8ufhc_.js → circle-alert-D0RyRec1.js} +1 -1
- package/web-app/dist/assets/{clock-DFBZPSsH.js → clock-xeF3gW0W.js} +1 -1
- package/web-app/dist/assets/{cloud-uy3p_5Q7.js → cloud-D5Sy69ZZ.js} +1 -1
- package/web-app/dist/assets/{code-xml-3CbYD9YL.js → code-xml-C6-5Ql9I.js} +1 -1
- package/web-app/dist/assets/{database-YiNBXc9c.js → database-BSjG1_z0.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-D0wBa4Cv.js → dollar-sign-FnDR2QUu.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-DDdZVZ5v.js → file-code-corner-BiOK9vR6.js} +1 -1
- package/web-app/dist/assets/{file-plus-D09LTBqt.js → file-plus-3WALQDEE.js} +1 -1
- package/web-app/dist/assets/{globe-BirTPwff.js → globe-vUdUrQqp.js} +1 -1
- package/web-app/dist/assets/{hammer-DaE5qgnd.js → hammer-0VwHUEpa.js} +1 -1
- package/web-app/dist/assets/{index-D8t7KnQf.js → index-BMKIwteZ.js} +3 -3
- package/web-app/dist/assets/{layers-DVVrckRY.js → layers-C6q8kzST.js} +1 -1
- package/web-app/dist/assets/{loader-circle-2-nMGmcb.js → loader-circle-B4Tz724d.js} +1 -1
- package/web-app/dist/assets/{lock-D0Xj43pC.js → lock-yxvEA65n.js} +1 -1
- package/web-app/dist/assets/{package-BB7S3egm.js → package-BpKD0Ot0.js} +1 -1
- package/web-app/dist/assets/{plus-BLhoMrn3.js → plus-Ibqx_Vo9.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DMc2uPLw.js → refresh-cw-C9auyvi-.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-DM3IP9x7.js → rotate-ccw-2cEiXm6O.js} +1 -1
- package/web-app/dist/assets/{scroll-text-lH2uW7rB.js → scroll-text-BPAWML-w.js} +1 -1
- package/web-app/dist/assets/{server-BkerZcdR.js → server-CZh6B1zx.js} +1 -1
- package/web-app/dist/assets/{shield-alert-B8ohcUfu.js → shield-alert-CivolUIx.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-CTIxLzPD.js → thumbs-up-BP9covEH.js} +1 -1
- package/web-app/dist/assets/{trash-2-C3zIpsH5.js → trash-2-BCDfTcNx.js} +1 -1
- package/web-app/dist/assets/{trending-down-BTT7wV4Y.js → trending-down-5LypivXh.js} +1 -1
- package/web-app/dist/assets/{trending-up-BzHdCDvF.js → trending-up-DNt4SFdk.js} +1 -1
- package/web-app/dist/assets/{upload-HxR34-A8.js → upload-DBmHbY6y.js} +1 -1
- package/web-app/dist/assets/{usePolling-DDj6Fupy.js → usePolling-DOPZcbUL.js} +1 -1
- package/web-app/dist/assets/{user-qH5y9yNy.js → user-BQTJJDCG.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/docs/BRANCH-LIFECYCLE-PLAN.md +0 -354
- package/docs/EXECUTION-COCKPIT-PLAN.md +0 -289
- package/packages/control-plane/ui/dist/assets/index-Dbhu244h.js +0 -155
|
@@ -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: sep4, value } = collItem;
|
|
3700
3700
|
const keyProps = resolveProps.resolveProps(start, {
|
|
3701
3701
|
indicator: "explicit-key-ind",
|
|
3702
|
-
next: key ??
|
|
3702
|
+
next: key ?? sep4?.[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 && !sep4) {
|
|
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(sep4 ?? [], {
|
|
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, sep4, 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 sep4 = "";
|
|
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 += sep4 + cb;
|
|
3858
|
+
sep4 = "";
|
|
3859
3859
|
break;
|
|
3860
3860
|
}
|
|
3861
3861
|
case "newline":
|
|
3862
3862
|
if (comment)
|
|
3863
|
-
|
|
3863
|
+
sep4 += 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: sep4, 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 ?? sep4?.[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 && !sep4 && !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 && !sep4 && !props.found) {
|
|
3967
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep4, 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(sep4 ?? [], {
|
|
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 (sep4)
|
|
3991
|
+
for (const st of sep4) {
|
|
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, sep4, 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 sep4 = "";
|
|
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 += sep4 + indent.slice(trimIndent) + content;
|
|
4204
|
+
sep4 = `
|
|
4205
4205
|
`;
|
|
4206
4206
|
} else if (indent.length > trimIndent || content[0] === "\t") {
|
|
4207
|
-
if (
|
|
4208
|
-
|
|
4207
|
+
if (sep4 === " ")
|
|
4208
|
+
sep4 = `
|
|
4209
4209
|
`;
|
|
4210
|
-
else if (!prevMoreIndented &&
|
|
4210
|
+
else if (!prevMoreIndented && sep4 === `
|
|
4211
4211
|
`)
|
|
4212
|
-
|
|
4212
|
+
sep4 = `
|
|
4213
4213
|
|
|
4214
4214
|
`;
|
|
4215
|
-
value +=
|
|
4216
|
-
|
|
4215
|
+
value += sep4 + indent.slice(trimIndent) + content;
|
|
4216
|
+
sep4 = `
|
|
4217
4217
|
`;
|
|
4218
4218
|
prevMoreIndented = true;
|
|
4219
4219
|
} else if (content === "") {
|
|
4220
|
-
if (
|
|
4220
|
+
if (sep4 === `
|
|
4221
4221
|
`)
|
|
4222
4222
|
value += `
|
|
4223
4223
|
`;
|
|
4224
4224
|
else
|
|
4225
|
-
|
|
4225
|
+
sep4 = `
|
|
4226
4226
|
`;
|
|
4227
4227
|
} else {
|
|
4228
|
-
value +=
|
|
4229
|
-
|
|
4228
|
+
value += sep4 + content;
|
|
4229
|
+
sep4 = " ";
|
|
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 sep4 = " ";
|
|
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 (sep4 === `
|
|
4415
4415
|
`)
|
|
4416
|
-
res +=
|
|
4416
|
+
res += sep4;
|
|
4417
4417
|
else
|
|
4418
|
-
|
|
4418
|
+
sep4 = `
|
|
4419
4419
|
`;
|
|
4420
4420
|
} else {
|
|
4421
|
-
res +=
|
|
4422
|
-
|
|
4421
|
+
res += sep4 + lm;
|
|
4422
|
+
sep4 = " ";
|
|
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 + sep4 + (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: sep4, 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 (sep4)
|
|
5217
|
+
for (const st of sep4)
|
|
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 sep4;
|
|
6366
6366
|
if (scalar.end) {
|
|
6367
|
-
|
|
6368
|
-
|
|
6367
|
+
sep4 = scalar.end;
|
|
6368
|
+
sep4.push(this.sourceToken);
|
|
6369
6369
|
delete scalar.end;
|
|
6370
6370
|
} else
|
|
6371
|
-
|
|
6371
|
+
sep4 = [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: sep4 }]
|
|
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 sep4 = it.sep;
|
|
6531
|
+
sep4.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: sep4 }]
|
|
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 sep4 = fc.end.splice(1, fc.end.length);
|
|
6733
|
+
sep4.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: sep4 }]
|
|
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 existsSync19, statSync as statSync13 } from "fs";
|
|
6963
|
-
import { join as
|
|
6963
|
+
import { join as join31, resolve as resolve6, sep as sep11 } from "path";
|
|
6964
6964
|
|
|
6965
6965
|
// node_modules/hono/dist/request/constants.js
|
|
6966
6966
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -8394,6 +8394,90 @@ var Hono3 = class extends Hono {
|
|
|
8394
8394
|
}
|
|
8395
8395
|
};
|
|
8396
8396
|
|
|
8397
|
+
// src/db/fixture-cleanup.ts
|
|
8398
|
+
import { realpathSync } from "fs";
|
|
8399
|
+
import { tmpdir } from "os";
|
|
8400
|
+
import { sep } from "path";
|
|
8401
|
+
|
|
8402
|
+
// src/db/prune.ts
|
|
8403
|
+
var n = (sqlite, sql, ...args) => sqlite.query(sql).get(...args).n;
|
|
8404
|
+
function countFor(sqlite, keys) {
|
|
8405
|
+
const bySource = new Map;
|
|
8406
|
+
for (const k of keys)
|
|
8407
|
+
bySource.set(k.source_id, [...bySource.get(k.source_id) ?? [], k.run_id]);
|
|
8408
|
+
let events = 0, sources = 0;
|
|
8409
|
+
for (const [src, runIds] of bySource) {
|
|
8410
|
+
let ev = 0;
|
|
8411
|
+
for (const r of runIds)
|
|
8412
|
+
ev += n(sqlite, "select count(*) n from events where source_id = ? and run_id = ?", src, r);
|
|
8413
|
+
events += ev;
|
|
8414
|
+
const runsLeft = n(sqlite, "select count(*) n from runs where source_id = ?", src) - runIds.length;
|
|
8415
|
+
const eventsLeft = n(sqlite, "select count(*) n from events where source_id = ?", src) - ev;
|
|
8416
|
+
if (runsLeft <= 0 && eventsLeft <= 0 && sqlite.query("select 1 x from sources where id = ?").get(src))
|
|
8417
|
+
sources++;
|
|
8418
|
+
}
|
|
8419
|
+
return { runs: keys.length, events, sources };
|
|
8420
|
+
}
|
|
8421
|
+
function audit(sqlite, action, actor, detail) {
|
|
8422
|
+
sqlite.query("insert into audit (ts, action, actor, detail) values (?, ?, ?, ?)").run(new Date().toISOString(), action, actor, JSON.stringify(detail));
|
|
8423
|
+
}
|
|
8424
|
+
var dropOrphanSources = (sqlite, ids) => {
|
|
8425
|
+
for (const id of ids)
|
|
8426
|
+
sqlite.query("delete from sources where id = ? and not exists (select 1 from runs where source_id = ?) and not exists (select 1 from events where source_id = ?)").run(id, id, id);
|
|
8427
|
+
};
|
|
8428
|
+
function removeRun(sqlite, source, run, actor = "http") {
|
|
8429
|
+
let out = null;
|
|
8430
|
+
sqlite.transaction(() => {
|
|
8431
|
+
if (!sqlite.query("select 1 x from runs where source_id = ? and run_id = ?").get(source, run))
|
|
8432
|
+
return;
|
|
8433
|
+
out = countFor(sqlite, [{ source_id: source, run_id: run }]);
|
|
8434
|
+
audit(sqlite, "run.remove", actor, { source_id: source, run_id: run, counts: out });
|
|
8435
|
+
sqlite.query("delete from events where source_id = ? and run_id = ?").run(source, run);
|
|
8436
|
+
sqlite.query("delete from runs where source_id = ? and run_id = ?").run(source, run);
|
|
8437
|
+
dropOrphanSources(sqlite, [source]);
|
|
8438
|
+
}).immediate();
|
|
8439
|
+
return out;
|
|
8440
|
+
}
|
|
8441
|
+
|
|
8442
|
+
// src/db/fixture-cleanup.ts
|
|
8443
|
+
var FIXTURE_REPO = "acme/widget";
|
|
8444
|
+
var DONE = "fixture.cleanup.done";
|
|
8445
|
+
var tempRoots = () => {
|
|
8446
|
+
const roots = new Set(["/tmp", "/private/tmp", "/var/tmp", tmpdir()]);
|
|
8447
|
+
for (const r of [...roots]) {
|
|
8448
|
+
try {
|
|
8449
|
+
roots.add(realpathSync(r));
|
|
8450
|
+
} catch {}
|
|
8451
|
+
}
|
|
8452
|
+
return [...roots].map((r) => r.replace(/[\\/]+$/, "")).filter((r) => r.length > 1);
|
|
8453
|
+
};
|
|
8454
|
+
var underTemp = (p, roots) => roots.some((r) => p === r || p.startsWith(r + sep) || p.startsWith(r + "/"));
|
|
8455
|
+
function cleanupLeakedFixtures(sqlite) {
|
|
8456
|
+
let removed = 0;
|
|
8457
|
+
sqlite.transaction(() => {
|
|
8458
|
+
if (sqlite.query("select 1 x from audit where action = ?").get(DONE))
|
|
8459
|
+
return;
|
|
8460
|
+
const roots = tempRoots();
|
|
8461
|
+
const tempSources = new Set(sqlite.query("select source_id, realpath from local_repos").all().filter((r) => underTemp(r.realpath, roots)).map((r) => r.source_id));
|
|
8462
|
+
const all = sqlite.query("select source_id, run_id, origin_repo from runs").all();
|
|
8463
|
+
const doomed = all.filter((r) => r.origin_repo === FIXTURE_REPO || tempSources.has(r.source_id));
|
|
8464
|
+
const touched = new Set;
|
|
8465
|
+
for (const k of doomed) {
|
|
8466
|
+
const ev = sqlite.query("select count(*) n from events where source_id = ? and run_id = ?").get(k.source_id, k.run_id).n;
|
|
8467
|
+
audit(sqlite, "fixture.cleanup", "startup", { source_id: k.source_id, run_id: k.run_id, origin_repo: k.origin_repo, events: ev });
|
|
8468
|
+
sqlite.query("delete from events where source_id = ? and run_id = ?").run(k.source_id, k.run_id);
|
|
8469
|
+
sqlite.query("delete from runs where source_id = ? and run_id = ?").run(k.source_id, k.run_id);
|
|
8470
|
+
touched.add(k.source_id);
|
|
8471
|
+
removed++;
|
|
8472
|
+
}
|
|
8473
|
+
for (const id of touched) {
|
|
8474
|
+
sqlite.query("delete from sources where id = ? and not exists (select 1 from runs where source_id = ?) and not exists (select 1 from events where source_id = ?)").run(id, id, id);
|
|
8475
|
+
}
|
|
8476
|
+
audit(sqlite, DONE, "startup", { removed });
|
|
8477
|
+
}).immediate();
|
|
8478
|
+
return removed;
|
|
8479
|
+
}
|
|
8480
|
+
|
|
8397
8481
|
// src/db/migrate.ts
|
|
8398
8482
|
import { Database as Database2 } from "bun:sqlite";
|
|
8399
8483
|
|
|
@@ -12436,7 +12520,7 @@ __export(exports_schema, {
|
|
|
12436
12520
|
runs: () => runs,
|
|
12437
12521
|
localRepos: () => localRepos,
|
|
12438
12522
|
events: () => events,
|
|
12439
|
-
audit: () =>
|
|
12523
|
+
audit: () => audit2,
|
|
12440
12524
|
actions: () => actions
|
|
12441
12525
|
});
|
|
12442
12526
|
var sources = sqliteTable("sources", {
|
|
@@ -12495,7 +12579,7 @@ var runs = sqliteTable("runs", {
|
|
|
12495
12579
|
index("runs_verdict").on(t.verdict),
|
|
12496
12580
|
index("runs_group").on(t.groupId)
|
|
12497
12581
|
]);
|
|
12498
|
-
var
|
|
12582
|
+
var audit2 = sqliteTable("audit", {
|
|
12499
12583
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
12500
12584
|
ts: text("ts").notNull(),
|
|
12501
12585
|
action: text("action").notNull(),
|
|
@@ -12698,6 +12782,8 @@ function writeAnswer(dir, source, run, answer) {
|
|
|
12698
12782
|
// src/server/integrity.ts
|
|
12699
12783
|
import { createHash, createPublicKey, verify } from "crypto";
|
|
12700
12784
|
import { readFileSync as readFileSync2 } from "fs";
|
|
12785
|
+
import { homedir as homedir2 } from "os";
|
|
12786
|
+
import { join as join3 } from "path";
|
|
12701
12787
|
var SHA = /^[0-9a-f]{64}$/;
|
|
12702
12788
|
var SEAL_LINE = "log.sealed";
|
|
12703
12789
|
var REDACTED = /\[REDACTED(?::[A-Z_]+)?\]/;
|
|
@@ -12706,17 +12792,48 @@ var UNATTESTED_SUFFIX = " (unattested)";
|
|
|
12706
12792
|
var SUCCESS_VERDICTS = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
12707
12793
|
var normVerdict = (v) => typeof v === "string" ? v.trim().toUpperCase() : null;
|
|
12708
12794
|
var kidOf = (pub) => createHash("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest("base64url");
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12795
|
+
var isEd25519 = (k) => k.asymmetricKeyType === "ed25519";
|
|
12796
|
+
function localKeyInfo(env = process.env) {
|
|
12797
|
+
const inline = env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
|
|
12798
|
+
const given = env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
12799
|
+
try {
|
|
12800
|
+
let pub;
|
|
12801
|
+
if (inline)
|
|
12802
|
+
pub = createPublicKey(inline);
|
|
12803
|
+
else {
|
|
12804
|
+
const file = given || join3(env["HOME"] || homedir2(), ".loki", "keys", "receipt-ed25519.pem");
|
|
12805
|
+
try {
|
|
12806
|
+
pub = createPublicKey(readFileSync2(file));
|
|
12807
|
+
} catch (e) {
|
|
12808
|
+
if (!given && e.code === "ENOENT")
|
|
12809
|
+
return {};
|
|
12810
|
+
throw e;
|
|
12811
|
+
}
|
|
12715
12812
|
}
|
|
12716
|
-
|
|
12813
|
+
return isEd25519(pub) ? { key: pub } : { reason: "local signing key is not an Ed25519 key" };
|
|
12814
|
+
} catch (e) {
|
|
12815
|
+
const code = e.code;
|
|
12816
|
+
return { reason: `local signing key ${inline ? "(LOKI_RECEIPT_SIGNING_KEY)" : given ? "file" : "default file"} could not be used: ${code ?? "malformed PEM"}` };
|
|
12817
|
+
}
|
|
12818
|
+
}
|
|
12819
|
+
var pemList = (raw2) => (raw2 ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
|
|
12820
|
+
try {
|
|
12821
|
+
const k = createPublicKey(readFileSync2(f));
|
|
12822
|
+
return isEd25519(k) ? [k] : [];
|
|
12823
|
+
} catch {
|
|
12824
|
+
return [];
|
|
12825
|
+
}
|
|
12826
|
+
});
|
|
12827
|
+
function pubkeysFromEnv(env = process.env) {
|
|
12828
|
+
const info = localKeyInfo(env);
|
|
12829
|
+
const explicit = pemList(env["LOKI_CP_RECEIPT_PUBKEYS"]);
|
|
12830
|
+
const keys = [...info.key ? [info.key] : [], ...pemList(env["LOKI_RECEIPT_RETIRED_PUBKEYS"]), ...explicit];
|
|
12717
12831
|
const fn = (kid) => keys.find((k) => kidOf(k) === kid);
|
|
12718
12832
|
fn.configured = keys.length > 0;
|
|
12719
|
-
fn.
|
|
12833
|
+
fn.strict = explicit.length > 0;
|
|
12834
|
+
if (info.reason)
|
|
12835
|
+
fn.reason = info.reason;
|
|
12836
|
+
fn.fingerprint = createHash("sha256").update(keys.map(kidOf).sort().join(",") + (fn.strict ? "|strict" : "")).digest("hex").slice(0, 16);
|
|
12720
12837
|
return fn;
|
|
12721
12838
|
}
|
|
12722
12839
|
var line = (e) => JSON.stringify({ v: e.v, seq: e.seq, ts: e.ts, run: e.run, type: e.type, stage: e.stage, data: e.data });
|
|
@@ -12731,7 +12848,7 @@ function verifyRunIntegrity(evs, opts = {}) {
|
|
|
12731
12848
|
const sealedAt = evs.map((e, i) => e.type === "receipt.sealed" ? i : -1).filter((i) => i >= 0);
|
|
12732
12849
|
const finalised = doneAt >= 0 || sealedAt.length > 0;
|
|
12733
12850
|
const lines = evs.map(line);
|
|
12734
|
-
const keysConfigured = opts.keysConfigured ?? opts.pubkeyFor?.configured ?? false;
|
|
12851
|
+
const keysConfigured = opts.keysConfigured ?? opts.pubkeyFor?.strict ?? opts.pubkeyFor?.configured ?? false;
|
|
12735
12852
|
let sigChecked = false;
|
|
12736
12853
|
if (finalised) {
|
|
12737
12854
|
const gap = evs.findIndex((e, i) => e.seq !== i);
|
|
@@ -12928,8 +13045,15 @@ var parseRun = (r) => ({
|
|
|
12928
13045
|
status: r.endedAt ? "completed" : "running",
|
|
12929
13046
|
elapsed_s: elapsedS(r)
|
|
12930
13047
|
});
|
|
13048
|
+
function taskTitle(db, sourceId, runId) {
|
|
13049
|
+
const d = db.select({ data: events.data }).from(events).where(and(eq(events.sourceId, sourceId), eq(events.runId, runId), eq(events.type, "stage.completed"), eq(events.stage, "intake"))).orderBy(asc(events.seq)).limit(1).get()?.data;
|
|
13050
|
+
const raw2 = str(d?.["title"]) ?? str(d?.["task"]);
|
|
13051
|
+
const t = raw2?.split(`
|
|
13052
|
+
`)[0]?.trim();
|
|
13053
|
+
return t ? t.length > 140 ? `${t.slice(0, 139)}...` : t : null;
|
|
13054
|
+
}
|
|
12931
13055
|
var withLive = (db, r) => {
|
|
12932
|
-
const p = parseRun(r);
|
|
13056
|
+
const p = { ...parseRun(r), title: taskTitle(db, r.sourceId, r.runId) };
|
|
12933
13057
|
return r.endedAt ? { ...p, current_stage: null, files_touched: [] } : { ...p, ...liveInfo(loadEvents(db, r.sourceId, r.runId)) };
|
|
12934
13058
|
};
|
|
12935
13059
|
function liveInfo(evs) {
|
|
@@ -12980,11 +13104,11 @@ var verdictFilter = (v) => {
|
|
|
12980
13104
|
var RECOMPUTE_CHUNK = 200;
|
|
12981
13105
|
function recomputeLegacy(db) {
|
|
12982
13106
|
const fp = pubkeysFromEnv().fingerprint ?? "";
|
|
12983
|
-
let
|
|
13107
|
+
let n2 = 0;
|
|
12984
13108
|
for (;; ) {
|
|
12985
13109
|
const rows = db.select({ s: runs.sourceId, r: runs.runId }).from(runs).where(sql`${runs.attested} is null or ${runs.integrityKeyFp} is not ${fp}`).limit(RECOMPUTE_CHUNK).all();
|
|
12986
13110
|
if (rows.length === 0)
|
|
12987
|
-
return
|
|
13111
|
+
return n2;
|
|
12988
13112
|
db.transaction((tx) => {
|
|
12989
13113
|
const t = tx;
|
|
12990
13114
|
for (const x of rows) {
|
|
@@ -12994,7 +13118,7 @@ function recomputeLegacy(db) {
|
|
|
12994
13118
|
rebuildRun(t, x.s, x.r);
|
|
12995
13119
|
}
|
|
12996
13120
|
});
|
|
12997
|
-
|
|
13121
|
+
n2 += rows.length;
|
|
12998
13122
|
}
|
|
12999
13123
|
}
|
|
13000
13124
|
function listRuns(db, q) {
|
|
@@ -13016,12 +13140,14 @@ function runDetail(db, sourceId, runId) {
|
|
|
13016
13140
|
continue;
|
|
13017
13141
|
let s = stages.find((x) => x.stage === e.stage);
|
|
13018
13142
|
if (!s)
|
|
13019
|
-
stages.push(s = { stage: e.stage, started_at: null, ended_at: null, status: "started" });
|
|
13143
|
+
stages.push(s = { stage: e.stage, started_at: null, ended_at: null, status: "started", reason: null });
|
|
13020
13144
|
if (e.type === "stage.started")
|
|
13021
13145
|
s.started_at = e.ts;
|
|
13022
13146
|
else {
|
|
13023
13147
|
s.ended_at = e.ts;
|
|
13024
13148
|
s.status = e.type.slice("stage.".length);
|
|
13149
|
+
const why = e.data?.reason;
|
|
13150
|
+
s.reason = typeof why === "string" && why ? why : null;
|
|
13025
13151
|
}
|
|
13026
13152
|
}
|
|
13027
13153
|
const f = fold(evs);
|
|
@@ -13135,19 +13261,19 @@ function tokenGuard(token) {
|
|
|
13135
13261
|
|
|
13136
13262
|
// src/shipper/backfill.ts
|
|
13137
13263
|
import { readdirSync } from "fs";
|
|
13138
|
-
import { join as
|
|
13264
|
+
import { join as join5 } from "path";
|
|
13139
13265
|
|
|
13140
13266
|
// src/shipper/ship.ts
|
|
13141
13267
|
import { createHash as createHash4 } from "crypto";
|
|
13142
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
13268
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
13143
13269
|
import { hostname } from "os";
|
|
13144
|
-
import { join as
|
|
13270
|
+
import { join as join4 } from "path";
|
|
13145
13271
|
var BATCH = 200;
|
|
13146
13272
|
var MAX_BYTES = 800000;
|
|
13147
13273
|
function sourceId(repoDir) {
|
|
13148
13274
|
let real2 = repoDir;
|
|
13149
13275
|
try {
|
|
13150
|
-
real2 =
|
|
13276
|
+
real2 = realpathSync2(repoDir);
|
|
13151
13277
|
} catch {}
|
|
13152
13278
|
return createHash4("sha256").update(`${hostname()}\x00${real2}`).digest("hex").slice(0, 16);
|
|
13153
13279
|
}
|
|
@@ -13156,24 +13282,24 @@ var redactEvent = (e) => ({ ...e, data: redactDeep(e.data) });
|
|
|
13156
13282
|
var backoffMs = (failures, rnd = Math.random) => Math.min(60000, 1000 * 2 ** Math.max(0, failures - 1)) * (1 + 0.25 * rnd());
|
|
13157
13283
|
function readShip(runDir, url) {
|
|
13158
13284
|
try {
|
|
13159
|
-
const s = JSON.parse(readFileSync3(
|
|
13285
|
+
const s = JSON.parse(readFileSync3(join4(runDir, "ship.json"), "utf8"));
|
|
13160
13286
|
if (s.url === url && Number.isInteger(s.acked_seq))
|
|
13161
13287
|
return { acked_seq: s.acked_seq, url };
|
|
13162
13288
|
} catch {}
|
|
13163
13289
|
return { acked_seq: -1, url };
|
|
13164
13290
|
}
|
|
13165
13291
|
function writeShip(runDir, s) {
|
|
13166
|
-
const tmp =
|
|
13292
|
+
const tmp = join4(runDir, `ship.json.${process.pid}.tmp`);
|
|
13167
13293
|
writeFileSync2(tmp, JSON.stringify(s) + `
|
|
13168
13294
|
`);
|
|
13169
|
-
renameSync(tmp,
|
|
13295
|
+
renameSync(tmp, join4(runDir, "ship.json"));
|
|
13170
13296
|
}
|
|
13171
13297
|
async function shipRun(o) {
|
|
13172
13298
|
const st = readShip(o.runDir, o.url);
|
|
13173
|
-
const all = readEvents(
|
|
13299
|
+
const all = readEvents(join4(o.runDir, "events.jsonl"));
|
|
13174
13300
|
let lines = 0;
|
|
13175
13301
|
try {
|
|
13176
|
-
lines = readFileSync3(
|
|
13302
|
+
lines = readFileSync3(join4(o.runDir, "events.jsonl"), "utf8").split(`
|
|
13177
13303
|
`).filter((l) => l.trim() !== "").length;
|
|
13178
13304
|
} catch {}
|
|
13179
13305
|
const skipped = lines - all.length;
|
|
@@ -13216,7 +13342,7 @@ async function shipRun(o) {
|
|
|
13216
13342
|
return { ok: true, sent, acked_seq: st.acked_seq, pending: 0 };
|
|
13217
13343
|
}
|
|
13218
13344
|
var shipEnabled = (env) => env.LOKI_CONTROL_URL || null;
|
|
13219
|
-
var hasSpool = (runDir) => existsSync3(
|
|
13345
|
+
var hasSpool = (runDir) => existsSync3(join4(runDir, "events.jsonl"));
|
|
13220
13346
|
|
|
13221
13347
|
// src/shipper/backfill.ts
|
|
13222
13348
|
async function backfill(o) {
|
|
@@ -13226,7 +13352,7 @@ async function backfill(o) {
|
|
|
13226
13352
|
return { runs: 0, sent: 0, failed: [] };
|
|
13227
13353
|
const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
13228
13354
|
const attempts = o.attempts ?? 3;
|
|
13229
|
-
const root =
|
|
13355
|
+
const root = join5(o.repoDir, ".loki", "runs");
|
|
13230
13356
|
let ids = [];
|
|
13231
13357
|
try {
|
|
13232
13358
|
ids = readdirSync(root).sort();
|
|
@@ -13237,7 +13363,7 @@ async function backfill(o) {
|
|
|
13237
13363
|
let runs2 = 0, sent = 0;
|
|
13238
13364
|
const failed = [];
|
|
13239
13365
|
for (const id of ids) {
|
|
13240
|
-
const runDir =
|
|
13366
|
+
const runDir = join5(root, id);
|
|
13241
13367
|
if (!hasSpool(runDir))
|
|
13242
13368
|
continue;
|
|
13243
13369
|
runs2++;
|
|
@@ -13256,57 +13382,17 @@ async function backfill(o) {
|
|
|
13256
13382
|
return { runs: runs2, sent, failed };
|
|
13257
13383
|
}
|
|
13258
13384
|
|
|
13259
|
-
// src/db/prune.ts
|
|
13260
|
-
var n = (sqlite, sql2, ...args) => sqlite.query(sql2).get(...args).n;
|
|
13261
|
-
function countFor(sqlite, keys) {
|
|
13262
|
-
const bySource = new Map;
|
|
13263
|
-
for (const k of keys)
|
|
13264
|
-
bySource.set(k.source_id, [...bySource.get(k.source_id) ?? [], k.run_id]);
|
|
13265
|
-
let events2 = 0, sources2 = 0;
|
|
13266
|
-
for (const [src, runIds] of bySource) {
|
|
13267
|
-
let ev = 0;
|
|
13268
|
-
for (const r of runIds)
|
|
13269
|
-
ev += n(sqlite, "select count(*) n from events where source_id = ? and run_id = ?", src, r);
|
|
13270
|
-
events2 += ev;
|
|
13271
|
-
const runsLeft = n(sqlite, "select count(*) n from runs where source_id = ?", src) - runIds.length;
|
|
13272
|
-
const eventsLeft = n(sqlite, "select count(*) n from events where source_id = ?", src) - ev;
|
|
13273
|
-
if (runsLeft <= 0 && eventsLeft <= 0 && sqlite.query("select 1 x from sources where id = ?").get(src))
|
|
13274
|
-
sources2++;
|
|
13275
|
-
}
|
|
13276
|
-
return { runs: keys.length, events: events2, sources: sources2 };
|
|
13277
|
-
}
|
|
13278
|
-
function audit2(sqlite, action, actor, detail) {
|
|
13279
|
-
sqlite.query("insert into audit (ts, action, actor, detail) values (?, ?, ?, ?)").run(new Date().toISOString(), action, actor, JSON.stringify(detail));
|
|
13280
|
-
}
|
|
13281
|
-
var dropOrphanSources = (sqlite, ids) => {
|
|
13282
|
-
for (const id of ids)
|
|
13283
|
-
sqlite.query("delete from sources where id = ? and not exists (select 1 from runs where source_id = ?) and not exists (select 1 from events where source_id = ?)").run(id, id, id);
|
|
13284
|
-
};
|
|
13285
|
-
function removeRun(sqlite, source, run, actor = "http") {
|
|
13286
|
-
let out = null;
|
|
13287
|
-
sqlite.transaction(() => {
|
|
13288
|
-
if (!sqlite.query("select 1 x from runs where source_id = ? and run_id = ?").get(source, run))
|
|
13289
|
-
return;
|
|
13290
|
-
out = countFor(sqlite, [{ source_id: source, run_id: run }]);
|
|
13291
|
-
audit2(sqlite, "run.remove", actor, { source_id: source, run_id: run, counts: out });
|
|
13292
|
-
sqlite.query("delete from events where source_id = ? and run_id = ?").run(source, run);
|
|
13293
|
-
sqlite.query("delete from runs where source_id = ? and run_id = ?").run(source, run);
|
|
13294
|
-
dropOrphanSources(sqlite, [source]);
|
|
13295
|
-
}).immediate();
|
|
13296
|
-
return out;
|
|
13297
|
-
}
|
|
13298
|
-
|
|
13299
13385
|
// src/shipper/discover.ts
|
|
13300
|
-
import { readFileSync as readFileSync5, realpathSync as
|
|
13301
|
-
import { basename, dirname, join as
|
|
13386
|
+
import { readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync2 } from "fs";
|
|
13387
|
+
import { basename, dirname, join as join7, resolve } from "path";
|
|
13302
13388
|
|
|
13303
13389
|
// src/shipper/watch.ts
|
|
13304
13390
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
|
|
13305
|
-
import { homedir as
|
|
13306
|
-
import { join as
|
|
13391
|
+
import { homedir as homedir3 } from "os";
|
|
13392
|
+
import { join as join6 } from "path";
|
|
13307
13393
|
function registryRepos(env) {
|
|
13308
13394
|
try {
|
|
13309
|
-
const reg = JSON.parse(readFileSync4(
|
|
13395
|
+
const reg = JSON.parse(readFileSync4(join6(env.HOME || homedir3(), ".loki", "dashboard", "projects.json"), "utf8"));
|
|
13310
13396
|
return Object.values(reg.projects ?? {}).map((p) => p.path).filter((p) => typeof p === "string" && p.startsWith("/"));
|
|
13311
13397
|
} catch {
|
|
13312
13398
|
return [];
|
|
@@ -13318,7 +13404,7 @@ function discoverLocalRepos(repoDir, env = process.env) {
|
|
|
13318
13404
|
const out = new Map;
|
|
13319
13405
|
for (const dir of [repoDir, ...registryRepos(env)]) {
|
|
13320
13406
|
try {
|
|
13321
|
-
const real2 =
|
|
13407
|
+
const real2 = realpathSync3(dir);
|
|
13322
13408
|
if (!statSync2(real2).isDirectory())
|
|
13323
13409
|
continue;
|
|
13324
13410
|
const id = sourceId(real2);
|
|
@@ -13346,8 +13432,8 @@ function mountRepos(act, db, peerIsLoopback2) {
|
|
|
13346
13432
|
}
|
|
13347
13433
|
|
|
13348
13434
|
// src/server/routes/artifacts.ts
|
|
13349
|
-
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, realpathSync as
|
|
13350
|
-
import { join as
|
|
13435
|
+
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, realpathSync as realpathSync4 } from "fs";
|
|
13436
|
+
import { join as join8, sep as sep2 } from "path";
|
|
13351
13437
|
var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
|
|
13352
13438
|
var EVENTS_DEFAULT = 500;
|
|
13353
13439
|
var EVENTS_MAX = 1000;
|
|
@@ -13428,19 +13514,19 @@ function mount(ctx) {
|
|
|
13428
13514
|
return c.json({ error: "artifact not available" }, 404);
|
|
13429
13515
|
let runsRoot, runDir, file;
|
|
13430
13516
|
try {
|
|
13431
|
-
runsRoot =
|
|
13432
|
-
runDir =
|
|
13517
|
+
runsRoot = realpathSync4(join8(repo.p, ".loki", "runs"));
|
|
13518
|
+
runDir = realpathSync4(join8(runsRoot, run));
|
|
13433
13519
|
} catch {
|
|
13434
13520
|
return c.json({ error: "artifact not found" }, 404);
|
|
13435
13521
|
}
|
|
13436
|
-
if (runDir !==
|
|
13522
|
+
if (runDir !== join8(runsRoot, run))
|
|
13437
13523
|
return c.json({ error: "artifact not found" }, 404);
|
|
13438
13524
|
try {
|
|
13439
|
-
file =
|
|
13525
|
+
file = realpathSync4(join8(runDir, name));
|
|
13440
13526
|
} catch {
|
|
13441
13527
|
return c.json({ error: "artifact not found" }, 404);
|
|
13442
13528
|
}
|
|
13443
|
-
if (!file.startsWith(runDir +
|
|
13529
|
+
if (!file.startsWith(runDir + sep2))
|
|
13444
13530
|
return c.json({ error: "artifact not found" }, 404);
|
|
13445
13531
|
const r = readCapped(file);
|
|
13446
13532
|
if (r.status === 413)
|
|
@@ -13478,8 +13564,8 @@ function mount2(ctx) {
|
|
|
13478
13564
|
|
|
13479
13565
|
// src/server/routes/checkpoints.ts
|
|
13480
13566
|
import { execFileSync } from "child_process";
|
|
13481
|
-
import { appendFileSync, copyFileSync, cpSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync7, realpathSync as
|
|
13482
|
-
import { join as
|
|
13567
|
+
import { appendFileSync, copyFileSync, cpSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync7, realpathSync as realpathSync5, rmSync, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13568
|
+
import { join as join10, sep as sep3 } from "path";
|
|
13483
13569
|
|
|
13484
13570
|
// src/server/audit.ts
|
|
13485
13571
|
var clip = (s, n2) => s === undefined ? null : s.slice(0, n2);
|
|
@@ -13494,7 +13580,7 @@ function audit3(db, e) {
|
|
|
13494
13580
|
|
|
13495
13581
|
// src/server/routes/cost_ledger.ts
|
|
13496
13582
|
import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
|
|
13497
|
-
import { join as
|
|
13583
|
+
import { join as join9 } from "path";
|
|
13498
13584
|
var isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
13499
13585
|
var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
13500
13586
|
var r6 = (n2) => Math.round(n2 * 1e6) / 1e6;
|
|
@@ -13525,7 +13611,7 @@ var DEFAULT_PRICING = {
|
|
|
13525
13611
|
"gpt-5.6-luna": { input: 0.5, output: 4 }
|
|
13526
13612
|
};
|
|
13527
13613
|
function pricing(lokiDir) {
|
|
13528
|
-
const d = readJson(
|
|
13614
|
+
const d = readJson(join9(lokiDir, "pricing.json"));
|
|
13529
13615
|
const models = isObj2(d) && isObj2(d.models) ? d.models : {};
|
|
13530
13616
|
return { ...DEFAULT_PRICING, ...models };
|
|
13531
13617
|
}
|
|
@@ -13538,7 +13624,7 @@ function modelCost(table, model, inp, out, cr = 0, cw = 0) {
|
|
|
13538
13624
|
return r6((inp * ir + out * or2 + cr * crr + cw * cwr) / 1e6);
|
|
13539
13625
|
}
|
|
13540
13626
|
function iterationRecords(lokiDir) {
|
|
13541
|
-
const dir =
|
|
13627
|
+
const dir = join9(lokiDir, "metrics", "efficiency");
|
|
13542
13628
|
let names = [];
|
|
13543
13629
|
try {
|
|
13544
13630
|
names = readdirSync3(dir).filter((n2) => /^iteration-.*\.json$/.test(n2)).sort();
|
|
@@ -13547,7 +13633,7 @@ function iterationRecords(lokiDir) {
|
|
|
13547
13633
|
}
|
|
13548
13634
|
const out = [];
|
|
13549
13635
|
for (const n2 of names) {
|
|
13550
|
-
const d = readJson(
|
|
13636
|
+
const d = readJson(join9(dir, n2));
|
|
13551
13637
|
if (isObj2(d))
|
|
13552
13638
|
out.push(d);
|
|
13553
13639
|
}
|
|
@@ -13584,7 +13670,7 @@ function costSnapshot(lokiDir) {
|
|
|
13584
13670
|
}
|
|
13585
13671
|
}
|
|
13586
13672
|
if (tIn === 0 && tOut === 0) {
|
|
13587
|
-
const ctx = readJson(
|
|
13673
|
+
const ctx = readJson(join9(lokiDir, "context", "tracking.json"));
|
|
13588
13674
|
const totals = isObj2(ctx) && isObj2(ctx.totals) ? ctx.totals : {};
|
|
13589
13675
|
tIn = num0(totals.total_input);
|
|
13590
13676
|
tOut = num0(totals.total_output);
|
|
@@ -13610,7 +13696,7 @@ function costSnapshot(lokiDir) {
|
|
|
13610
13696
|
}
|
|
13611
13697
|
}
|
|
13612
13698
|
let limit = null, used = null, remaining = null;
|
|
13613
|
-
const bd = readJson(
|
|
13699
|
+
const bd = readJson(join9(lokiDir, "metrics", "budget.json"));
|
|
13614
13700
|
if (isObj2(bd) && isNum(bd.limit)) {
|
|
13615
13701
|
limit = bd.limit;
|
|
13616
13702
|
if (recorded && !unknownUsd) {
|
|
@@ -13661,7 +13747,7 @@ function budgetSnapshot(lokiDir) {
|
|
|
13661
13747
|
total += priced(table, d);
|
|
13662
13748
|
}
|
|
13663
13749
|
let limit = null;
|
|
13664
|
-
const bd = readJson(
|
|
13750
|
+
const bd = readJson(join9(lokiDir, "metrics", "budget.json"));
|
|
13665
13751
|
if (isObj2(bd)) {
|
|
13666
13752
|
const l = bd.limit || bd.budget_limit;
|
|
13667
13753
|
limit = isNum(l) ? l : null;
|
|
@@ -13712,19 +13798,19 @@ function costTimeline(lokiDir) {
|
|
|
13712
13798
|
}
|
|
13713
13799
|
const runs2 = [];
|
|
13714
13800
|
let projectTotal = 0, runsMeasured = 0;
|
|
13715
|
-
const proofs =
|
|
13801
|
+
const proofs = join9(lokiDir, "proofs");
|
|
13716
13802
|
let entries = [];
|
|
13717
13803
|
try {
|
|
13718
13804
|
entries = readdirSync3(proofs).sort();
|
|
13719
13805
|
} catch {}
|
|
13720
13806
|
for (const e of entries) {
|
|
13721
13807
|
try {
|
|
13722
|
-
if (!statSync3(
|
|
13808
|
+
if (!statSync3(join9(proofs, e)).isDirectory())
|
|
13723
13809
|
continue;
|
|
13724
13810
|
} catch {
|
|
13725
13811
|
continue;
|
|
13726
13812
|
}
|
|
13727
|
-
const p = readJson(
|
|
13813
|
+
const p = readJson(join9(proofs, e, "proof.json"));
|
|
13728
13814
|
if (!isObj2(p))
|
|
13729
13815
|
continue;
|
|
13730
13816
|
const cost = isObj2(p.cost) ? p.cost : {};
|
|
@@ -13754,7 +13840,7 @@ function costTimeline(lokiDir) {
|
|
|
13754
13840
|
...recorded || runsMeasured ? {} : { not_measured: NOT_MEASURED }
|
|
13755
13841
|
};
|
|
13756
13842
|
}
|
|
13757
|
-
var lokiDirOf = (repoDir) =>
|
|
13843
|
+
var lokiDirOf = (repoDir) => join9(repoDir, ".loki");
|
|
13758
13844
|
var lokiDirExists = (repoDir) => existsSync4(lokiDirOf(repoDir));
|
|
13759
13845
|
function mount3(ctx) {
|
|
13760
13846
|
const deny = { error: "loopback only without a token" };
|
|
@@ -13768,8 +13854,8 @@ var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
|
|
|
13768
13854
|
var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
13769
13855
|
var MAX_CHECKPOINTS = 50;
|
|
13770
13856
|
var validCheckpointId = (id) => SAFE_ID.test(id);
|
|
13771
|
-
var storeOf = (repoDir) =>
|
|
13772
|
-
var lokiOf = (repoDir) =>
|
|
13857
|
+
var storeOf = (repoDir) => join10(repoDir, ".loki", "state", "checkpoints");
|
|
13858
|
+
var lokiOf = (repoDir) => join10(repoDir, ".loki");
|
|
13773
13859
|
var readObj = (f) => {
|
|
13774
13860
|
try {
|
|
13775
13861
|
const d = JSON.parse(readFileSync7(f, "utf8"));
|
|
@@ -13783,7 +13869,7 @@ function listCheckpoints(repoDir, limit = 20) {
|
|
|
13783
13869
|
const store = storeOf(repoDir);
|
|
13784
13870
|
let lines = [];
|
|
13785
13871
|
try {
|
|
13786
|
-
lines = readFileSync7(
|
|
13872
|
+
lines = readFileSync7(join10(store, "index.jsonl"), "utf8").split(`
|
|
13787
13873
|
`);
|
|
13788
13874
|
} catch {
|
|
13789
13875
|
return [];
|
|
@@ -13811,7 +13897,7 @@ function listCheckpoints(repoDir, limit = 20) {
|
|
|
13811
13897
|
message: str2(raw2.task) ?? str2(raw2.message) ?? str2(raw2.task_description),
|
|
13812
13898
|
iteration: Number.isInteger(it) ? it : null
|
|
13813
13899
|
};
|
|
13814
|
-
const meta = readObj(
|
|
13900
|
+
const meta = readObj(join10(store, id, "metadata.json"));
|
|
13815
13901
|
if (meta) {
|
|
13816
13902
|
cp.git_branch = str2(meta.git_branch);
|
|
13817
13903
|
cp.provider = str2(meta.provider);
|
|
@@ -13819,7 +13905,7 @@ function listCheckpoints(repoDir, limit = 20) {
|
|
|
13819
13905
|
cp.message ??= str2(meta.task_description);
|
|
13820
13906
|
cp.git_sha ??= str2(meta.git_sha);
|
|
13821
13907
|
cp.created_at ??= str2(meta.timestamp);
|
|
13822
|
-
cp.files_count = countFiles(
|
|
13908
|
+
cp.files_count = countFiles(join10(store, id));
|
|
13823
13909
|
}
|
|
13824
13910
|
out.push(cp);
|
|
13825
13911
|
}
|
|
@@ -13830,7 +13916,7 @@ function countFiles(dir) {
|
|
|
13830
13916
|
const walk = (d, top) => {
|
|
13831
13917
|
for (const e of readdirSync4(d, { withFileTypes: true })) {
|
|
13832
13918
|
if (e.isDirectory())
|
|
13833
|
-
walk(
|
|
13919
|
+
walk(join10(d, e.name), false);
|
|
13834
13920
|
else if (e.isFile() && !(top && e.name === "metadata.json"))
|
|
13835
13921
|
n2++;
|
|
13836
13922
|
}
|
|
@@ -13841,7 +13927,7 @@ function countFiles(dir) {
|
|
|
13841
13927
|
return n2;
|
|
13842
13928
|
}
|
|
13843
13929
|
function getCheckpoint(repoDir, id) {
|
|
13844
|
-
return validCheckpointId(id) ? readObj(
|
|
13930
|
+
return validCheckpointId(id) ? readObj(join10(storeOf(repoDir), id, "metadata.json")) : null;
|
|
13845
13931
|
}
|
|
13846
13932
|
var gitHead = (repoDir) => {
|
|
13847
13933
|
try {
|
|
@@ -13853,69 +13939,69 @@ var gitHead = (repoDir) => {
|
|
|
13853
13939
|
function createCheckpoint(repoDir, message, now = new Date) {
|
|
13854
13940
|
const store = storeOf(repoDir), loki = lokiOf(repoDir);
|
|
13855
13941
|
mkdirSync3(store, { recursive: true });
|
|
13856
|
-
const id = `chk-${stamp(now)}`, dir =
|
|
13942
|
+
const id = `chk-${stamp(now)}`, dir = join10(store, id);
|
|
13857
13943
|
mkdirSync3(dir, { recursive: true });
|
|
13858
13944
|
for (const f of ["dashboard-state.json", "session.json"]) {
|
|
13859
13945
|
try {
|
|
13860
|
-
if (existsSync5(
|
|
13861
|
-
copyFileSync(
|
|
13946
|
+
if (existsSync5(join10(loki, f)))
|
|
13947
|
+
copyFileSync(join10(loki, f), join10(dir, f));
|
|
13862
13948
|
} catch {}
|
|
13863
13949
|
}
|
|
13864
13950
|
try {
|
|
13865
|
-
if (existsSync5(
|
|
13866
|
-
cpSync(
|
|
13951
|
+
if (existsSync5(join10(loki, "queue")))
|
|
13952
|
+
cpSync(join10(loki, "queue"), join10(dir, "queue"), { recursive: true });
|
|
13867
13953
|
} catch {}
|
|
13868
13954
|
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) };
|
|
13869
|
-
writeFileSync3(
|
|
13870
|
-
appendFileSync(
|
|
13955
|
+
writeFileSync3(join10(dir, "metadata.json"), JSON.stringify(meta, null, 2));
|
|
13956
|
+
appendFileSync(join10(store, "index.jsonl"), `${JSON.stringify(meta)}
|
|
13871
13957
|
`);
|
|
13872
13958
|
const mine = readdirSync4(store, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("chk-")).map((e) => e.name).sort();
|
|
13873
13959
|
while (mine.length > MAX_CHECKPOINTS)
|
|
13874
|
-
rmSync(
|
|
13960
|
+
rmSync(join10(store, mine.shift()), { recursive: true, force: true });
|
|
13875
13961
|
return meta;
|
|
13876
13962
|
}
|
|
13877
13963
|
function rollbackCheckpoint(repoDir, id, now = new Date) {
|
|
13878
13964
|
const store = storeOf(repoDir), loki = lokiOf(repoDir);
|
|
13879
13965
|
let real2 = "", realStore = "";
|
|
13880
13966
|
try {
|
|
13881
|
-
real2 =
|
|
13882
|
-
realStore =
|
|
13967
|
+
real2 = realpathSync5(join10(store, id));
|
|
13968
|
+
realStore = realpathSync5(store);
|
|
13883
13969
|
} catch {
|
|
13884
13970
|
return null;
|
|
13885
13971
|
}
|
|
13886
|
-
if (!real2.startsWith(`${realStore}${
|
|
13972
|
+
if (!real2.startsWith(`${realStore}${sep3}`) || !statSync4(real2).isDirectory())
|
|
13887
13973
|
return null;
|
|
13888
|
-
const preId = `rb-pre-${stamp(now)}`, pre =
|
|
13974
|
+
const preId = `rb-pre-${stamp(now)}`, pre = join10(store, preId);
|
|
13889
13975
|
mkdirSync3(pre, { recursive: true });
|
|
13890
13976
|
const notStore = (src) => src !== store;
|
|
13891
13977
|
for (const n2 of ["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json"]) {
|
|
13892
13978
|
try {
|
|
13893
|
-
if (existsSync5(
|
|
13894
|
-
copyFileSync(
|
|
13979
|
+
if (existsSync5(join10(loki, n2)))
|
|
13980
|
+
copyFileSync(join10(loki, n2), join10(pre, n2));
|
|
13895
13981
|
} catch {}
|
|
13896
13982
|
}
|
|
13897
13983
|
for (const d of ["state", "queue"]) {
|
|
13898
13984
|
try {
|
|
13899
|
-
if (existsSync5(
|
|
13900
|
-
cpSync(
|
|
13985
|
+
if (existsSync5(join10(loki, d)))
|
|
13986
|
+
cpSync(join10(loki, d), join10(pre, d), { recursive: true, filter: notStore });
|
|
13901
13987
|
} catch {}
|
|
13902
13988
|
}
|
|
13903
13989
|
const preMeta = { id: preId, created_at: now.toISOString(), message: `pre-rollback snapshot (before restoring ${id})`, created_by: "control plane rollback" };
|
|
13904
|
-
writeFileSync3(
|
|
13905
|
-
appendFileSync(
|
|
13990
|
+
writeFileSync3(join10(pre, "metadata.json"), JSON.stringify(preMeta, null, 2));
|
|
13991
|
+
appendFileSync(join10(store, "index.jsonl"), `${JSON.stringify(preMeta)}
|
|
13906
13992
|
`);
|
|
13907
13993
|
let restored = 0;
|
|
13908
13994
|
const errors = [];
|
|
13909
13995
|
for (const e of readdirSync4(real2, { withFileTypes: true })) {
|
|
13910
13996
|
if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
|
|
13911
13997
|
continue;
|
|
13912
|
-
const src =
|
|
13998
|
+
const src = join10(real2, e.name);
|
|
13913
13999
|
try {
|
|
13914
14000
|
if (lstatSync(src).isSymbolicLink()) {
|
|
13915
14001
|
errors.push(`${e.name}: symlink skipped`);
|
|
13916
14002
|
continue;
|
|
13917
14003
|
}
|
|
13918
|
-
cpSync(src,
|
|
14004
|
+
cpSync(src, join10(loki, e.name), { recursive: true, filter: (s) => s !== join10(real2, "state", "checkpoints") });
|
|
13919
14005
|
restored++;
|
|
13920
14006
|
} catch (err) {
|
|
13921
14007
|
errors.push(`${e.name}: ${err.message}`);
|
|
@@ -13925,14 +14011,14 @@ function rollbackCheckpoint(repoDir, id, now = new Date) {
|
|
|
13925
14011
|
}
|
|
13926
14012
|
function auditSummary(db, days, now = Date.now()) {
|
|
13927
14013
|
const since = new Date(now - days * 86400000).toISOString();
|
|
13928
|
-
const where = gte(
|
|
13929
|
-
const total = db.select({ n: sql`count(*)` }).from(
|
|
13930
|
-
const by = (col) => Object.fromEntries(db.select({ k: col, n: sql`count(*)` }).from(
|
|
14014
|
+
const where = gte(audit2.ts, since);
|
|
14015
|
+
const total = db.select({ n: sql`count(*)` }).from(audit2).where(where).get()?.n ?? 0;
|
|
14016
|
+
const by = (col) => Object.fromEntries(db.select({ k: col, n: sql`count(*)` }).from(audit2).where(where).groupBy(col).all().map((r) => [r.k, r.n]));
|
|
13931
14017
|
return {
|
|
13932
14018
|
period_days: days,
|
|
13933
14019
|
total_events: total,
|
|
13934
|
-
by_action: by(
|
|
13935
|
-
by_user: by(
|
|
14020
|
+
by_action: by(audit2.action),
|
|
14021
|
+
by_user: by(audit2.actor),
|
|
13936
14022
|
successful_events: null,
|
|
13937
14023
|
failed_events: null,
|
|
13938
14024
|
by_resource_type: null,
|
|
@@ -13998,8 +14084,8 @@ function mount4(ctx) {
|
|
|
13998
14084
|
|
|
13999
14085
|
// src/server/routes/config.ts
|
|
14000
14086
|
import { createHash as createHash5, randomBytes } from "crypto";
|
|
14001
|
-
import { closeSync as closeSync3, fchmodSync, fsyncSync, lstatSync as lstatSync2, openSync as openSync3, readFileSync as readFileSync9, realpathSync as
|
|
14002
|
-
import { basename as basename3, dirname as dirname2, join as
|
|
14087
|
+
import { closeSync as closeSync3, fchmodSync, fsyncSync, lstatSync as lstatSync2, openSync as openSync3, readFileSync as readFileSync9, realpathSync as realpathSync6, renameSync as renameSync2, statSync as statSync6, unlinkSync, writeSync as writeSync2 } from "fs";
|
|
14088
|
+
import { basename as basename3, dirname as dirname2, join as join12, sep as sep4 } from "path";
|
|
14003
14089
|
|
|
14004
14090
|
// node_modules/yaml/dist/index.js
|
|
14005
14091
|
var composer = require_composer();
|
|
@@ -14050,8 +14136,8 @@ var $visitAsync = visit.visitAsync;
|
|
|
14050
14136
|
// src/server/spawn.ts
|
|
14051
14137
|
import { spawn } from "child_process";
|
|
14052
14138
|
import { existsSync as existsSync6, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
14053
|
-
import { homedir as
|
|
14054
|
-
import { basename as basename2, join as
|
|
14139
|
+
import { homedir as homedir4 } from "os";
|
|
14140
|
+
import { basename as basename2, join as join11, resolve as resolve2 } from "path";
|
|
14055
14141
|
var ISSUE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}\/[A-Za-z0-9._-]{1,100}#[1-9][0-9]{0,8}$/;
|
|
14056
14142
|
var TASK = /^[A-Za-z0-9][A-Za-z0-9 .,_/#@+=-]{0,499}$/;
|
|
14057
14143
|
var hasDotDot = (t) => t.split(/[\\/ ]/).some((seg) => seg.startsWith(".."));
|
|
@@ -14067,7 +14153,7 @@ var BUDGET = /^(?:[1-9][0-9]{0,5}|0)(?:\.[0-9]{1,2})?$/;
|
|
|
14067
14153
|
var WORKSPACE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
14068
14154
|
function registryRepos2(env = process.env) {
|
|
14069
14155
|
try {
|
|
14070
|
-
const j = JSON.parse(readFileSync8(
|
|
14156
|
+
const j = JSON.parse(readFileSync8(join11(env.HOME || homedir4(), ".loki", "dashboard", "projects.json"), "utf8"));
|
|
14071
14157
|
return Object.values(j.projects ?? {}).map((p) => typeof p?.path === "string" ? p.path : "").filter((p) => p && existsSync6(p) && statSync5(p).isDirectory());
|
|
14072
14158
|
} catch {
|
|
14073
14159
|
return [];
|
|
@@ -14512,11 +14598,11 @@ function renderVerified(doc, next, quote = needsQuote) {
|
|
|
14512
14598
|
function resolveConfigFile(repoDir) {
|
|
14513
14599
|
let root;
|
|
14514
14600
|
try {
|
|
14515
|
-
root =
|
|
14601
|
+
root = realpathSync6(repoDir);
|
|
14516
14602
|
} catch {
|
|
14517
14603
|
return { ok: false, status: 400, error: "repo directory not found" };
|
|
14518
14604
|
}
|
|
14519
|
-
const p =
|
|
14605
|
+
const p = join12(root, "loki.yaml");
|
|
14520
14606
|
let st;
|
|
14521
14607
|
try {
|
|
14522
14608
|
st = lstatSync2(p);
|
|
@@ -14526,11 +14612,11 @@ function resolveConfigFile(repoDir) {
|
|
|
14526
14612
|
if (st.isSymbolicLink()) {
|
|
14527
14613
|
let real2;
|
|
14528
14614
|
try {
|
|
14529
|
-
real2 =
|
|
14615
|
+
real2 = realpathSync6(p);
|
|
14530
14616
|
} catch {
|
|
14531
14617
|
return { ok: false, status: 400, error: "loki.yaml is a broken symlink" };
|
|
14532
14618
|
}
|
|
14533
|
-
if (!real2.startsWith(root +
|
|
14619
|
+
if (!real2.startsWith(root + sep4))
|
|
14534
14620
|
return { ok: false, status: 400, error: "loki.yaml is a symlink that resolves outside the repo" };
|
|
14535
14621
|
if (!statSync6(real2).isFile())
|
|
14536
14622
|
return { ok: false, status: 422, error: "loki.yaml is not a regular file" };
|
|
@@ -14541,7 +14627,7 @@ function resolveConfigFile(repoDir) {
|
|
|
14541
14627
|
return { ok: true, file: p, exists: true };
|
|
14542
14628
|
}
|
|
14543
14629
|
function atomicWrite(file, text2, mode) {
|
|
14544
|
-
const tmp =
|
|
14630
|
+
const tmp = join12(dirname2(file), `.${basename3(file)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
|
|
14545
14631
|
const fd = openSync3(tmp, "wx", mode);
|
|
14546
14632
|
try {
|
|
14547
14633
|
writeSync2(fd, text2);
|
|
@@ -14691,14 +14777,14 @@ function mount6(ctx) {
|
|
|
14691
14777
|
}
|
|
14692
14778
|
|
|
14693
14779
|
// src/server/routes/control.ts
|
|
14694
|
-
import { existsSync as existsSync7, realpathSync as
|
|
14695
|
-
import { join as
|
|
14780
|
+
import { existsSync as existsSync7, realpathSync as realpathSync7 } from "fs";
|
|
14781
|
+
import { join as join14, resolve as resolve3, sep as sep5 } from "path";
|
|
14696
14782
|
|
|
14697
14783
|
// ../../loki-ts/src/util/run_pid.ts
|
|
14698
14784
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
14699
14785
|
import { chmodSync as chmodSync2, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
14700
|
-
import { join as
|
|
14701
|
-
var runPidPath = (runDir) =>
|
|
14786
|
+
import { join as join13 } from "path";
|
|
14787
|
+
var runPidPath = (runDir) => join13(runDir, "run.pid");
|
|
14702
14788
|
function processStartTime(pid) {
|
|
14703
14789
|
if (!Number.isInteger(pid) || pid <= 1)
|
|
14704
14790
|
return null;
|
|
@@ -14754,14 +14840,14 @@ function mount7(ctx) {
|
|
|
14754
14840
|
const repoRow = db.select().from(localRepos).where(eq(localRepos.sourceId, source)).get();
|
|
14755
14841
|
let repo = "";
|
|
14756
14842
|
try {
|
|
14757
|
-
repo = repoRow ?
|
|
14843
|
+
repo = repoRow ? realpathSync7(repoRow.realpath) : "";
|
|
14758
14844
|
} catch {
|
|
14759
14845
|
repo = "";
|
|
14760
14846
|
}
|
|
14761
14847
|
if (!repo)
|
|
14762
14848
|
return done(404, "refused", { error: "this run's repo is not known on this machine" });
|
|
14763
|
-
const runsRoot =
|
|
14764
|
-
if (!runDir.startsWith(runsRoot +
|
|
14849
|
+
const runsRoot = join14(repo, ".loki", "runs"), runDir = resolve3(runsRoot, id);
|
|
14850
|
+
if (!runDir.startsWith(runsRoot + sep5))
|
|
14765
14851
|
return done(400, "refused", { error: "invalid run id" }, "path escape");
|
|
14766
14852
|
return handler({ source, run: id, repo, runDir }, done);
|
|
14767
14853
|
});
|
|
@@ -14808,7 +14894,7 @@ function mount7(ctx) {
|
|
|
14808
14894
|
return done(404, "refused", { error: "run not found" });
|
|
14809
14895
|
if (!blockedQuestion(loadEvents(db, a.source, a.run), row.verdict))
|
|
14810
14896
|
return done(409, "refused", { error: "run is not BLOCKED, so there is nothing to resume" });
|
|
14811
|
-
if (!existsSync7(
|
|
14897
|
+
if (!existsSync7(join14(answerDir, a.source, `${a.run}.answer.txt`)))
|
|
14812
14898
|
return done(409, "refused", { error: "no answer recorded for this run; answer it first" });
|
|
14813
14899
|
return launch(a, [bin, "answer", a.run], done);
|
|
14814
14900
|
});
|
|
@@ -14887,11 +14973,11 @@ function mount8(ctx) {
|
|
|
14887
14973
|
|
|
14888
14974
|
// src/server/routes/fleet.ts
|
|
14889
14975
|
import { readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
|
|
14890
|
-
import { homedir as
|
|
14891
|
-
import { basename as basename4, join as
|
|
14976
|
+
import { homedir as homedir5 } from "os";
|
|
14977
|
+
import { basename as basename4, join as join15 } from "path";
|
|
14892
14978
|
var isNum2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
14893
14979
|
var isObj5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
14894
|
-
var registryFile = () =>
|
|
14980
|
+
var registryFile = () => join15(process.env.HOME ?? homedir5(), ".loki", "dashboard", "projects.json");
|
|
14895
14981
|
function pidAlive(pid) {
|
|
14896
14982
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
14897
14983
|
return false;
|
|
@@ -14906,33 +14992,33 @@ function projectSnapshot(path) {
|
|
|
14906
14992
|
const snap = { phase: "", iteration: null, cost_usd: null, started_at: null, ended_at: null };
|
|
14907
14993
|
if (!path)
|
|
14908
14994
|
return snap;
|
|
14909
|
-
const loki =
|
|
14910
|
-
const st = readJson(
|
|
14995
|
+
const loki = join15(path, ".loki");
|
|
14996
|
+
const st = readJson(join15(loki, "dashboard-state.json"));
|
|
14911
14997
|
if (isObj5(st)) {
|
|
14912
14998
|
snap.phase = typeof st.phase === "string" ? st.phase : "";
|
|
14913
14999
|
snap.iteration = Number.isInteger(st.iteration) ? st.iteration : null;
|
|
14914
15000
|
}
|
|
14915
|
-
const ss = readJson(
|
|
15001
|
+
const ss = readJson(join15(loki, "session.json"));
|
|
14916
15002
|
if (isObj5(ss)) {
|
|
14917
15003
|
const a = ss.startedAt || ss.started_at, b = ss.endedAt || ss.ended_at;
|
|
14918
15004
|
snap.started_at = typeof a === "string" ? a : null;
|
|
14919
15005
|
snap.ended_at = typeof b === "string" ? b : null;
|
|
14920
15006
|
}
|
|
14921
15007
|
let cost = 0, found = false;
|
|
14922
|
-
const eff =
|
|
15008
|
+
const eff = join15(loki, "metrics", "efficiency");
|
|
14923
15009
|
let names = [];
|
|
14924
15010
|
try {
|
|
14925
15011
|
names = readdirSync5(eff).filter((n2) => n2.endsWith(".json"));
|
|
14926
15012
|
} catch {}
|
|
14927
15013
|
for (const n2 of names) {
|
|
14928
|
-
const d = readJson(
|
|
15014
|
+
const d = readJson(join15(eff, n2));
|
|
14929
15015
|
if (isObj5(d) && isNum2(d.cost_usd) && recordIsMeasured(d)) {
|
|
14930
15016
|
cost += d.cost_usd;
|
|
14931
15017
|
found = true;
|
|
14932
15018
|
}
|
|
14933
15019
|
}
|
|
14934
15020
|
if (!found) {
|
|
14935
|
-
const t = readJson(
|
|
15021
|
+
const t = readJson(join15(loki, "context", "tracking.json"));
|
|
14936
15022
|
const totals = isObj5(t) && isObj5(t.totals) ? t.totals : null;
|
|
14937
15023
|
if (totals && isNum2(totals.total_cost_usd) && recordIsMeasured({ cost_usd: totals.total_cost_usd, input_tokens: totals.total_input, output_tokens: totals.total_output })) {
|
|
14938
15024
|
cost = totals.total_cost_usd;
|
|
@@ -15005,8 +15091,8 @@ function mount9(ctx) {
|
|
|
15005
15091
|
}
|
|
15006
15092
|
|
|
15007
15093
|
// src/server/routes/memory.ts
|
|
15008
|
-
import { existsSync as existsSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as
|
|
15009
|
-
import { join as
|
|
15094
|
+
import { existsSync as existsSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as realpathSync8, statSync as statSync7 } from "fs";
|
|
15095
|
+
import { join as join16, sep as sep6 } from "path";
|
|
15010
15096
|
var isObj6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15011
15097
|
var nonEmpty = (v) => !(v === undefined || v === null || v === "" || Array.isArray(v) && v.length === 0 || isObj6(v) && Object.keys(v).length === 0);
|
|
15012
15098
|
var focus = { dir: null };
|
|
@@ -15037,7 +15123,7 @@ function jsonFiles(dir, cap = Infinity) {
|
|
|
15037
15123
|
for (const e of ents.sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
15038
15124
|
if (out2.length >= cap)
|
|
15039
15125
|
return;
|
|
15040
|
-
const p =
|
|
15126
|
+
const p = join16(d, e.name);
|
|
15041
15127
|
if (e.isDirectory())
|
|
15042
15128
|
walk(p);
|
|
15043
15129
|
else if (e.isFile() && e.name.endsWith(".json"))
|
|
@@ -15058,24 +15144,24 @@ function setFocus(repoDir, raw2, registry = registryFile()) {
|
|
|
15058
15144
|
return { ok: false, error: "project_dir must be an absolute path without .. segments" };
|
|
15059
15145
|
let real2 = "";
|
|
15060
15146
|
try {
|
|
15061
|
-
real2 =
|
|
15147
|
+
real2 = realpathSync8(s);
|
|
15062
15148
|
if (!statSync7(real2).isDirectory())
|
|
15063
15149
|
throw new Error("not a dir");
|
|
15064
15150
|
} catch {
|
|
15065
15151
|
return { ok: false, error: `Directory does not exist: ${s}` };
|
|
15066
15152
|
}
|
|
15067
|
-
if (!existsSync8(
|
|
15153
|
+
if (!existsSync8(join16(real2, ".loki")) || !statSync7(join16(real2, ".loki")).isDirectory())
|
|
15068
15154
|
return { ok: false, error: `Directory does not contain a .loki/ subdirectory: ${s}` };
|
|
15069
15155
|
const allowed = new Set;
|
|
15070
15156
|
try {
|
|
15071
|
-
allowed.add(
|
|
15157
|
+
allowed.add(realpathSync8(repoDir));
|
|
15072
15158
|
} catch {}
|
|
15073
15159
|
const reg = readJson(registry);
|
|
15074
15160
|
if (isObj6(reg) && isObj6(reg.projects))
|
|
15075
15161
|
for (const p of Object.values(reg.projects)) {
|
|
15076
15162
|
if (isObj6(p) && typeof p.path === "string") {
|
|
15077
15163
|
try {
|
|
15078
|
-
allowed.add(
|
|
15164
|
+
allowed.add(realpathSync8(p.path));
|
|
15079
15165
|
} catch {}
|
|
15080
15166
|
}
|
|
15081
15167
|
}
|
|
@@ -15089,7 +15175,7 @@ var QUEUES = [["pending.json", "pending"], ["in-progress.json", "in_progress"],
|
|
|
15089
15175
|
var RANK = { done: 3, in_progress: 2, review: 1, pending: 0 };
|
|
15090
15176
|
function tasksList(loki, status) {
|
|
15091
15177
|
const all = [];
|
|
15092
|
-
const st = readJson(
|
|
15178
|
+
const st = readJson(join16(loki, "dashboard-state.json"));
|
|
15093
15179
|
const groups = isObj6(st) && isObj6(st.tasks) ? st.tasks : {};
|
|
15094
15180
|
for (const [key, mapped] of GROUPS) {
|
|
15095
15181
|
const g = groups[key];
|
|
@@ -15111,7 +15197,7 @@ function tasksList(loki, status) {
|
|
|
15111
15197
|
});
|
|
15112
15198
|
}
|
|
15113
15199
|
for (const [file, qs] of QUEUES) {
|
|
15114
|
-
const raw2 = readJson(
|
|
15200
|
+
const raw2 = readJson(join16(loki, "queue", file));
|
|
15115
15201
|
const items = isObj6(raw2) ? raw2.tasks : raw2;
|
|
15116
15202
|
if (!Array.isArray(items))
|
|
15117
15203
|
continue;
|
|
@@ -15165,7 +15251,7 @@ function tasksList(loki, status) {
|
|
|
15165
15251
|
}
|
|
15166
15252
|
return status ? out2.filter((t) => t.status === status) : out2;
|
|
15167
15253
|
}
|
|
15168
|
-
var memDir = (loki, sub) =>
|
|
15254
|
+
var memDir = (loki, sub) => join16(loki, "memory", sub);
|
|
15169
15255
|
function memoryEpisodes(loki, limit = 50) {
|
|
15170
15256
|
const eps = jsonFiles(memDir(loki, "episodic")).map(readJson).filter(isObj6);
|
|
15171
15257
|
eps.sort((a, b) => String(b.timestamp ?? "").localeCompare(String(a.timestamp ?? "")));
|
|
@@ -15174,19 +15260,19 @@ function memoryEpisodes(loki, limit = 50) {
|
|
|
15174
15260
|
function memoryEpisode(loki, id) {
|
|
15175
15261
|
for (const f of jsonFiles(memDir(loki, "episodic"))) {
|
|
15176
15262
|
const d = readJson(f);
|
|
15177
|
-
if (isObj6(d) && (d.id === id || f.split(
|
|
15263
|
+
if (isObj6(d) && (d.id === id || f.split(sep6).pop() === `${id}.json`))
|
|
15178
15264
|
return d;
|
|
15179
15265
|
}
|
|
15180
15266
|
return null;
|
|
15181
15267
|
}
|
|
15182
15268
|
function memoryPatterns(loki) {
|
|
15183
|
-
const d = readJson(
|
|
15269
|
+
const d = readJson(join16(memDir(loki, "semantic"), "patterns.json"));
|
|
15184
15270
|
const list = Array.isArray(d) ? d : isObj6(d) && Array.isArray(d.patterns) ? d.patterns : [];
|
|
15185
15271
|
return list.filter(isObj6);
|
|
15186
15272
|
}
|
|
15187
15273
|
var memorySkills = (loki) => jsonFiles(memDir(loki, "skills")).map(readJson).filter(isObj6);
|
|
15188
15274
|
function memoryEconomics(loki) {
|
|
15189
|
-
const rawV = readJson(
|
|
15275
|
+
const rawV = readJson(join16(loki, "memory", "token_economics.json"));
|
|
15190
15276
|
const raw2 = isObj6(rawV) ? rawV : {};
|
|
15191
15277
|
const m = isObj6(raw2.metrics) ? raw2.metrics : {};
|
|
15192
15278
|
const n2 = (v) => typeof v === "number" && Number.isFinite(v) ? Math.trunc(v) : 0;
|
|
@@ -15222,7 +15308,7 @@ function memoryEconomics(loki) {
|
|
|
15222
15308
|
};
|
|
15223
15309
|
}
|
|
15224
15310
|
function contextTracking(loki) {
|
|
15225
|
-
const f =
|
|
15311
|
+
const f = join16(loki, "context", "tracking.json");
|
|
15226
15312
|
if (!existsSync8(f)) {
|
|
15227
15313
|
return { status: 200, body: {
|
|
15228
15314
|
session_id: null,
|
|
@@ -15284,7 +15370,7 @@ function mount10(ctx) {
|
|
|
15284
15370
|
|
|
15285
15371
|
// src/server/routes/metrics.ts
|
|
15286
15372
|
import { readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
|
|
15287
|
-
import { join as
|
|
15373
|
+
import { join as join17 } from "path";
|
|
15288
15374
|
var isObj7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15289
15375
|
var arr = (v) => Array.isArray(v) ? v : [];
|
|
15290
15376
|
function buildMetricsText(repoDir, now = Date.now()) {
|
|
@@ -15301,7 +15387,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15301
15387
|
L.push(...samples);
|
|
15302
15388
|
L.push("");
|
|
15303
15389
|
};
|
|
15304
|
-
const stRaw = readJson(
|
|
15390
|
+
const stRaw = readJson(join17(loki, "dashboard-state.json"));
|
|
15305
15391
|
const state = isObj7(stRaw) ? stRaw : null;
|
|
15306
15392
|
let status = 0;
|
|
15307
15393
|
if (state?.mode === "paused")
|
|
@@ -15310,7 +15396,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15310
15396
|
status = 1;
|
|
15311
15397
|
else {
|
|
15312
15398
|
try {
|
|
15313
|
-
if (pidAlive(parseInt(readFileSync13(
|
|
15399
|
+
if (pidAlive(parseInt(readFileSync13(join17(loki, "loki.pid"), "utf8").trim(), 10)))
|
|
15314
15400
|
status = 1;
|
|
15315
15401
|
} catch {}
|
|
15316
15402
|
}
|
|
@@ -15326,7 +15412,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15326
15412
|
`loki_tasks_total{status="failed"} ${arr(tasks.failed).length}`
|
|
15327
15413
|
] : null);
|
|
15328
15414
|
let agents = null;
|
|
15329
|
-
const af = readJson(
|
|
15415
|
+
const af = readJson(join17(loki, "state", "agents.json"));
|
|
15330
15416
|
if (Array.isArray(af) && af.length)
|
|
15331
15417
|
agents = af;
|
|
15332
15418
|
else if (state && Array.isArray(state.agents) && state.agents.length)
|
|
@@ -15338,7 +15424,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15338
15424
|
gauge("loki_cost_usd", "Estimated total cost in USD", "gauge", cost.estimated_cost_usd !== null ? [`loki_cost_usd ${cost.estimated_cost_usd}`] : null);
|
|
15339
15425
|
let events2 = null;
|
|
15340
15426
|
try {
|
|
15341
|
-
events2 = readFileSync13(
|
|
15427
|
+
events2 = readFileSync13(join17(loki, "events.jsonl"), "utf8").split(`
|
|
15342
15428
|
`).filter((l) => l.trim()).length;
|
|
15343
15429
|
} catch {}
|
|
15344
15430
|
gauge("loki_events_total", "Total number of events recorded", "counter", events2 !== null ? [`loki_events_total ${events2}`] : null);
|
|
@@ -15350,19 +15436,19 @@ function buildMetricsText(repoDir, now = Date.now()) {
|
|
|
15350
15436
|
}
|
|
15351
15437
|
gauge("loki_uptime_seconds", "Seconds since session started", "gauge", uptime !== null ? [`loki_uptime_seconds ${uptime}`] : null);
|
|
15352
15438
|
let total = 0, ver = 0, gaps = 0, notver = 0, unk = 0, att = 0, sig = 0;
|
|
15353
|
-
const pdir =
|
|
15439
|
+
const pdir = join17(loki, "proofs");
|
|
15354
15440
|
let entries = [];
|
|
15355
15441
|
try {
|
|
15356
15442
|
entries = readdirSync7(pdir).sort();
|
|
15357
15443
|
} catch {}
|
|
15358
15444
|
for (const e of entries) {
|
|
15359
15445
|
try {
|
|
15360
|
-
if (!statSync8(
|
|
15446
|
+
if (!statSync8(join17(pdir, e)).isDirectory())
|
|
15361
15447
|
continue;
|
|
15362
15448
|
} catch {
|
|
15363
15449
|
continue;
|
|
15364
15450
|
}
|
|
15365
|
-
const d = readJson(
|
|
15451
|
+
const d = readJson(join17(pdir, e, "proof.json"));
|
|
15366
15452
|
if (!isObj7(d))
|
|
15367
15453
|
continue;
|
|
15368
15454
|
total++;
|
|
@@ -15407,7 +15493,7 @@ function mount11(ctx) {
|
|
|
15407
15493
|
|
|
15408
15494
|
// src/server/routes/session_control.ts
|
|
15409
15495
|
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync8, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
15410
|
-
import { dirname as dirname3, join as
|
|
15496
|
+
import { dirname as dirname3, join as join18 } from "path";
|
|
15411
15497
|
var isObj8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15412
15498
|
var ACTIONS = ["pause", "resume", "stop", "council-review"];
|
|
15413
15499
|
var isAction = (v) => ACTIONS.includes(v);
|
|
@@ -15432,7 +15518,7 @@ var readJson2 = (p) => {
|
|
|
15432
15518
|
};
|
|
15433
15519
|
function runnerPid(loki) {
|
|
15434
15520
|
try {
|
|
15435
|
-
const pid = parseInt(readFileSync14(
|
|
15521
|
+
const pid = parseInt(readFileSync14(join18(loki, "loki.pid"), "utf8").trim(), 10);
|
|
15436
15522
|
return pidAlive(pid) ? pid : null;
|
|
15437
15523
|
} catch {
|
|
15438
15524
|
return null;
|
|
@@ -15442,18 +15528,18 @@ function applyControl(repoDir, action) {
|
|
|
15442
15528
|
const loki = lokiDirOf(repoDir);
|
|
15443
15529
|
const running = runnerPid(loki) !== null;
|
|
15444
15530
|
const put = (rel) => {
|
|
15445
|
-
const f =
|
|
15531
|
+
const f = join18(loki, rel);
|
|
15446
15532
|
mkdirSync4(dirname3(f), { recursive: true });
|
|
15447
15533
|
writeFileSync5(f, new Date().toISOString());
|
|
15448
15534
|
};
|
|
15449
15535
|
if (action === "council-review") {
|
|
15450
|
-
put(
|
|
15536
|
+
put(join18("signals", "COUNCIL_REVIEW_REQUESTED"));
|
|
15451
15537
|
return { status: 200, body: { success: true, message: "Council review requested" } };
|
|
15452
15538
|
}
|
|
15453
15539
|
if (action === "resume") {
|
|
15454
15540
|
for (const n2 of ["PAUSE", "STOP"]) {
|
|
15455
15541
|
try {
|
|
15456
|
-
unlinkSync2(
|
|
15542
|
+
unlinkSync2(join18(loki, n2));
|
|
15457
15543
|
} catch {}
|
|
15458
15544
|
}
|
|
15459
15545
|
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" } };
|
|
@@ -15473,16 +15559,16 @@ function runControl(db, repoDir, action, c, via) {
|
|
|
15473
15559
|
return c.json(r.body, r.status);
|
|
15474
15560
|
}
|
|
15475
15561
|
function councilState(loki) {
|
|
15476
|
-
const f =
|
|
15562
|
+
const f = join18(loki, "council", "state.json");
|
|
15477
15563
|
if (!existsSync9(f))
|
|
15478
15564
|
return { enabled: false, total_votes: 0, verdicts: [] };
|
|
15479
15565
|
const d = readJson2(f);
|
|
15480
15566
|
return isObj8(d) ? d : { enabled: null, total_votes: null, verdicts: null, error: "unreadable_state" };
|
|
15481
15567
|
}
|
|
15482
15568
|
function councilVerdicts(loki, limit = 20) {
|
|
15483
|
-
const st = readJson2(
|
|
15569
|
+
const st = readJson2(join18(loki, "council", "state.json"));
|
|
15484
15570
|
const verdicts = isObj8(st) && Array.isArray(st.verdicts) ? st.verdicts : [];
|
|
15485
|
-
const votes =
|
|
15571
|
+
const votes = join18(loki, "council", "votes");
|
|
15486
15572
|
const details = [];
|
|
15487
15573
|
let names = [];
|
|
15488
15574
|
try {
|
|
@@ -15496,8 +15582,8 @@ function councilVerdicts(loki, limit = 20) {
|
|
|
15496
15582
|
}
|
|
15497
15583
|
};
|
|
15498
15584
|
for (const n2 of names.slice(0, limit)) {
|
|
15499
|
-
const dir =
|
|
15500
|
-
const ev = text2(
|
|
15585
|
+
const dir = join18(votes, n2), d = { iteration: n2 };
|
|
15586
|
+
const ev = text2(join18(dir, "evidence.md"));
|
|
15501
15587
|
if (ev !== null)
|
|
15502
15588
|
d.evidence_preview = ev.slice(0, 500);
|
|
15503
15589
|
let files = [];
|
|
@@ -15505,10 +15591,10 @@ function councilVerdicts(loki, limit = 20) {
|
|
|
15505
15591
|
files = readdirSync8(dir).filter((f) => /^member-.*\.txt$/.test(f)).sort();
|
|
15506
15592
|
} catch {}
|
|
15507
15593
|
d.members = files.flatMap((f) => {
|
|
15508
|
-
const t = text2(
|
|
15594
|
+
const t = text2(join18(dir, f));
|
|
15509
15595
|
return t === null ? [] : [{ member: f.replace(/\.txt$/, ""), content: t.trim() }];
|
|
15510
15596
|
});
|
|
15511
|
-
const con = text2(
|
|
15597
|
+
const con = text2(join18(dir, "contrarian.txt"));
|
|
15512
15598
|
if (con !== null)
|
|
15513
15599
|
d.contrarian = con.trim();
|
|
15514
15600
|
details.push(d);
|
|
@@ -15519,7 +15605,7 @@ function councilConvergence(loki) {
|
|
|
15519
15605
|
const dataPoints = [];
|
|
15520
15606
|
let raw2 = "";
|
|
15521
15607
|
try {
|
|
15522
|
-
raw2 = readFileSync14(
|
|
15608
|
+
raw2 = readFileSync14(join18(loki, "council", "convergence.log"), "utf8");
|
|
15523
15609
|
} catch {}
|
|
15524
15610
|
for (const line2 of raw2.trim().split(`
|
|
15525
15611
|
`)) {
|
|
@@ -15535,7 +15621,7 @@ function councilConvergence(loki) {
|
|
|
15535
15621
|
}
|
|
15536
15622
|
function councilReport(loki) {
|
|
15537
15623
|
try {
|
|
15538
|
-
return { report: readFileSync14(
|
|
15624
|
+
return { report: readFileSync14(join18(loki, "council", "report.md"), "utf8") };
|
|
15539
15625
|
} catch {
|
|
15540
15626
|
return { report: null };
|
|
15541
15627
|
}
|
|
@@ -15548,7 +15634,7 @@ function councilTranscripts(loki, q) {
|
|
|
15548
15634
|
if (Number.isNaN(since))
|
|
15549
15635
|
return { error: "Invalid 'since' timestamp format; expected ISO8601" };
|
|
15550
15636
|
}
|
|
15551
|
-
const dir =
|
|
15637
|
+
const dir = join18(loki, "council", "transcripts");
|
|
15552
15638
|
let names = [];
|
|
15553
15639
|
try {
|
|
15554
15640
|
names = readdirSync8(dir).filter((f) => /^iter-.*\.json$/.test(f)).sort().reverse();
|
|
@@ -15557,7 +15643,7 @@ function councilTranscripts(loki, q) {
|
|
|
15557
15643
|
}
|
|
15558
15644
|
const out2 = [];
|
|
15559
15645
|
for (const n2 of names) {
|
|
15560
|
-
const rec = readJson2(
|
|
15646
|
+
const rec = readJson2(join18(dir, n2));
|
|
15561
15647
|
if (!isObj8(rec) || typeof rec.iteration_id !== "string")
|
|
15562
15648
|
continue;
|
|
15563
15649
|
if (since !== null) {
|
|
@@ -15610,11 +15696,11 @@ function mount12(ctx) {
|
|
|
15610
15696
|
// src/server/routes/integrations.ts
|
|
15611
15697
|
import { spawn as spawn2 } from "child_process";
|
|
15612
15698
|
import { existsSync as existsSync10, readFileSync as readFileSync15, statSync as statSync9 } from "fs";
|
|
15613
|
-
import { delimiter, join as
|
|
15699
|
+
import { delimiter, join as join19 } from "path";
|
|
15614
15700
|
var GH_TIMEOUT_MS = 3000;
|
|
15615
15701
|
var isSet = (n2) => !!n2 && (process.env[n2] ?? "") !== "";
|
|
15616
15702
|
function ghAuthed(pathVar = process.env.PATH ?? "", timeoutMs = GH_TIMEOUT_MS) {
|
|
15617
|
-
const exe = pathVar.split(delimiter).filter(Boolean).map((d) =>
|
|
15703
|
+
const exe = pathVar.split(delimiter).filter(Boolean).map((d) => join19(d, "gh")).find((p) => existsSync10(p));
|
|
15618
15704
|
if (!exe)
|
|
15619
15705
|
return Promise.resolve(null);
|
|
15620
15706
|
return new Promise((resolve4) => {
|
|
@@ -15659,7 +15745,7 @@ function configuredName(repoDir, path) {
|
|
|
15659
15745
|
}
|
|
15660
15746
|
function mcpServers(repoDir) {
|
|
15661
15747
|
try {
|
|
15662
|
-
const p =
|
|
15748
|
+
const p = join19(repoDir, ".mcp.json");
|
|
15663
15749
|
if (!existsSync10(p) || statSync9(p).size > 1e6)
|
|
15664
15750
|
return null;
|
|
15665
15751
|
const j = JSON.parse(readFileSync15(p, "utf8"));
|
|
@@ -15740,7 +15826,7 @@ function mount13(ctx) {
|
|
|
15740
15826
|
|
|
15741
15827
|
// src/server/routes/merge_risk.ts
|
|
15742
15828
|
import { spawn as spawn3 } from "child_process";
|
|
15743
|
-
import { realpathSync as
|
|
15829
|
+
import { realpathSync as realpathSync9 } from "fs";
|
|
15744
15830
|
import { basename as basename5, resolve as resolve4 } from "path";
|
|
15745
15831
|
var PR = /^[0-9]{1,7}$/;
|
|
15746
15832
|
var REF = /^[A-Za-z0-9._/][A-Za-z0-9._/-]{0,99}$/;
|
|
@@ -15839,7 +15925,7 @@ function mount14(ctx) {
|
|
|
15839
15925
|
const active = { run: 0, queue: 0, risk: 0 };
|
|
15840
15926
|
const real2 = (p) => {
|
|
15841
15927
|
try {
|
|
15842
|
-
return
|
|
15928
|
+
return realpathSync9(p);
|
|
15843
15929
|
} catch {
|
|
15844
15930
|
return null;
|
|
15845
15931
|
}
|
|
@@ -16051,7 +16137,7 @@ function mount15(ctx) {
|
|
|
16051
16137
|
// src/server/routes/providers.ts
|
|
16052
16138
|
import { spawn as spawn4 } from "child_process";
|
|
16053
16139
|
import { existsSync as existsSync11, readFileSync as readFileSync16 } from "fs";
|
|
16054
|
-
import { delimiter as delimiter2, join as
|
|
16140
|
+
import { delimiter as delimiter2, join as join20 } from "path";
|
|
16055
16141
|
var ORDER = ["claude", "codex", "cline", "aider", "opencode"];
|
|
16056
16142
|
var DEPRECATED = ["gemini"];
|
|
16057
16143
|
var AUTH_ENV = {
|
|
@@ -16067,7 +16153,7 @@ function findOnPath(bin, pathVar) {
|
|
|
16067
16153
|
for (const d of pathVar.split(delimiter2)) {
|
|
16068
16154
|
if (!d)
|
|
16069
16155
|
continue;
|
|
16070
|
-
const p =
|
|
16156
|
+
const p = join20(d, bin);
|
|
16071
16157
|
if (existsSync11(p))
|
|
16072
16158
|
return p;
|
|
16073
16159
|
}
|
|
@@ -16112,14 +16198,14 @@ function probeCli(bin, timeoutMs, pathVar = process.env.PATH ?? "") {
|
|
|
16112
16198
|
});
|
|
16113
16199
|
}
|
|
16114
16200
|
function providersDir() {
|
|
16115
|
-
const cands = [process.env.LOKI_PROVIDERS_DIR,
|
|
16116
|
-
return cands.find((d) => !!d && existsSync11(
|
|
16201
|
+
const cands = [process.env.LOKI_PROVIDERS_DIR, join20(import.meta.dir, "../../../../../providers"), join20(import.meta.dir, "../../../providers")];
|
|
16202
|
+
return cands.find((d) => !!d && existsSync11(join20(d, "model_catalog.json"))) ?? null;
|
|
16117
16203
|
}
|
|
16118
16204
|
function loadCatalog(dir) {
|
|
16119
16205
|
if (!dir)
|
|
16120
16206
|
return { providers: {}, updated: null };
|
|
16121
16207
|
try {
|
|
16122
|
-
const j = JSON.parse(readFileSync16(
|
|
16208
|
+
const j = JSON.parse(readFileSync16(join20(dir, "model_catalog.json"), "utf8"));
|
|
16123
16209
|
return { providers: j.providers ?? {}, updated: typeof j.updated === "string" ? j.updated : null };
|
|
16124
16210
|
} catch {
|
|
16125
16211
|
return { providers: {}, updated: null };
|
|
@@ -16165,7 +16251,7 @@ function mount16(ctx) {
|
|
|
16165
16251
|
function computeStats(db, since) {
|
|
16166
16252
|
const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
|
|
16167
16253
|
const by = {};
|
|
16168
|
-
let finished = 0, running = 0, blocked = 0,
|
|
16254
|
+
let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed = 0, satisfied = 0, other = 0;
|
|
16169
16255
|
let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
|
|
16170
16256
|
for (const r of all) {
|
|
16171
16257
|
if (r.endedAt)
|
|
@@ -16176,11 +16262,16 @@ function computeStats(db, since) {
|
|
|
16176
16262
|
if (verdict) {
|
|
16177
16263
|
by[verdict] = (by[verdict] ?? 0) + 1;
|
|
16178
16264
|
if (r.endedAt) {
|
|
16179
|
-
withVerdict++;
|
|
16180
16265
|
if (verdict === "VERIFIED")
|
|
16181
16266
|
verified++;
|
|
16182
16267
|
else if (verdict === "VERIFIED (signature not checked)")
|
|
16183
16268
|
unchecked++;
|
|
16269
|
+
else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
|
|
16270
|
+
satisfied++;
|
|
16271
|
+
else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
|
|
16272
|
+
failed++;
|
|
16273
|
+
else
|
|
16274
|
+
other++;
|
|
16184
16275
|
}
|
|
16185
16276
|
}
|
|
16186
16277
|
if (r.verdict === "SPEC_CONFLICT")
|
|
@@ -16210,9 +16301,11 @@ function computeStats(db, since) {
|
|
|
16210
16301
|
runs_running: running,
|
|
16211
16302
|
by_verdict: by,
|
|
16212
16303
|
blocked_waiting: blocked,
|
|
16213
|
-
verified_rate:
|
|
16304
|
+
verified_rate: verified + unchecked + failed ? verified / (verified + unchecked + failed) : null,
|
|
16305
|
+
breakdown: { verified, failed, already_satisfied: satisfied, other },
|
|
16214
16306
|
verified_unchecked: unchecked,
|
|
16215
16307
|
keys_configured: pubkeysFromEnv().configured === true,
|
|
16308
|
+
...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
|
|
16216
16309
|
cost: {
|
|
16217
16310
|
measured_usd: mRuns ? r62(mUsd) : null,
|
|
16218
16311
|
measured_runs: mRuns,
|
|
@@ -16355,13 +16448,13 @@ data: ${JSON.stringify({ source: k.slice(0, i), run: k.slice(i + 1) })}
|
|
|
16355
16448
|
}
|
|
16356
16449
|
|
|
16357
16450
|
// src/server/routes/verify.ts
|
|
16358
|
-
import { readFileSync as readFileSync25, realpathSync as
|
|
16451
|
+
import { readFileSync as readFileSync25, realpathSync as realpathSync12 } from "fs";
|
|
16359
16452
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
16360
|
-
import { join as
|
|
16453
|
+
import { join as join29, sep as sep10 } from "path";
|
|
16361
16454
|
|
|
16362
16455
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
16363
16456
|
import { existsSync as existsSync17, readdirSync as readdirSync12, readFileSync as readFileSync24, statSync as statSync12 } from "fs";
|
|
16364
|
-
import { dirname as dirname12, join as
|
|
16457
|
+
import { dirname as dirname12, join as join28 } from "path";
|
|
16365
16458
|
import { createHash as createHash10, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
|
|
16366
16459
|
|
|
16367
16460
|
// ../../loki-ts/src/util/paths.ts
|
|
@@ -16387,14 +16480,14 @@ var REPO_ROOT = findRepoRoot();
|
|
|
16387
16480
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
16388
16481
|
import { createHash as createHash9, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
|
|
16389
16482
|
import { chmodSync as chmodSync4, existsSync as existsSync16, readdirSync as readdirSync11, linkSync, mkdirSync as mkdirSync8, readFileSync as readFileSync22, statSync as statSync11, unlinkSync as unlinkSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
16390
|
-
import { homedir as
|
|
16391
|
-
import { basename as basename9, dirname as dirname11, join as
|
|
16483
|
+
import { homedir as homedir6 } from "os";
|
|
16484
|
+
import { basename as basename9, dirname as dirname11, join as join27 } from "path";
|
|
16392
16485
|
|
|
16393
16486
|
// ../../loki-ts/src/e10ext/assert_delta.ts
|
|
16394
16487
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
16395
16488
|
import { mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
|
|
16396
|
-
import { tmpdir } from "os";
|
|
16397
|
-
import { join as
|
|
16489
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
16490
|
+
import { join as join21 } from "path";
|
|
16398
16491
|
import { basename as basename6 } from "path";
|
|
16399
16492
|
var WEAKENED = { verdict: "weakened", changes: [] };
|
|
16400
16493
|
var PY = String.raw`
|
|
@@ -16553,7 +16646,7 @@ function classifyAssertDelta(inp) {
|
|
|
16553
16646
|
return WEAKENED;
|
|
16554
16647
|
let cwd = "";
|
|
16555
16648
|
try {
|
|
16556
|
-
cwd = mkdtempSync(
|
|
16649
|
+
cwd = mkdtempSync(join21(tmpdir2(), "loki-ad-"));
|
|
16557
16650
|
const raw2 = execFileSync3("python3", ["-I", "-c", PY], {
|
|
16558
16651
|
input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
|
|
16559
16652
|
cwd,
|
|
@@ -16584,7 +16677,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
|
|
|
16584
16677
|
return null;
|
|
16585
16678
|
try {
|
|
16586
16679
|
const show = (ref) => execFileSync3("git", ["show", `${ref}:${path}`], { cwd: repoDir, encoding: "utf8", env: process.env, stdio: ["ignore", "pipe", "ignore"] });
|
|
16587
|
-
const head = headRef ? show(headRef) : readFileSync17(
|
|
16680
|
+
const head = headRef ? show(headRef) : readFileSync17(join21(repoDir, path), "utf8");
|
|
16588
16681
|
const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
|
|
16589
16682
|
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;
|
|
16590
16683
|
} catch {
|
|
@@ -16601,15 +16694,15 @@ function classifyTestEdit(file, oldText, newText, taskText, baseCounts, headCoun
|
|
|
16601
16694
|
|
|
16602
16695
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
16603
16696
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
16604
|
-
import { chmodSync as chmodSync3, closeSync as closeSync4, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync18, realpathSync as
|
|
16605
|
-
import { dirname as dirname5, join as
|
|
16697
|
+
import { chmodSync as chmodSync3, closeSync as closeSync4, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync18, realpathSync as realpathSync10, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
16698
|
+
import { dirname as dirname5, join as join22, relative, sep as sep7 } from "path";
|
|
16606
16699
|
function safeRestore(repoDir, f, want, mode) {
|
|
16607
|
-
const root =
|
|
16700
|
+
const root = realpathSync10(repoDir), fp = join22(root, f), rel = relative(root, dirname5(fp));
|
|
16608
16701
|
if (rel.startsWith(".."))
|
|
16609
16702
|
throw new Error("outside repo");
|
|
16610
16703
|
let cur = root;
|
|
16611
|
-
for (const part of rel === "" ? [] : rel.split(
|
|
16612
|
-
cur =
|
|
16704
|
+
for (const part of rel === "" ? [] : rel.split(sep7)) {
|
|
16705
|
+
cur = join22(cur, part);
|
|
16613
16706
|
let st2;
|
|
16614
16707
|
try {
|
|
16615
16708
|
st2 = lstatSync3(cur);
|
|
@@ -16658,7 +16751,7 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
|
|
|
16658
16751
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
16659
16752
|
const notProven = [];
|
|
16660
16753
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
16661
|
-
const fp =
|
|
16754
|
+
const fp = join22(repoDir, f);
|
|
16662
16755
|
try {
|
|
16663
16756
|
if (v.startsWith("!")) {
|
|
16664
16757
|
const st = lstatSync3(fp);
|
|
@@ -16677,7 +16770,7 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
|
|
|
16677
16770
|
try {
|
|
16678
16771
|
let m = 420;
|
|
16679
16772
|
try {
|
|
16680
|
-
const l = lstatSync3(
|
|
16773
|
+
const l = lstatSync3(join22(repoDir, f));
|
|
16681
16774
|
if (l.isFile())
|
|
16682
16775
|
m = l.mode & 511;
|
|
16683
16776
|
} catch {}
|
|
@@ -16717,7 +16810,7 @@ function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
|
|
|
16717
16810
|
import { basename as basename8, dirname as dirname7 } from "path";
|
|
16718
16811
|
|
|
16719
16812
|
// ../../loki-ts/src/engine10/testmap.ts
|
|
16720
|
-
import { basename as basename7, extname, join as
|
|
16813
|
+
import { basename as basename7, extname, join as join23, relative as relative2, sep as sep8 } from "path";
|
|
16721
16814
|
var SKIP_DIRS = new Set([
|
|
16722
16815
|
".git",
|
|
16723
16816
|
"node_modules",
|
|
@@ -16742,7 +16835,7 @@ function isTestFile(rel) {
|
|
|
16742
16835
|
}
|
|
16743
16836
|
|
|
16744
16837
|
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
16745
|
-
import { closeSync as closeSync5, constants, fstatSync as fstatSync3, lstatSync as lstatSync4, openSync as openSync5, readSync as readSync3, realpathSync as
|
|
16838
|
+
import { closeSync as closeSync5, constants, fstatSync as fstatSync3, lstatSync as lstatSync4, openSync as openSync5, readSync as readSync3, realpathSync as realpathSync11 } from "fs";
|
|
16746
16839
|
var MAX_SPEC_BYTES = 1024 * 1024;
|
|
16747
16840
|
var MAX_TOKEN_BUDGET = 1e9;
|
|
16748
16841
|
var norm2 = (p) => p.replace(/^\.\//, "");
|
|
@@ -16914,7 +17007,7 @@ import { spawn as spawn5 } from "child_process";
|
|
|
16914
17007
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
|
|
16915
17008
|
import { existsSync as existsSync13, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync7 } from "fs";
|
|
16916
17009
|
import { createServer } from "net";
|
|
16917
|
-
import { dirname as dirname8, isAbsolute, join as
|
|
17010
|
+
import { dirname as dirname8, isAbsolute, join as join24, normalize, relative as relative3, sep as sep9 } from "path";
|
|
16918
17011
|
var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0";
|
|
16919
17012
|
var PAGE_RE = /^(?:.*\/)?(?:app|pages|src|public)\/.*\.(?:html|jsx|tsx|vue|svelte)$/;
|
|
16920
17013
|
var isPageFile = (p) => PAGE_RE.test(p);
|
|
@@ -16929,7 +17022,7 @@ function hashScreens(runDir, rels) {
|
|
|
16929
17022
|
const out2 = [];
|
|
16930
17023
|
for (const rel of rels) {
|
|
16931
17024
|
try {
|
|
16932
|
-
out2.push({ path: rel, sha256: sha256File(
|
|
17025
|
+
out2.push({ path: rel, sha256: sha256File(join24(runDir, rel)) });
|
|
16933
17026
|
} catch {}
|
|
16934
17027
|
}
|
|
16935
17028
|
return out2;
|
|
@@ -16942,10 +17035,10 @@ function checkScreens(runDir, screens) {
|
|
|
16942
17035
|
return "evidence_screens entry is malformed";
|
|
16943
17036
|
if (isAbsolute(s.path) || normalize(s.path).startsWith(".."))
|
|
16944
17037
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
16945
|
-
const file =
|
|
17038
|
+
const file = join24(runDir, s.path);
|
|
16946
17039
|
let cur = runDir;
|
|
16947
|
-
for (const seg of normalize(s.path).split(
|
|
16948
|
-
cur =
|
|
17040
|
+
for (const seg of normalize(s.path).split(sep9)) {
|
|
17041
|
+
cur = join24(cur, seg);
|
|
16949
17042
|
if (isSymlink(cur))
|
|
16950
17043
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
16951
17044
|
}
|
|
@@ -16991,12 +17084,12 @@ async function waitUp(url, ms, signal) {
|
|
|
16991
17084
|
return false;
|
|
16992
17085
|
}
|
|
16993
17086
|
var playwrightBin = (repoDir) => {
|
|
16994
|
-
const bin =
|
|
17087
|
+
const bin = join24(repoDir, "node_modules", ".bin", "playwright");
|
|
16995
17088
|
return existsSync13(bin) ? bin : null;
|
|
16996
17089
|
};
|
|
16997
17090
|
function openapiPaths(repoDir) {
|
|
16998
17091
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
16999
|
-
const p =
|
|
17092
|
+
const p = join24(repoDir, f);
|
|
17000
17093
|
if (!existsSync13(p))
|
|
17001
17094
|
continue;
|
|
17002
17095
|
try {
|
|
@@ -17010,8 +17103,8 @@ function openapiPaths(repoDir) {
|
|
|
17010
17103
|
return [];
|
|
17011
17104
|
}
|
|
17012
17105
|
var playwrightTestPkg = (repoDir) => {
|
|
17013
|
-
const d =
|
|
17014
|
-
return existsSync13(
|
|
17106
|
+
const d = join24(repoDir, "node_modules", "@playwright", "test");
|
|
17107
|
+
return existsSync13(join24(d, "package.json")) ? d : null;
|
|
17015
17108
|
};
|
|
17016
17109
|
var e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
17017
17110
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
@@ -17023,24 +17116,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
17023
17116
|
if (!pw || !pkg)
|
|
17024
17117
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
17025
17118
|
try {
|
|
17026
|
-
const dir =
|
|
17119
|
+
const dir = join24(runDir, relDir, "e2e");
|
|
17027
17120
|
mkdirSync6(dir, { recursive: true });
|
|
17028
|
-
writeFileSync7(
|
|
17029
|
-
writeFileSync7(
|
|
17121
|
+
writeFileSync7(join24(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
17122
|
+
writeFileSync7(join24(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
17030
17123
|
`);
|
|
17031
|
-
if (!await shoot(pw, ["test", "--config",
|
|
17124
|
+
if (!await shoot(pw, ["test", "--config", join24(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
17032
17125
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
17033
17126
|
const rels = [];
|
|
17034
17127
|
const walk = (d) => {
|
|
17035
17128
|
for (const e of readdirSync9(d, { withFileTypes: true })) {
|
|
17036
|
-
const full =
|
|
17129
|
+
const full = join24(d, e.name);
|
|
17037
17130
|
if (e.isDirectory())
|
|
17038
17131
|
walk(full);
|
|
17039
17132
|
else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
|
|
17040
|
-
rels.push(
|
|
17133
|
+
rels.push(join24(relDir, "e2e", relative3(dir, full)));
|
|
17041
17134
|
}
|
|
17042
17135
|
};
|
|
17043
|
-
walk(
|
|
17136
|
+
walk(join24(dir, "out"));
|
|
17044
17137
|
return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
|
|
17045
17138
|
} catch (e) {
|
|
17046
17139
|
return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
|
|
@@ -17133,7 +17226,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
17133
17226
|
try {
|
|
17134
17227
|
if (!visualEvidenceEnabled(opts.env))
|
|
17135
17228
|
return skip("LOKI_VISUAL_EVIDENCE is 0");
|
|
17136
|
-
const pkgPath =
|
|
17229
|
+
const pkgPath = join24(repoDir, "package.json");
|
|
17137
17230
|
if (!existsSync13(pkgPath))
|
|
17138
17231
|
return skip("no package.json");
|
|
17139
17232
|
const script = pickScript(JSON.parse(readFileSync19(pkgPath, "utf8")));
|
|
@@ -17161,20 +17254,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
17161
17254
|
}
|
|
17162
17255
|
if (!await waitUp(base, left(), signal))
|
|
17163
17256
|
return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
|
|
17164
|
-
const evRoot =
|
|
17257
|
+
const evRoot = join24(runDir, "evidence");
|
|
17165
17258
|
if (isSymlink(evRoot))
|
|
17166
17259
|
return skip("evidence dir is a symlink");
|
|
17167
17260
|
mkdirSync6(evRoot, { recursive: true });
|
|
17168
17261
|
if (pages.length > 0) {
|
|
17169
|
-
const rel0 =
|
|
17170
|
-
mkdirSync6(
|
|
17262
|
+
const rel0 = join24("evidence", randomBytes2(6).toString("hex"));
|
|
17263
|
+
mkdirSync6(join24(runDir, rel0));
|
|
17171
17264
|
const rels = [];
|
|
17172
17265
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
17173
17266
|
if (aborted() || left() <= 0)
|
|
17174
17267
|
break;
|
|
17175
|
-
const rel =
|
|
17176
|
-
const ok = await shoot(pw, ["screenshot", `${base}${route}`,
|
|
17177
|
-
if (ok && !aborted() && !isSymlink(
|
|
17268
|
+
const rel = join24(rel0, `${screenName(route)}.png`);
|
|
17269
|
+
const ok = await shoot(pw, ["screenshot", `${base}${route}`, join24(runDir, rel)], repoDir, left(), signal);
|
|
17270
|
+
if (ok && !aborted() && !isSymlink(join24(runDir, rel)) && existsSync13(join24(runDir, rel)))
|
|
17178
17271
|
rels.push(rel);
|
|
17179
17272
|
}
|
|
17180
17273
|
if (aborted())
|
|
@@ -17199,9 +17292,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
17199
17292
|
}
|
|
17200
17293
|
if (aborted())
|
|
17201
17294
|
return skip("aborted");
|
|
17202
|
-
const hdir =
|
|
17295
|
+
const hdir = join24(evRoot, randomBytes2(6).toString("hex"));
|
|
17203
17296
|
mkdirSync6(hdir);
|
|
17204
|
-
writeFileSync7(
|
|
17297
|
+
writeFileSync7(join24(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
17205
17298
|
`);
|
|
17206
17299
|
return { screens: [], http: true, skipped: null };
|
|
17207
17300
|
} catch (e) {
|
|
@@ -17243,7 +17336,7 @@ var STAGE_BUDGETS = {
|
|
|
17243
17336
|
|
|
17244
17337
|
// ../../loki-ts/src/features/contract.ts
|
|
17245
17338
|
import { existsSync as existsSync14, lstatSync as lstatSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "fs";
|
|
17246
|
-
import { dirname as dirname9, join as
|
|
17339
|
+
import { dirname as dirname9, join as join25 } from "path";
|
|
17247
17340
|
import { createHash as createHash7 } from "crypto";
|
|
17248
17341
|
var MAX_CRITERIA = 50;
|
|
17249
17342
|
var MAX_TEXT = 500;
|
|
@@ -17278,7 +17371,7 @@ function untracedLines(trace) {
|
|
|
17278
17371
|
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)}`);
|
|
17279
17372
|
}
|
|
17280
17373
|
function contractPath(repoDir) {
|
|
17281
|
-
return
|
|
17374
|
+
return join25(repoDir, ".loki", "contract.json");
|
|
17282
17375
|
}
|
|
17283
17376
|
var MAX_CONTRACT_BYTES = 1024 * 1024;
|
|
17284
17377
|
function readContract(repoDir) {
|
|
@@ -17360,14 +17453,14 @@ function sealContract(repoDir, body, rawDiff, checks, env = process.env, snap) {
|
|
|
17360
17453
|
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
17361
17454
|
import { createHash as createHash8 } from "crypto";
|
|
17362
17455
|
import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync21, statSync as statSync10 } from "fs";
|
|
17363
|
-
import { dirname as dirname10, join as
|
|
17456
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
17364
17457
|
var ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
17365
17458
|
var PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
17366
17459
|
var sha2 = (b) => createHash8("sha256").update(b).digest("hex");
|
|
17367
17460
|
var isObj9 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
17368
17461
|
var text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "";
|
|
17369
17462
|
var num2 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
17370
|
-
var unitsDir = (runDir) =>
|
|
17463
|
+
var unitsDir = (runDir) => join26(runDir, "group", "units");
|
|
17371
17464
|
var readJson3 = (p) => {
|
|
17372
17465
|
try {
|
|
17373
17466
|
return JSON.parse(readFileSync21(p, "utf8"));
|
|
@@ -17383,7 +17476,7 @@ var dirNames = (d) => {
|
|
|
17383
17476
|
}
|
|
17384
17477
|
};
|
|
17385
17478
|
function sealGroup(runDir, rh) {
|
|
17386
|
-
const manifestPath =
|
|
17479
|
+
const manifestPath = join26(runDir, "group", "manifest.json");
|
|
17387
17480
|
if (!existsSync15(manifestPath))
|
|
17388
17481
|
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
17389
17482
|
const notProven = [];
|
|
@@ -17408,7 +17501,7 @@ function sealGroup(runDir, rh) {
|
|
|
17408
17501
|
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
17409
17502
|
seen.add(id);
|
|
17410
17503
|
seenLower.add(id.toLowerCase());
|
|
17411
|
-
const dir =
|
|
17504
|
+
const dir = join26(unitsDir(runDir), id), rp = join26(dir, "receipt.json"), ep = join26(dir, "events.jsonl");
|
|
17412
17505
|
let bytes;
|
|
17413
17506
|
try {
|
|
17414
17507
|
bytes = readFileSync21(rp);
|
|
@@ -17453,7 +17546,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
17453
17546
|
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
17454
17547
|
const g = receipt["group"];
|
|
17455
17548
|
if (g === undefined)
|
|
17456
|
-
return existsSync15(
|
|
17549
|
+
return existsSync15(join26(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
17457
17550
|
if (!isObj9(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
17458
17551
|
return bad("group section malformed");
|
|
17459
17552
|
const entries = g["units"];
|
|
@@ -17465,7 +17558,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
|
17465
17558
|
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
17466
17559
|
let unchecked = null;
|
|
17467
17560
|
for (let i = 0;i < entries.length; i++) {
|
|
17468
|
-
const e = entries[i], id = ids[i], dir =
|
|
17561
|
+
const e = entries[i], id = ids[i], dir = join26(unitsDir(runDir), id), rp = join26(dir, "receipt.json"), ep = join26(dir, "events.jsonl");
|
|
17469
17562
|
if (e["index"] !== i)
|
|
17470
17563
|
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
17471
17564
|
let bytes;
|
|
@@ -17533,7 +17626,7 @@ function loadSigningKey(generate = true) {
|
|
|
17533
17626
|
let pem = inline ?? "";
|
|
17534
17627
|
if (!inline) {
|
|
17535
17628
|
const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
17536
|
-
const file = given ||
|
|
17629
|
+
const file = given || join27(process.env["HOME"] || homedir6(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
|
|
17537
17630
|
try {
|
|
17538
17631
|
pem = readFileSync22(file);
|
|
17539
17632
|
if (!given) {
|
|
@@ -17683,7 +17776,7 @@ var sealStage = {
|
|
|
17683
17776
|
if (wallNotRun > 0) {
|
|
17684
17777
|
try {
|
|
17685
17778
|
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename9(String(f.path))));
|
|
17686
|
-
for (const n2 of readdirSync11(
|
|
17779
|
+
for (const n2 of readdirSync11(join27(ctx.runDir, "wall")).sort())
|
|
17687
17780
|
if (!kept.has(n2))
|
|
17688
17781
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
17689
17782
|
} catch {}
|
|
@@ -17743,7 +17836,7 @@ var sealStage = {
|
|
|
17743
17836
|
const cost = ctx.cost.read(ctx.repoDir, iterIds);
|
|
17744
17837
|
if (cost.unmetered)
|
|
17745
17838
|
notProven.add("cost unmetered (CLI invoker; recorded as 0)");
|
|
17746
|
-
const eventsPath =
|
|
17839
|
+
const eventsPath = join27(ctx.runDir, "events.jsonl");
|
|
17747
17840
|
const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
|
|
17748
17841
|
const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
|
|
17749
17842
|
if (wallFiles.length > 0 && wallPassed === null)
|
|
@@ -17794,10 +17887,10 @@ var sealStage = {
|
|
|
17794
17887
|
}
|
|
17795
17888
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
17796
17889
|
mkdirSync8(ctx.runDir, { recursive: true });
|
|
17797
|
-
const path =
|
|
17890
|
+
const path = join27(ctx.runDir, "receipt.json");
|
|
17798
17891
|
writeFileSync9(path, JSON.stringify(receipt, null, 2) + `
|
|
17799
17892
|
`);
|
|
17800
|
-
writeFileSync9(
|
|
17893
|
+
writeFileSync9(join27(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
17801
17894
|
const signed = sig.jwt !== null;
|
|
17802
17895
|
const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven };
|
|
17803
17896
|
ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven });
|
|
@@ -17934,7 +18027,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
17934
18027
|
const bound = receipt["events_sha256"];
|
|
17935
18028
|
if (typeof bound !== "string" || bound === createHash10("sha256").digest("hex"))
|
|
17936
18029
|
return null;
|
|
17937
|
-
const path =
|
|
18030
|
+
const path = join28(dirname12(receiptPath), "events.jsonl");
|
|
17938
18031
|
if (!existsSync17(path))
|
|
17939
18032
|
return "events.jsonl is missing";
|
|
17940
18033
|
const raw2 = readFileSync24(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
|
|
@@ -17953,7 +18046,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
17953
18046
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
17954
18047
|
}
|
|
17955
18048
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
17956
|
-
const path =
|
|
18049
|
+
const path = join28(dirname12(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
17957
18050
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
17958
18051
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
17959
18052
|
if (done < 0)
|
|
@@ -18008,6 +18101,9 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
18008
18101
|
return { verdict: "UNCHECKED", reasons: [outcome.reason ?? "attestation not checked"] };
|
|
18009
18102
|
if (outcome.status === "tampered")
|
|
18010
18103
|
return { verdict: "TAMPERED", reasons: [outcome.reason ?? "attestation invalid"] };
|
|
18104
|
+
const shownKid = verification.kid, signedKid = JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid;
|
|
18105
|
+
if (shownKid !== undefined && shownKid !== signedKid)
|
|
18106
|
+
return { verdict: "TAMPERED", reasons: ["verification.kid differs from the signing kid in the attestation header (metadata outside the signed digest was edited)"] };
|
|
18011
18107
|
const seal = receipt["log_seal"] !== true ? null : checkLogSeal(receiptPath, computed, JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid, deps.pubkey);
|
|
18012
18108
|
if (seal)
|
|
18013
18109
|
return { verdict: seal.verdict, reasons: [seal.reason] };
|
|
@@ -18040,15 +18136,15 @@ function mount19(ctx) {
|
|
|
18040
18136
|
return done(404, "receipt not available", "not_found");
|
|
18041
18137
|
let runDir, file;
|
|
18042
18138
|
try {
|
|
18043
|
-
const root =
|
|
18044
|
-
runDir =
|
|
18045
|
-
if (runDir !==
|
|
18139
|
+
const root = realpathSync12(join29(repo.p, ".loki", "runs"));
|
|
18140
|
+
runDir = realpathSync12(join29(root, run2));
|
|
18141
|
+
if (runDir !== join29(root, run2))
|
|
18046
18142
|
return done(404, "receipt not found", "not_found");
|
|
18047
|
-
file =
|
|
18143
|
+
file = realpathSync12(join29(runDir, "receipt.json"));
|
|
18048
18144
|
} catch {
|
|
18049
18145
|
return done(404, "receipt not found", "not_found");
|
|
18050
18146
|
}
|
|
18051
|
-
if (!file.startsWith(runDir +
|
|
18147
|
+
if (!file.startsWith(runDir + sep10))
|
|
18052
18148
|
return done(404, "receipt not found", "not_found");
|
|
18053
18149
|
let integrity, reasons, sha3 = null, outcome = "UNREADABLE";
|
|
18054
18150
|
try {
|
|
@@ -18126,7 +18222,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now = Date.now) {
|
|
|
18126
18222
|
|
|
18127
18223
|
// src/server/legacy/shim.ts
|
|
18128
18224
|
import { existsSync as existsSync18, readFileSync as readFileSync26 } from "fs";
|
|
18129
|
-
import { join as
|
|
18225
|
+
import { join as join30 } from "path";
|
|
18130
18226
|
|
|
18131
18227
|
// src/server/legacy/routes.ts
|
|
18132
18228
|
var LEGACY_ROUTES = [
|
|
@@ -18498,7 +18594,7 @@ var logged = new Set;
|
|
|
18498
18594
|
var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
|
|
18499
18595
|
var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
18500
18596
|
var repoVersion = () => {
|
|
18501
|
-
for (const f of [
|
|
18597
|
+
for (const f of [join30(import.meta.dir, "../../../../../VERSION")]) {
|
|
18502
18598
|
try {
|
|
18503
18599
|
if (existsSync18(f))
|
|
18504
18600
|
return readFileSync26(f, "utf8").trim();
|
|
@@ -18515,8 +18611,8 @@ var intParam = (v, def, min, max) => {
|
|
|
18515
18611
|
return n2 >= min && n2 <= max ? n2 : null;
|
|
18516
18612
|
};
|
|
18517
18613
|
function queryAudit(db, q) {
|
|
18518
|
-
const where = and(q.action ? eq(
|
|
18519
|
-
return db.select().from(
|
|
18614
|
+
const where = and(q.action ? eq(audit2.action, q.action) : undefined, q.start_date ? gte(audit2.ts, q.start_date) : undefined, q.end_date ? lt(audit2.ts, `${q.end_date}\uFFFF`) : undefined);
|
|
18615
|
+
return db.select().from(audit2).where(where).orderBy(desc(audit2.id)).limit(q.limit).offset(q.offset).all().map((r) => ({ id: r.id, timestamp: r.ts, action: r.action, actor: r.actor, details: r.detail }));
|
|
18520
18616
|
}
|
|
18521
18617
|
function legacyShim(opts) {
|
|
18522
18618
|
const { db } = opts;
|
|
@@ -18683,10 +18779,11 @@ function legacyShim(opts) {
|
|
|
18683
18779
|
|
|
18684
18780
|
// src/server/app.ts
|
|
18685
18781
|
var MAX_BODY2 = 1e6;
|
|
18686
|
-
var defaultUiDir = () => [
|
|
18782
|
+
var defaultUiDir = () => [join31(import.meta.dir, "../../ui/dist"), join31(import.meta.dir, "../ui/dist")].find((d) => existsSync19(join31(d, "index.html")));
|
|
18687
18783
|
function createApp(opts) {
|
|
18688
18784
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
18689
18785
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
18786
|
+
cleanupLeakedFixtures(sqlite);
|
|
18690
18787
|
recomputeLegacy(db);
|
|
18691
18788
|
let ready = true;
|
|
18692
18789
|
const answerDir = opts.answerDir ?? defaultAnswerDir();
|
|
@@ -18808,11 +18905,11 @@ function createApp(opts) {
|
|
|
18808
18905
|
return c.json({ error: "not found" }, 404);
|
|
18809
18906
|
const root = resolve6(uiDir);
|
|
18810
18907
|
const f = resolve6(root, `.${p}`);
|
|
18811
|
-
if (f.startsWith(root +
|
|
18908
|
+
if (f.startsWith(root + sep11) && existsSync19(f) && statSync13(f).isFile())
|
|
18812
18909
|
return new Response(Bun.file(f));
|
|
18813
18910
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
18814
18911
|
return c.json({ error: "not found" }, 404);
|
|
18815
|
-
return new Response(Bun.file(
|
|
18912
|
+
return new Response(Bun.file(join31(root, "index.html")));
|
|
18816
18913
|
});
|
|
18817
18914
|
return { app, db, close: () => sqlite.close() };
|
|
18818
18915
|
}
|