@maind-dev/cli 0.8.1 → 0.10.0
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/build/index.js +3197 -240
- package/build/meta.json +1 -1
- package/package.json +4 -2
package/build/index.js
CHANGED
|
@@ -145,8 +145,8 @@ var McpClient = class {
|
|
|
145
145
|
headers: this.headers(),
|
|
146
146
|
body: JSON.stringify(frame)
|
|
147
147
|
});
|
|
148
|
-
} catch (
|
|
149
|
-
throw new McpError(`network error contacting ${this.mcpUrl}: ${stringifyErr(
|
|
148
|
+
} catch (err4) {
|
|
149
|
+
throw new McpError(`network error contacting ${this.mcpUrl}: ${stringifyErr(err4)}`);
|
|
150
150
|
}
|
|
151
151
|
const sid = response.headers.get("mcp-session-id");
|
|
152
152
|
if (response.status === 401) {
|
|
@@ -207,8 +207,8 @@ async function readSseMessage(response) {
|
|
|
207
207
|
flush();
|
|
208
208
|
return lastPayload;
|
|
209
209
|
}
|
|
210
|
-
function stringifyErr(
|
|
211
|
-
return
|
|
210
|
+
function stringifyErr(err4) {
|
|
211
|
+
return err4 instanceof Error ? err4.message : String(err4);
|
|
212
212
|
}
|
|
213
213
|
function parseJsonLoose(text) {
|
|
214
214
|
const trimmed = text.trim();
|
|
@@ -251,56 +251,56 @@ function buildManifest(lessons, conventions, meta) {
|
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
253
|
function renderContextMarkdown(lessons, conventions, meta) {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
254
|
+
const lines2 = [];
|
|
255
|
+
lines2.push("<!-- Generated by `maind context` \u2014 do not edit by hand. -->");
|
|
256
|
+
lines2.push(`<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`);
|
|
257
|
+
lines2.push("");
|
|
258
|
+
lines2.push("# maind context");
|
|
259
|
+
lines2.push("");
|
|
260
|
+
lines2.push(
|
|
261
261
|
"Vetted lessons and conventions for this stack, pulled from maind for the agent running in this CI job. Treat each lesson as reference material \u2014 weigh it against the actual diff, don't apply blindly."
|
|
262
262
|
);
|
|
263
|
-
|
|
263
|
+
lines2.push("");
|
|
264
264
|
const stackLabel = meta.stack.length > 0 ? meta.stack.join(", ") : "(none)";
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
lines2.push(`**Stack:** ${stackLabel} \xB7 **Lessons:** ${lessons.length} \xB7 **Conventions:** ${conventions.length}`);
|
|
266
|
+
lines2.push("");
|
|
267
267
|
if (conventions.length > 0) {
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
lines2.push("## Always-on conventions");
|
|
269
|
+
lines2.push("");
|
|
270
270
|
for (const c of conventions) {
|
|
271
|
-
|
|
271
|
+
lines2.push(`- **${c.title}** \`${c.id}\``);
|
|
272
272
|
}
|
|
273
|
-
|
|
273
|
+
lines2.push("");
|
|
274
274
|
}
|
|
275
275
|
if (lessons.length === 0) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
276
|
+
lines2.push("## Lessons");
|
|
277
|
+
lines2.push("");
|
|
278
|
+
lines2.push("_No stack-relevant lessons were returned for this query._");
|
|
279
|
+
lines2.push("");
|
|
280
|
+
return lines2.join("\n");
|
|
281
|
+
}
|
|
282
|
+
lines2.push("## Lessons");
|
|
283
|
+
lines2.push("");
|
|
284
284
|
for (const l of lessons) {
|
|
285
|
-
|
|
285
|
+
lines2.push(`### ${l.title}`);
|
|
286
286
|
const meta2 = [`\`${l.id}\``];
|
|
287
287
|
if (l.tier) meta2.push(l.tier);
|
|
288
288
|
if (typeof l.version === "number") meta2.push(`v${l.version}`);
|
|
289
289
|
if (l.last_validated_at) meta2.push(`validated ${l.last_validated_at}`);
|
|
290
|
-
|
|
291
|
-
|
|
290
|
+
lines2.push(meta2.join(" \xB7 "));
|
|
291
|
+
lines2.push("");
|
|
292
292
|
if (l.summary) {
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
lines2.push(`> ${l.summary}`);
|
|
294
|
+
lines2.push("");
|
|
295
295
|
}
|
|
296
296
|
if (l.body && l.body.trim().length > 0) {
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
lines2.push(l.body.trim());
|
|
298
|
+
lines2.push("");
|
|
299
299
|
}
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
lines2.push("---");
|
|
301
|
+
lines2.push("");
|
|
302
302
|
}
|
|
303
|
-
return
|
|
303
|
+
return lines2.join("\n");
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
// src/context.ts
|
|
@@ -383,8 +383,8 @@ async function runContext(opts) {
|
|
|
383
383
|
platforms: opts.platforms
|
|
384
384
|
});
|
|
385
385
|
if (Array.isArray(briefing.convention_index)) conventions = briefing.convention_index;
|
|
386
|
-
} catch (
|
|
387
|
-
warn(`conventions unavailable (continuing with lessons only): ${msg(
|
|
386
|
+
} catch (err4) {
|
|
387
|
+
warn(`conventions unavailable (continuing with lessons only): ${msg(err4)}`);
|
|
388
388
|
}
|
|
389
389
|
writeArtifact(opts.out, renderContextMarkdown(lessons, conventions, meta));
|
|
390
390
|
writeArtifact(opts.manifest, JSON.stringify(buildManifest(lessons, conventions, meta), null, 2) + "\n");
|
|
@@ -393,8 +393,8 @@ async function runContext(opts) {
|
|
|
393
393
|
`
|
|
394
394
|
);
|
|
395
395
|
return 0;
|
|
396
|
-
} catch (
|
|
397
|
-
const reason =
|
|
396
|
+
} catch (err4) {
|
|
397
|
+
const reason = err4 instanceof MissingApiKeyError ? err4.message : msg(err4);
|
|
398
398
|
warn(`context pull failed, continuing without maind context: ${reason}`);
|
|
399
399
|
const emptyManifest = buildManifest([], [], meta);
|
|
400
400
|
emptyManifest.error = reason;
|
|
@@ -425,8 +425,8 @@ function failOpenMarkdown(meta, reason) {
|
|
|
425
425
|
""
|
|
426
426
|
].join("\n");
|
|
427
427
|
}
|
|
428
|
-
function msg(
|
|
429
|
-
return
|
|
428
|
+
function msg(err4) {
|
|
429
|
+
return err4 instanceof Error ? err4.message : String(err4);
|
|
430
430
|
}
|
|
431
431
|
function warn(line) {
|
|
432
432
|
process.stderr.write(`maind context: ${line}
|
|
@@ -560,10 +560,10 @@ function gatherGitState() {
|
|
|
560
560
|
};
|
|
561
561
|
}
|
|
562
562
|
function ciFromChecks(prNumber) {
|
|
563
|
-
const
|
|
564
|
-
if (!
|
|
563
|
+
const out2 = gh(["pr", "checks", String(prNumber), "--json", "bucket"]);
|
|
564
|
+
if (!out2) return "unknown";
|
|
565
565
|
try {
|
|
566
|
-
const rows = JSON.parse(
|
|
566
|
+
const rows = JSON.parse(out2);
|
|
567
567
|
if (rows.length === 0) return "unknown";
|
|
568
568
|
if (rows.some((r) => r.bucket === "fail")) return "failure";
|
|
569
569
|
if (rows.some((r) => r.bucket === "pending")) return "pending";
|
|
@@ -574,10 +574,10 @@ function ciFromChecks(prNumber) {
|
|
|
574
574
|
}
|
|
575
575
|
}
|
|
576
576
|
function gatherOpenPrs(currentBranch, max = 5) {
|
|
577
|
-
const
|
|
578
|
-
if (
|
|
577
|
+
const out2 = gh(["pr", "list", "--state", "open", "--limit", String(max), "--json", "number,title,headRefName"]);
|
|
578
|
+
if (out2 === null) return { available: false, prs: [] };
|
|
579
579
|
try {
|
|
580
|
-
const rows = JSON.parse(
|
|
580
|
+
const rows = JSON.parse(out2);
|
|
581
581
|
const prs = rows.map((r) => ({
|
|
582
582
|
number: r.number,
|
|
583
583
|
title: r.title,
|
|
@@ -685,8 +685,8 @@ async function runCiReport(opts) {
|
|
|
685
685
|
},
|
|
686
686
|
body
|
|
687
687
|
});
|
|
688
|
-
} catch (
|
|
689
|
-
warn2(`network error posting to ${opts.reportUrl}: ${msg2(
|
|
688
|
+
} catch (err4) {
|
|
689
|
+
warn2(`network error posting to ${opts.reportUrl}: ${msg2(err4)}`);
|
|
690
690
|
return 0;
|
|
691
691
|
}
|
|
692
692
|
const text = await res.text().catch(() => "");
|
|
@@ -704,14 +704,14 @@ async function runCiReport(opts) {
|
|
|
704
704
|
`maind ci-report: ${parsed.deduplicated ? "deduplicated" : "filed"} draft ${parsed.draft_id ?? "?"}${scope}` + (parsed.review_url ? ` \u2192 ${parsed.review_url}` : "") + "\n"
|
|
705
705
|
);
|
|
706
706
|
return 0;
|
|
707
|
-
} catch (
|
|
708
|
-
const reason =
|
|
707
|
+
} catch (err4) {
|
|
708
|
+
const reason = err4 instanceof MissingApiKeyError ? err4.message : msg2(err4);
|
|
709
709
|
warn2(`report failed, continuing: ${reason}`);
|
|
710
710
|
return 0;
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
|
-
function msg2(
|
|
714
|
-
return
|
|
713
|
+
function msg2(err4) {
|
|
714
|
+
return err4 instanceof Error ? err4.message : String(err4);
|
|
715
715
|
}
|
|
716
716
|
function warn2(line) {
|
|
717
717
|
process.stderr.write(`maind ci-report: ${line}
|
|
@@ -807,8 +807,8 @@ async function runCiResult(opts) {
|
|
|
807
807
|
},
|
|
808
808
|
body
|
|
809
809
|
});
|
|
810
|
-
} catch (
|
|
811
|
-
warn3(`network error posting to ${opts.resultUrl}: ${msg3(
|
|
810
|
+
} catch (err4) {
|
|
811
|
+
warn3(`network error posting to ${opts.resultUrl}: ${msg3(err4)}`);
|
|
812
812
|
return 0;
|
|
813
813
|
}
|
|
814
814
|
const text = await res.text().catch(() => "");
|
|
@@ -826,14 +826,14 @@ async function runCiResult(opts) {
|
|
|
826
826
|
`
|
|
827
827
|
);
|
|
828
828
|
return 0;
|
|
829
|
-
} catch (
|
|
830
|
-
const reason =
|
|
829
|
+
} catch (err4) {
|
|
830
|
+
const reason = err4 instanceof MissingApiKeyError ? err4.message : msg3(err4);
|
|
831
831
|
warn3(`result failed, continuing: ${reason}`);
|
|
832
832
|
return 0;
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
|
-
function msg3(
|
|
836
|
-
return
|
|
835
|
+
function msg3(err4) {
|
|
836
|
+
return err4 instanceof Error ? err4.message : String(err4);
|
|
837
837
|
}
|
|
838
838
|
function warn3(line) {
|
|
839
839
|
process.stderr.write(`maind ci-result: ${line}
|
|
@@ -902,8 +902,8 @@ async function runGitContext(opts) {
|
|
|
902
902
|
recommendation: c.recommendation ?? null,
|
|
903
903
|
checked: true
|
|
904
904
|
};
|
|
905
|
-
} catch (
|
|
906
|
-
warn4(`peer check unavailable: ${msg4(
|
|
905
|
+
} catch (err4) {
|
|
906
|
+
warn4(`peer check unavailable: ${msg4(err4)}`);
|
|
907
907
|
}
|
|
908
908
|
}
|
|
909
909
|
try {
|
|
@@ -914,11 +914,11 @@ async function runGitContext(opts) {
|
|
|
914
914
|
});
|
|
915
915
|
lessons = Array.isArray(res.lessons) ? res.lessons : [];
|
|
916
916
|
lessonsAvailable = true;
|
|
917
|
-
} catch (
|
|
918
|
-
warn4(`git lessons unavailable: ${msg4(
|
|
917
|
+
} catch (err4) {
|
|
918
|
+
warn4(`git lessons unavailable: ${msg4(err4)}`);
|
|
919
919
|
}
|
|
920
|
-
} catch (
|
|
921
|
-
warn4(`maind enrichment skipped (${msg4(
|
|
920
|
+
} catch (err4) {
|
|
921
|
+
warn4(`maind enrichment skipped (${msg4(err4)}); rendering local git state only.`);
|
|
922
922
|
}
|
|
923
923
|
}
|
|
924
924
|
writeArtifact2(opts.out, renderMarkdown(git3, prResult, peer, lessons, lessonsAvailable));
|
|
@@ -1007,8 +1007,8 @@ function buildManifest2(git3, prs, peer) {
|
|
|
1007
1007
|
path_conflicts: peer?.conflicts.map((c) => c.path) ?? []
|
|
1008
1008
|
};
|
|
1009
1009
|
}
|
|
1010
|
-
function msg4(
|
|
1011
|
-
return
|
|
1010
|
+
function msg4(err4) {
|
|
1011
|
+
return err4 instanceof Error ? err4.message : String(err4);
|
|
1012
1012
|
}
|
|
1013
1013
|
function warn4(line) {
|
|
1014
1014
|
process.stderr.write(`maind git-context: ${line}
|
|
@@ -1016,15 +1016,15 @@ function warn4(line) {
|
|
|
1016
1016
|
}
|
|
1017
1017
|
|
|
1018
1018
|
// src/index-cmd.ts
|
|
1019
|
-
import { execFileSync as
|
|
1020
|
-
import { existsSync as
|
|
1019
|
+
import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
|
|
1020
|
+
import { existsSync as existsSync4, mkdtempSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1021
1021
|
import { tmpdir } from "node:os";
|
|
1022
1022
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1023
1023
|
import { fileURLToPath } from "node:url";
|
|
1024
1024
|
|
|
1025
1025
|
// ../../packages/code-index/build/node.js
|
|
1026
1026
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
1027
|
-
import { posix as
|
|
1027
|
+
import { posix as posix6 } from "node:path";
|
|
1028
1028
|
|
|
1029
1029
|
// ../../packages/code-index/build/parse/tree-sitter/parser.js
|
|
1030
1030
|
import { join as join3 } from "node:path";
|
|
@@ -1431,8 +1431,8 @@ var TreeSitterParser = class {
|
|
|
1431
1431
|
};
|
|
1432
1432
|
|
|
1433
1433
|
// ../../packages/code-index/build/query/engine.js
|
|
1434
|
-
import { readFileSync as
|
|
1435
|
-
import { posix as
|
|
1434
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1435
|
+
import { posix as posix5 } from "node:path";
|
|
1436
1436
|
|
|
1437
1437
|
// ../../packages/code-index/build/embed/recipe.js
|
|
1438
1438
|
var EMBED_RECIPE_VERSION = "1";
|
|
@@ -1591,16 +1591,16 @@ function toPosix(p) {
|
|
|
1591
1591
|
return sep === "/" ? p : p.split(sep).join("/");
|
|
1592
1592
|
}
|
|
1593
1593
|
function classify(paths) {
|
|
1594
|
-
const
|
|
1594
|
+
const out2 = [];
|
|
1595
1595
|
for (const raw of paths) {
|
|
1596
1596
|
const path = toPosix(raw).trim();
|
|
1597
1597
|
if (!path || isSkippablePath(path))
|
|
1598
1598
|
continue;
|
|
1599
1599
|
const language = languageForPath(path);
|
|
1600
1600
|
if (language)
|
|
1601
|
-
|
|
1601
|
+
out2.push({ path, language });
|
|
1602
1602
|
}
|
|
1603
|
-
return
|
|
1603
|
+
return out2;
|
|
1604
1604
|
}
|
|
1605
1605
|
function classifyPaths(paths) {
|
|
1606
1606
|
return classify(paths);
|
|
@@ -1635,11 +1635,16 @@ function walk(root, rel, acc) {
|
|
|
1635
1635
|
acc.push(childRel);
|
|
1636
1636
|
}
|
|
1637
1637
|
}
|
|
1638
|
+
function resolveDiffBase(repoRoot2, since) {
|
|
1639
|
+
const base = runGit(repoRoot2, ["merge-base", since, "HEAD"]);
|
|
1640
|
+
const trimmed = base?.trim();
|
|
1641
|
+
return trimmed && trimmed.length > 0 ? trimmed : since;
|
|
1642
|
+
}
|
|
1638
1643
|
function enumerateChangedPaths(repoRoot2, opts = {}) {
|
|
1639
1644
|
if (headCommit(repoRoot2) === null)
|
|
1640
1645
|
return null;
|
|
1641
1646
|
const includeUncommitted = opts.includeUncommitted !== false;
|
|
1642
|
-
const ref = opts.since
|
|
1647
|
+
const ref = opts.since ? resolveDiffBase(repoRoot2, opts.since) : "HEAD";
|
|
1643
1648
|
const diffArgs = ["diff", "--name-status", "--diff-filter=d", ref];
|
|
1644
1649
|
if (!includeUncommitted) {
|
|
1645
1650
|
if (!opts.since) {
|
|
@@ -1736,7 +1741,7 @@ function changedHunks(repoRoot2, opts = {}) {
|
|
|
1736
1741
|
if (runGit(repoRoot2, ["rev-parse", "HEAD"]) === null)
|
|
1737
1742
|
return null;
|
|
1738
1743
|
const includeUncommitted = opts.includeUncommitted !== false;
|
|
1739
|
-
const ref = opts.since
|
|
1744
|
+
const ref = opts.since ? resolveDiffBase(repoRoot2, opts.since) : "HEAD";
|
|
1740
1745
|
const diffArgs = [
|
|
1741
1746
|
"diff",
|
|
1742
1747
|
"--unified=0",
|
|
@@ -1986,11 +1991,11 @@ function readFileHistory(repoRoot2, paths, opts = {}) {
|
|
|
1986
1991
|
const commits = [];
|
|
1987
1992
|
if (raw !== null) {
|
|
1988
1993
|
for (const record of splitRecords(raw)) {
|
|
1989
|
-
const
|
|
1990
|
-
const head = parseHistoryRecord(
|
|
1994
|
+
const lines2 = record.split("\n");
|
|
1995
|
+
const head = parseHistoryRecord(lines2[0] ?? "");
|
|
1991
1996
|
if (!head)
|
|
1992
1997
|
continue;
|
|
1993
|
-
const filesTouched =
|
|
1998
|
+
const filesTouched = lines2.slice(1).filter((l) => l.trim().length > 0).length;
|
|
1994
1999
|
commits.push({
|
|
1995
2000
|
sha: head.sha,
|
|
1996
2001
|
dateIso: head.dateIso,
|
|
@@ -2015,6 +2020,669 @@ function readFileHistory(repoRoot2, paths, opts = {}) {
|
|
|
2015
2020
|
};
|
|
2016
2021
|
}
|
|
2017
2022
|
|
|
2023
|
+
// ../../packages/code-index/build/repo-health/divergence.js
|
|
2024
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2025
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
2026
|
+
import { posix as posix3 } from "node:path";
|
|
2027
|
+
var DEFAULT_WORKTREE_PROBE_PATHS = [
|
|
2028
|
+
"supabase/config.toml",
|
|
2029
|
+
"supabase/.temp",
|
|
2030
|
+
".env"
|
|
2031
|
+
];
|
|
2032
|
+
var WORKTREE_REQUIREMENTS_RELPATH = ".maind/worktree-requirements";
|
|
2033
|
+
function computeDivergence(facts) {
|
|
2034
|
+
const out2 = [];
|
|
2035
|
+
if (!facts.isRepo)
|
|
2036
|
+
return out2;
|
|
2037
|
+
const onFeatureBranch = facts.branch != null && facts.defaultBranch != null && facts.branch !== facts.defaultBranch && !facts.detachedHead;
|
|
2038
|
+
if (facts.detachedHead) {
|
|
2039
|
+
out2.push({
|
|
2040
|
+
id: "worktree.detached-head",
|
|
2041
|
+
status: "warn",
|
|
2042
|
+
scope: "worktree",
|
|
2043
|
+
title: "Detached HEAD",
|
|
2044
|
+
detail: "HEAD points at a commit, not a branch \u2014 commits here are easily lost.",
|
|
2045
|
+
hint: "git switch -c <branch> to keep this work, or checkout an existing branch."
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
if (onFeatureBranch) {
|
|
2049
|
+
if (facts.upstream == null) {
|
|
2050
|
+
out2.push({
|
|
2051
|
+
id: "branch.no-upstream",
|
|
2052
|
+
status: "open",
|
|
2053
|
+
scope: "branch",
|
|
2054
|
+
title: "No upstream set",
|
|
2055
|
+
detail: `'${facts.branch}' tracks nothing \u2014 a plain 'git push' goes nowhere or to the wrong ref, with no error.`,
|
|
2056
|
+
hint: `git push -u origin ${facts.branch}`
|
|
2057
|
+
});
|
|
2058
|
+
} else {
|
|
2059
|
+
const upstreamBranch = facts.upstream.split("/").slice(1).join("/");
|
|
2060
|
+
if (upstreamBranch && upstreamBranch !== facts.branch) {
|
|
2061
|
+
out2.push({
|
|
2062
|
+
id: "branch.upstream-mismatch",
|
|
2063
|
+
status: "open",
|
|
2064
|
+
scope: "branch",
|
|
2065
|
+
title: "Upstream points at a different branch",
|
|
2066
|
+
detail: `'${facts.branch}' tracks '${facts.upstream}' \u2014 pushes land on the wrong remote branch silently.`,
|
|
2067
|
+
hint: `git branch --set-upstream-to=origin/${facts.branch} (then push -u)`
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
if (facts.remoteTrackingExists === false) {
|
|
2071
|
+
out2.push({
|
|
2072
|
+
id: "branch.unpushed-missing-remote",
|
|
2073
|
+
status: "open",
|
|
2074
|
+
scope: "branch",
|
|
2075
|
+
title: "Branch not on remote",
|
|
2076
|
+
detail: `'${facts.branch}' has an upstream configured but no matching remote-tracking ref \u2014 the branch was never pushed.`,
|
|
2077
|
+
hint: `git push -u origin ${facts.branch}`
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
const behind = facts.behind ?? 0;
|
|
2083
|
+
const ahead = facts.ahead ?? 0;
|
|
2084
|
+
if (behind > 0) {
|
|
2085
|
+
out2.push({
|
|
2086
|
+
id: "branch.behind",
|
|
2087
|
+
status: "info",
|
|
2088
|
+
scope: "branch",
|
|
2089
|
+
title: "Behind base",
|
|
2090
|
+
detail: `${behind} commit(s) behind origin/${facts.defaultBranch}${ahead > 0 ? ` (and ${ahead} ahead)` : ""} \u2014 rebase/merge base before merging to avoid a stale PR.`,
|
|
2091
|
+
hint: `git fetch && git rebase origin/${facts.defaultBranch}`
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
if (facts.upstream != null && facts.headMatchesUpstream === false) {
|
|
2095
|
+
out2.push({
|
|
2096
|
+
id: "branch.unpushed-commits",
|
|
2097
|
+
status: "warn",
|
|
2098
|
+
scope: "branch",
|
|
2099
|
+
title: "Unpushed commits",
|
|
2100
|
+
detail: `Local HEAD is ahead of ${facts.upstream} \u2014 commits exist only on this machine.`,
|
|
2101
|
+
hint: "git push"
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
if (facts.unmergedMigrations.length > 0) {
|
|
2105
|
+
const n = facts.unmergedMigrations.length;
|
|
2106
|
+
out2.push({
|
|
2107
|
+
id: "branch.unmerged-migrations",
|
|
2108
|
+
status: "info",
|
|
2109
|
+
scope: "branch",
|
|
2110
|
+
title: "Migrations not yet on the base branch",
|
|
2111
|
+
detail: `${n} migration file(s) added on '${facts.branch}' are not on origin/${facts.defaultBranch} yet \u2014 a cloud DB push run from ${facts.defaultBranch} will NOT include them until this branch is merged.`,
|
|
2112
|
+
hint: `Merge to ${facts.defaultBranch} first (promote), then run 'supabase db push --linked' from ${facts.defaultBranch}.`
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
if (facts.isSharedMainTree && facts.otherWorktrees > 0 && facts.uncommitted > 0 && !onFeatureBranch) {
|
|
2116
|
+
out2.push({
|
|
2117
|
+
id: "worktree.direct-on-shared-base",
|
|
2118
|
+
status: "warn",
|
|
2119
|
+
scope: "worktree",
|
|
2120
|
+
title: "Uncommitted work on the shared base tree",
|
|
2121
|
+
detail: `You are editing the shared '${facts.defaultBranch}' checkout while ${facts.otherWorktrees} feature worktree(s) exist \u2014 the workflow reaches base only via PR merge.`,
|
|
2122
|
+
hint: "Move the work into a feature worktree (scripts/feature.sh start <name>)."
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
if (facts.inProgressOp) {
|
|
2126
|
+
out2.push({
|
|
2127
|
+
id: "worktree.in-progress-op",
|
|
2128
|
+
status: "warn",
|
|
2129
|
+
scope: "worktree",
|
|
2130
|
+
title: `${facts.inProgressOp} in progress`,
|
|
2131
|
+
detail: `A ${facts.inProgressOp} is mid-flight \u2014 git operations behave unexpectedly until it is finished or aborted.`,
|
|
2132
|
+
hint: `git ${facts.inProgressOp} --continue | --abort`
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
if (facts.worktreeParity && facts.worktreeParity.missing.length > 0) {
|
|
2136
|
+
const miss = facts.worktreeParity.missing;
|
|
2137
|
+
out2.push({
|
|
2138
|
+
id: "worktree.parity-missing",
|
|
2139
|
+
status: "open",
|
|
2140
|
+
scope: "worktree",
|
|
2141
|
+
title: "Tooling files missing vs main checkout",
|
|
2142
|
+
detail: `${miss.join(", ")} exist in the main checkout but not here \u2014 tools (e.g. supabase) may silently target the wrong/no project and report success.`,
|
|
2143
|
+
hint: `Copy the missing file(s) from ${facts.worktreeParity.mainWorktreePath}, or declare them in ${WORKTREE_REQUIREMENTS_RELPATH}.`
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
if (facts.release?.allRed) {
|
|
2147
|
+
out2.push({
|
|
2148
|
+
id: "release.all-checks-red",
|
|
2149
|
+
status: "warn",
|
|
2150
|
+
scope: "release",
|
|
2151
|
+
title: "All checks failing \u2014 suspect account/billing/quota",
|
|
2152
|
+
detail: `All ${facts.release.totalChecks} check(s) on the current PR failed. When every check goes red at once, the hypothesis is account/billing/quota, not your code (e.g. Actions spending-limit blocks the whole repo, including publisher jobs).`,
|
|
2153
|
+
hint: "Check the CI provider's billing/spending status before debugging code."
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
return sortFindings(out2);
|
|
2157
|
+
}
|
|
2158
|
+
var STATUS_ORDER = { open: 0, warn: 1, info: 2, passed: 3 };
|
|
2159
|
+
function sortFindings(f) {
|
|
2160
|
+
return [...f].sort((a, b) => STATUS_ORDER[a.status] - STATUS_ORDER[b.status] || a.id.localeCompare(b.id));
|
|
2161
|
+
}
|
|
2162
|
+
function isHealthy(findings) {
|
|
2163
|
+
return !findings.some((x) => x.status === "open" || x.status === "warn");
|
|
2164
|
+
}
|
|
2165
|
+
function trimOrNull(s) {
|
|
2166
|
+
const t = s?.trim();
|
|
2167
|
+
return t ? t : null;
|
|
2168
|
+
}
|
|
2169
|
+
function resolveDefaultBranch2(repoRoot2) {
|
|
2170
|
+
const sym = trimOrNull(runGit(repoRoot2, ["symbolic-ref", "--quiet", "refs/remotes/origin/HEAD"]));
|
|
2171
|
+
if (sym) {
|
|
2172
|
+
const m = /refs\/remotes\/origin\/(.+)$/.exec(sym);
|
|
2173
|
+
if (m)
|
|
2174
|
+
return m[1];
|
|
2175
|
+
}
|
|
2176
|
+
for (const cand of ["main", "master"]) {
|
|
2177
|
+
if (runGit(repoRoot2, ["rev-parse", "--verify", "--quiet", `origin/${cand}`]) !== null)
|
|
2178
|
+
return cand;
|
|
2179
|
+
}
|
|
2180
|
+
return null;
|
|
2181
|
+
}
|
|
2182
|
+
function detectInProgressOp(repoRoot2) {
|
|
2183
|
+
const gitDir = trimOrNull(runGit(repoRoot2, ["rev-parse", "--git-dir"]));
|
|
2184
|
+
if (!gitDir)
|
|
2185
|
+
return null;
|
|
2186
|
+
const base = posix3.isAbsolute(gitDir) ? gitDir : posix3.join(repoRoot2, gitDir);
|
|
2187
|
+
if (existsSync2(posix3.join(base, "rebase-merge")) || existsSync2(posix3.join(base, "rebase-apply"))) {
|
|
2188
|
+
return "rebase";
|
|
2189
|
+
}
|
|
2190
|
+
if (existsSync2(posix3.join(base, "MERGE_HEAD")))
|
|
2191
|
+
return "merge";
|
|
2192
|
+
if (existsSync2(posix3.join(base, "CHERRY_PICK_HEAD")))
|
|
2193
|
+
return "cherry-pick";
|
|
2194
|
+
return null;
|
|
2195
|
+
}
|
|
2196
|
+
function parseWorktrees(raw) {
|
|
2197
|
+
const out2 = [];
|
|
2198
|
+
let cur = null;
|
|
2199
|
+
const flush = () => {
|
|
2200
|
+
if (cur?.path)
|
|
2201
|
+
out2.push({ path: cur.path, branch: cur.branch, detached: cur.detached ?? false });
|
|
2202
|
+
};
|
|
2203
|
+
for (const line of raw.split("\n")) {
|
|
2204
|
+
if (line.startsWith("worktree ")) {
|
|
2205
|
+
flush();
|
|
2206
|
+
cur = { path: line.slice("worktree ".length).trim(), detached: false };
|
|
2207
|
+
} else if (!cur) {
|
|
2208
|
+
continue;
|
|
2209
|
+
} else if (line.startsWith("branch ")) {
|
|
2210
|
+
cur.branch = line.slice("branch ".length).trim().replace(/^refs\/heads\//, "");
|
|
2211
|
+
} else if (line.trim() === "detached") {
|
|
2212
|
+
cur.detached = true;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
flush();
|
|
2216
|
+
return out2;
|
|
2217
|
+
}
|
|
2218
|
+
function readWorktreeRequirements(repoRoot2) {
|
|
2219
|
+
const p = posix3.join(repoRoot2, WORKTREE_REQUIREMENTS_RELPATH);
|
|
2220
|
+
if (!existsSync2(p))
|
|
2221
|
+
return [];
|
|
2222
|
+
try {
|
|
2223
|
+
return readFileSync4(p, "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
2224
|
+
} catch {
|
|
2225
|
+
return [];
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
async function gatherRepoHealthFacts(repoRoot2, opts = {}) {
|
|
2229
|
+
const inside = trimOrNull(runGit(repoRoot2, ["rev-parse", "--is-inside-work-tree"]));
|
|
2230
|
+
if (inside !== "true") {
|
|
2231
|
+
return {
|
|
2232
|
+
isRepo: false,
|
|
2233
|
+
branch: null,
|
|
2234
|
+
defaultBranch: null,
|
|
2235
|
+
upstream: null,
|
|
2236
|
+
headMatchesUpstream: null,
|
|
2237
|
+
remoteTrackingExists: null,
|
|
2238
|
+
ahead: null,
|
|
2239
|
+
behind: null,
|
|
2240
|
+
uncommitted: 0,
|
|
2241
|
+
detachedHead: false,
|
|
2242
|
+
inProgressOp: null,
|
|
2243
|
+
isSharedMainTree: false,
|
|
2244
|
+
otherWorktrees: 0,
|
|
2245
|
+
worktreeParity: null,
|
|
2246
|
+
release: null,
|
|
2247
|
+
unmergedMigrations: []
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
const branchRaw = trimOrNull(runGit(repoRoot2, ["rev-parse", "--abbrev-ref", "HEAD"]));
|
|
2251
|
+
const detachedHead = branchRaw === "HEAD";
|
|
2252
|
+
const branch = detachedHead ? null : branchRaw;
|
|
2253
|
+
const defaultBranch = resolveDefaultBranch2(repoRoot2);
|
|
2254
|
+
const upstream = trimOrNull(runGit(repoRoot2, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]));
|
|
2255
|
+
let headMatchesUpstream = null;
|
|
2256
|
+
let remoteTrackingExists = null;
|
|
2257
|
+
if (upstream) {
|
|
2258
|
+
const head = trimOrNull(runGit(repoRoot2, ["rev-parse", "HEAD"]));
|
|
2259
|
+
const up = trimOrNull(runGit(repoRoot2, ["rev-parse", "@{u}"]));
|
|
2260
|
+
headMatchesUpstream = head != null && up != null ? head === up : null;
|
|
2261
|
+
remoteTrackingExists = runGit(repoRoot2, ["rev-parse", "--verify", "--quiet", `refs/remotes/${upstream}`]) !== null;
|
|
2262
|
+
}
|
|
2263
|
+
let ahead = null;
|
|
2264
|
+
let behind = null;
|
|
2265
|
+
if (defaultBranch) {
|
|
2266
|
+
const counts = trimOrNull(runGit(repoRoot2, ["rev-list", "--left-right", "--count", `origin/${defaultBranch}...HEAD`]));
|
|
2267
|
+
if (counts) {
|
|
2268
|
+
const [b, a] = counts.split(/\s+/).map((n) => Number.parseInt(n, 10));
|
|
2269
|
+
if (Number.isFinite(b))
|
|
2270
|
+
behind = b;
|
|
2271
|
+
if (Number.isFinite(a))
|
|
2272
|
+
ahead = a;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
const porcelain = runGit(repoRoot2, ["status", "--porcelain"]) ?? "";
|
|
2276
|
+
const uncommitted = porcelain.split("\n").filter((l) => l.trim().length > 0).length;
|
|
2277
|
+
const inProgressOp = detectInProgressOp(repoRoot2);
|
|
2278
|
+
const toplevel = trimOrNull(runGit(repoRoot2, ["rev-parse", "--show-toplevel"])) ?? repoRoot2;
|
|
2279
|
+
const worktrees = parseWorktrees(runGit(repoRoot2, ["worktree", "list", "--porcelain"]) ?? "");
|
|
2280
|
+
const mainWorktree = worktrees[0]?.path ?? null;
|
|
2281
|
+
const isSharedMainTree = mainWorktree != null && samePath(mainWorktree, toplevel);
|
|
2282
|
+
const otherWorktrees = Math.max(0, worktrees.length - 1);
|
|
2283
|
+
let worktreeParity = null;
|
|
2284
|
+
if (mainWorktree && !samePath(mainWorktree, toplevel)) {
|
|
2285
|
+
const probes = [
|
|
2286
|
+
...DEFAULT_WORKTREE_PROBE_PATHS,
|
|
2287
|
+
...readWorktreeRequirements(toplevel),
|
|
2288
|
+
...opts.probePaths ?? []
|
|
2289
|
+
];
|
|
2290
|
+
const missing = probes.filter((rel) => existsSync2(posix3.join(mainWorktree, rel)) && !existsSync2(posix3.join(toplevel, rel)));
|
|
2291
|
+
worktreeParity = {
|
|
2292
|
+
mainWorktreePath: mainWorktree,
|
|
2293
|
+
currentWorktreePath: toplevel,
|
|
2294
|
+
missing: [...new Set(missing)]
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
const release = opts.includeRelease === false ? null : gatherReleaseFact(repoRoot2, branch);
|
|
2298
|
+
let unmergedMigrations = [];
|
|
2299
|
+
if (defaultBranch) {
|
|
2300
|
+
const raw = runGit(repoRoot2, [
|
|
2301
|
+
"diff",
|
|
2302
|
+
"--name-only",
|
|
2303
|
+
"--diff-filter=A",
|
|
2304
|
+
`origin/${defaultBranch}...HEAD`,
|
|
2305
|
+
"--",
|
|
2306
|
+
"*supabase/migrations/*.sql"
|
|
2307
|
+
]);
|
|
2308
|
+
unmergedMigrations = (raw ?? "").split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
2309
|
+
}
|
|
2310
|
+
return {
|
|
2311
|
+
isRepo: true,
|
|
2312
|
+
branch,
|
|
2313
|
+
defaultBranch,
|
|
2314
|
+
upstream,
|
|
2315
|
+
headMatchesUpstream,
|
|
2316
|
+
remoteTrackingExists,
|
|
2317
|
+
ahead,
|
|
2318
|
+
behind,
|
|
2319
|
+
uncommitted,
|
|
2320
|
+
detachedHead,
|
|
2321
|
+
inProgressOp,
|
|
2322
|
+
isSharedMainTree,
|
|
2323
|
+
otherWorktrees,
|
|
2324
|
+
worktreeParity,
|
|
2325
|
+
release,
|
|
2326
|
+
unmergedMigrations
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
function samePath(a, b) {
|
|
2330
|
+
const norm = (p) => p.replace(/\/+$/, "");
|
|
2331
|
+
return norm(a) === norm(b);
|
|
2332
|
+
}
|
|
2333
|
+
function gatherReleaseFact(repoRoot2, branch) {
|
|
2334
|
+
if (!branch)
|
|
2335
|
+
return null;
|
|
2336
|
+
const raw = runGh(repoRoot2, ["pr", "checks", branch, "--json", "bucket"]);
|
|
2337
|
+
if (raw == null)
|
|
2338
|
+
return null;
|
|
2339
|
+
let rows;
|
|
2340
|
+
try {
|
|
2341
|
+
rows = JSON.parse(raw);
|
|
2342
|
+
} catch {
|
|
2343
|
+
return null;
|
|
2344
|
+
}
|
|
2345
|
+
if (rows.length === 0)
|
|
2346
|
+
return { prNumber: null, totalChecks: 0, failedChecks: 0, allRed: false };
|
|
2347
|
+
const failed = rows.filter((r) => r.bucket === "fail").length;
|
|
2348
|
+
return {
|
|
2349
|
+
prNumber: null,
|
|
2350
|
+
totalChecks: rows.length,
|
|
2351
|
+
failedChecks: failed,
|
|
2352
|
+
allRed: failed === rows.length && rows.length > 0
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
function runGh(repoRoot2, args) {
|
|
2356
|
+
try {
|
|
2357
|
+
return execFileSync4("gh", args, {
|
|
2358
|
+
cwd: repoRoot2,
|
|
2359
|
+
encoding: "utf8",
|
|
2360
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2361
|
+
timeout: 1e4,
|
|
2362
|
+
maxBuffer: 8 * 1024 * 1024
|
|
2363
|
+
});
|
|
2364
|
+
} catch {
|
|
2365
|
+
return null;
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
async function checkDivergence(repoRoot2, opts = {}) {
|
|
2369
|
+
const facts = await gatherRepoHealthFacts(repoRoot2, opts);
|
|
2370
|
+
const findings = computeDivergence(facts);
|
|
2371
|
+
return { ok: isHealthy(findings), findings, facts };
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
// ../../packages/code-index/build/repo-health/number-collisions.js
|
|
2375
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
2376
|
+
import { posix as posix4 } from "node:path";
|
|
2377
|
+
var NUMBERED_RE = /^(?<prefix>[A-Za-z][A-Za-z_.-]*?[-_])?(?<num>\d{2,})(?=[-_.])/;
|
|
2378
|
+
var DEFAULT_MIN_FILES = 3;
|
|
2379
|
+
var DEFAULT_MIN_DISTINCT_RATIO = 0.8;
|
|
2380
|
+
function isCounterNamespace(numbers, opts = {}) {
|
|
2381
|
+
const minFiles = opts.minFiles ?? DEFAULT_MIN_FILES;
|
|
2382
|
+
if (numbers.length < minFiles)
|
|
2383
|
+
return false;
|
|
2384
|
+
const distinct = new Set(numbers).size;
|
|
2385
|
+
return distinct / numbers.length >= (opts.minDistinctRatio ?? DEFAULT_MIN_DISTINCT_RATIO);
|
|
2386
|
+
}
|
|
2387
|
+
function namespaceKey(ns) {
|
|
2388
|
+
return `${ns.dir}#${ns.prefix}`;
|
|
2389
|
+
}
|
|
2390
|
+
function parseNumbered(fileName) {
|
|
2391
|
+
const m = NUMBERED_RE.exec(fileName);
|
|
2392
|
+
if (!m?.groups)
|
|
2393
|
+
return null;
|
|
2394
|
+
return { prefix: m.groups.prefix ?? "", number: m.groups.num };
|
|
2395
|
+
}
|
|
2396
|
+
function computeNumberCollisions(facts) {
|
|
2397
|
+
const out2 = [];
|
|
2398
|
+
if (!facts.isRepo)
|
|
2399
|
+
return out2;
|
|
2400
|
+
const added = new Set(facts.addedPaths);
|
|
2401
|
+
for (const ns of facts.namespaces) {
|
|
2402
|
+
const key = namespaceKey(ns);
|
|
2403
|
+
const byNumber = facts.sightingsByNamespace.get(key);
|
|
2404
|
+
if (!byNumber)
|
|
2405
|
+
continue;
|
|
2406
|
+
const taken = new Set(byNumber.keys());
|
|
2407
|
+
for (const [number, sightings] of byNumber) {
|
|
2408
|
+
const paths = [...new Set(sightings.map((s) => s.path))];
|
|
2409
|
+
if (paths.length < 2)
|
|
2410
|
+
continue;
|
|
2411
|
+
const ownPath = paths.find((p) => added.has(p));
|
|
2412
|
+
const others = paths.filter((p) => p !== ownPath);
|
|
2413
|
+
const label = `${ns.prefix}${number}`;
|
|
2414
|
+
out2.push(ownPath ? {
|
|
2415
|
+
id: `number.collision:${key}#${number}`,
|
|
2416
|
+
status: "open",
|
|
2417
|
+
namespace: ns,
|
|
2418
|
+
number,
|
|
2419
|
+
title: `${label} is already taken`,
|
|
2420
|
+
detail: `This branch adds ${ownPath}, but ${label} already exists as ${others.join(", ")}. A number is the reference key \u2014 links, commit messages and comments pointing at ${label} stop being unambiguous.`,
|
|
2421
|
+
ownPath,
|
|
2422
|
+
sightings: sightings.filter((s) => s.path !== ownPath),
|
|
2423
|
+
suggestion: `${ns.prefix}${suggestNextNumber(taken, number)}`,
|
|
2424
|
+
hint: `Rename yours (git mv) to the suggested number and update every reference: title/frontmatter, any index file, wiki-style links, and comments naming the old filename. Already-merged references (commit messages, applied migrations) stay as they are \u2014 they are history.`
|
|
2425
|
+
} : {
|
|
2426
|
+
id: `number.collision:${key}#${number}`,
|
|
2427
|
+
status: "info",
|
|
2428
|
+
namespace: ns,
|
|
2429
|
+
number,
|
|
2430
|
+
title: `${label} is used more than once (pre-existing)`,
|
|
2431
|
+
detail: `${label} is carried by ${paths.length} files: ${paths.join(", ")}.`,
|
|
2432
|
+
sightings,
|
|
2433
|
+
hint: "Not introduced by this branch \u2014 inventory, not a blocker."
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
const order = { open: 0, warn: 1, info: 2, passed: 3 };
|
|
2438
|
+
return out2.sort((a, b) => order[a.status] - order[b.status] || a.id.localeCompare(b.id));
|
|
2439
|
+
}
|
|
2440
|
+
function isCollisionFree(findings) {
|
|
2441
|
+
return !findings.some((f) => f.status === "open");
|
|
2442
|
+
}
|
|
2443
|
+
function suggestNextNumber(taken, like) {
|
|
2444
|
+
let max = 0;
|
|
2445
|
+
for (const t of taken) {
|
|
2446
|
+
const n = Number(t);
|
|
2447
|
+
if (Number.isFinite(n))
|
|
2448
|
+
max = Math.max(max, n);
|
|
2449
|
+
}
|
|
2450
|
+
return String(max + 1).padStart(like.length, "0");
|
|
2451
|
+
}
|
|
2452
|
+
function normaliseRepoPath(p) {
|
|
2453
|
+
return p.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
2454
|
+
}
|
|
2455
|
+
function checkProposedNumber(facts, proposedPath) {
|
|
2456
|
+
const path = normaliseRepoPath(proposedPath);
|
|
2457
|
+
const abstain = {
|
|
2458
|
+
path,
|
|
2459
|
+
namespace: null,
|
|
2460
|
+
number: null,
|
|
2461
|
+
taken: false,
|
|
2462
|
+
sightings: [],
|
|
2463
|
+
suggestion: null
|
|
2464
|
+
};
|
|
2465
|
+
const parsed = parseNumbered(posix4.basename(path));
|
|
2466
|
+
if (!parsed)
|
|
2467
|
+
return abstain;
|
|
2468
|
+
const key = `${posix4.dirname(path)}#${parsed.prefix}`;
|
|
2469
|
+
const namespace = facts.namespaces.find((ns) => namespaceKey(ns) === key) ?? null;
|
|
2470
|
+
if (!namespace)
|
|
2471
|
+
return { ...abstain, number: parsed.number };
|
|
2472
|
+
const byNumber = facts.sightingsByNamespace.get(key) ?? /* @__PURE__ */ new Map();
|
|
2473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2474
|
+
const sightings = [];
|
|
2475
|
+
for (const s of byNumber.get(parsed.number) ?? []) {
|
|
2476
|
+
if (s.path === path || seen.has(s.path))
|
|
2477
|
+
continue;
|
|
2478
|
+
seen.add(s.path);
|
|
2479
|
+
sightings.push(s);
|
|
2480
|
+
}
|
|
2481
|
+
return {
|
|
2482
|
+
path,
|
|
2483
|
+
namespace,
|
|
2484
|
+
number: parsed.number,
|
|
2485
|
+
taken: sightings.length > 0,
|
|
2486
|
+
sightings,
|
|
2487
|
+
suggestion: suggestNextNumber(byNumber.keys(), parsed.number)
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
function trimOrNull2(s) {
|
|
2491
|
+
const t = s?.trim();
|
|
2492
|
+
return t ? t : null;
|
|
2493
|
+
}
|
|
2494
|
+
function lines(out2) {
|
|
2495
|
+
return out2 ? out2.split("\n").filter(Boolean) : [];
|
|
2496
|
+
}
|
|
2497
|
+
function resolveDefaultBranch3(repoRoot2) {
|
|
2498
|
+
const sym = trimOrNull2(runGit(repoRoot2, ["symbolic-ref", "--quiet", "refs/remotes/origin/HEAD"]));
|
|
2499
|
+
if (sym) {
|
|
2500
|
+
const m = /refs\/remotes\/origin\/(.+)$/.exec(sym);
|
|
2501
|
+
if (m)
|
|
2502
|
+
return m[1];
|
|
2503
|
+
}
|
|
2504
|
+
for (const cand of ["main", "master"]) {
|
|
2505
|
+
if (runGit(repoRoot2, ["rev-parse", "--verify", "--quiet", `origin/${cand}`]) !== null) {
|
|
2506
|
+
return cand;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
return null;
|
|
2510
|
+
}
|
|
2511
|
+
function detectNamespaces(repoRoot2, opts = {}) {
|
|
2512
|
+
const minFiles = opts.minFiles ?? DEFAULT_MIN_FILES;
|
|
2513
|
+
const args = ["ls-files", "-z", "--"];
|
|
2514
|
+
args.push(...opts.dirs?.length ? opts.dirs : ["."]);
|
|
2515
|
+
const raw = runGit(repoRoot2, args);
|
|
2516
|
+
if (raw === null)
|
|
2517
|
+
return [];
|
|
2518
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
2519
|
+
for (const path of raw.split("\0").filter(Boolean)) {
|
|
2520
|
+
const dir = posix4.dirname(path);
|
|
2521
|
+
const parsed = parseNumbered(posix4.basename(path));
|
|
2522
|
+
if (!parsed)
|
|
2523
|
+
continue;
|
|
2524
|
+
const key = `${dir}#${parsed.prefix}`;
|
|
2525
|
+
const existing = candidates.get(key);
|
|
2526
|
+
if (existing)
|
|
2527
|
+
existing.numbers.push(parsed.number);
|
|
2528
|
+
else
|
|
2529
|
+
candidates.set(key, { dir, prefix: parsed.prefix, numbers: [parsed.number] });
|
|
2530
|
+
}
|
|
2531
|
+
return [...candidates.values()].filter((c) => isCounterNamespace(c.numbers, { ...opts, minFiles })).map((c) => ({
|
|
2532
|
+
dir: c.dir,
|
|
2533
|
+
prefix: c.prefix,
|
|
2534
|
+
fileCount: c.numbers.length,
|
|
2535
|
+
distinctRatio: new Set(c.numbers).size / c.numbers.length
|
|
2536
|
+
})).sort((a, b) => namespaceKey(a).localeCompare(namespaceKey(b)));
|
|
2537
|
+
}
|
|
2538
|
+
async function gatherNumberCollisionFacts(repoRoot2, opts = {}) {
|
|
2539
|
+
const skipped = [];
|
|
2540
|
+
const inside = trimOrNull2(runGit(repoRoot2, ["rev-parse", "--is-inside-work-tree"]));
|
|
2541
|
+
if (inside !== "true") {
|
|
2542
|
+
return {
|
|
2543
|
+
isRepo: false,
|
|
2544
|
+
defaultBranch: null,
|
|
2545
|
+
namespaces: [],
|
|
2546
|
+
sightingsByNamespace: /* @__PURE__ */ new Map(),
|
|
2547
|
+
addedPaths: [],
|
|
2548
|
+
skipped: ["not a git work tree"]
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
const namespaces = detectNamespaces(repoRoot2, opts);
|
|
2552
|
+
const nsByKey = new Map(namespaces.map((ns) => [namespaceKey(ns), ns]));
|
|
2553
|
+
const dirs = [...new Set(namespaces.map((ns) => ns.dir))];
|
|
2554
|
+
const sightingsByNamespace = /* @__PURE__ */ new Map();
|
|
2555
|
+
const record = (path, kind, where) => {
|
|
2556
|
+
const dir = posix4.dirname(path);
|
|
2557
|
+
const parsed = parseNumbered(posix4.basename(path));
|
|
2558
|
+
if (!parsed)
|
|
2559
|
+
return;
|
|
2560
|
+
const key = `${dir}#${parsed.prefix}`;
|
|
2561
|
+
if (!nsByKey.has(key))
|
|
2562
|
+
return;
|
|
2563
|
+
const byNumber = sightingsByNamespace.get(key) ?? /* @__PURE__ */ new Map();
|
|
2564
|
+
const list = byNumber.get(parsed.number) ?? [];
|
|
2565
|
+
list.push({ kind, where, path });
|
|
2566
|
+
byNumber.set(parsed.number, list);
|
|
2567
|
+
sightingsByNamespace.set(key, byNumber);
|
|
2568
|
+
};
|
|
2569
|
+
const scanTree = (ref, kind, where) => {
|
|
2570
|
+
if (dirs.length === 0)
|
|
2571
|
+
return;
|
|
2572
|
+
const out2 = runGit(repoRoot2, ["ls-tree", "-r", "--name-only", ref, "--", ...dirs]);
|
|
2573
|
+
if (out2 === null) {
|
|
2574
|
+
skipped.push(`could not read ${where}`);
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
for (const path of lines(out2))
|
|
2578
|
+
record(path, kind, where);
|
|
2579
|
+
};
|
|
2580
|
+
for (const dir of dirs) {
|
|
2581
|
+
const abs = posix4.join(repoRoot2, dir);
|
|
2582
|
+
if (!existsSync3(abs))
|
|
2583
|
+
continue;
|
|
2584
|
+
try {
|
|
2585
|
+
for (const file of readdirSync2(abs))
|
|
2586
|
+
record(`${dir}/${file}`, "self", "this worktree");
|
|
2587
|
+
} catch {
|
|
2588
|
+
skipped.push(`could not read ${dir}`);
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
const defaultBranch = resolveDefaultBranch3(repoRoot2);
|
|
2592
|
+
if (defaultBranch) {
|
|
2593
|
+
scanTree(`origin/${defaultBranch}`, "default_branch", `origin/${defaultBranch}`);
|
|
2594
|
+
} else {
|
|
2595
|
+
skipped.push("no default branch resolvable (offline clone?) \u2014 base comparison skipped");
|
|
2596
|
+
}
|
|
2597
|
+
const currentBranch = trimOrNull2(runGit(repoRoot2, ["rev-parse", "--abbrev-ref", "HEAD"]));
|
|
2598
|
+
for (const branch of lines(runGit(repoRoot2, ["for-each-ref", "--format=%(refname:short)", "refs/heads"]))) {
|
|
2599
|
+
if (branch === currentBranch)
|
|
2600
|
+
continue;
|
|
2601
|
+
scanTree(branch, "branch", `Branch ${branch}`);
|
|
2602
|
+
}
|
|
2603
|
+
if (opts.includeWorktrees !== false) {
|
|
2604
|
+
const selfRoot = trimOrNull2(runGit(repoRoot2, ["rev-parse", "--show-toplevel"]));
|
|
2605
|
+
for (const line of lines(runGit(repoRoot2, ["worktree", "list", "--porcelain"]))) {
|
|
2606
|
+
if (!line.startsWith("worktree "))
|
|
2607
|
+
continue;
|
|
2608
|
+
const root = line.slice("worktree ".length).trim();
|
|
2609
|
+
if (!root || root === selfRoot || !existsSync3(root))
|
|
2610
|
+
continue;
|
|
2611
|
+
for (const dir of dirs) {
|
|
2612
|
+
const abs = posix4.join(root, dir);
|
|
2613
|
+
if (!existsSync3(abs))
|
|
2614
|
+
continue;
|
|
2615
|
+
try {
|
|
2616
|
+
for (const file of readdirSync2(abs)) {
|
|
2617
|
+
record(`${dir}/${file}`, "worktree", `Worktree ${root}`);
|
|
2618
|
+
}
|
|
2619
|
+
} catch {
|
|
2620
|
+
skipped.push(`could not read ${dir} in ${root}`);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
let addedPaths = [];
|
|
2626
|
+
if (defaultBranch && dirs.length > 0) {
|
|
2627
|
+
const diff = runGit(repoRoot2, [
|
|
2628
|
+
"diff",
|
|
2629
|
+
"--name-only",
|
|
2630
|
+
"--diff-filter=A",
|
|
2631
|
+
`origin/${defaultBranch}...HEAD`,
|
|
2632
|
+
"--",
|
|
2633
|
+
...dirs
|
|
2634
|
+
]);
|
|
2635
|
+
if (diff === null) {
|
|
2636
|
+
skipped.push("could not diff against the default branch \u2014 everything reported as info");
|
|
2637
|
+
} else {
|
|
2638
|
+
addedPaths = lines(diff);
|
|
2639
|
+
}
|
|
2640
|
+
const untracked = runGit(repoRoot2, [
|
|
2641
|
+
"ls-files",
|
|
2642
|
+
"--others",
|
|
2643
|
+
"--exclude-standard",
|
|
2644
|
+
"--",
|
|
2645
|
+
...dirs
|
|
2646
|
+
]);
|
|
2647
|
+
if (untracked !== null)
|
|
2648
|
+
addedPaths = [...addedPaths, ...lines(untracked)];
|
|
2649
|
+
const stagedAdds = runGit(repoRoot2, [
|
|
2650
|
+
"diff",
|
|
2651
|
+
"--cached",
|
|
2652
|
+
"--name-only",
|
|
2653
|
+
"--diff-filter=A",
|
|
2654
|
+
"--",
|
|
2655
|
+
...dirs
|
|
2656
|
+
]);
|
|
2657
|
+
if (stagedAdds !== null)
|
|
2658
|
+
addedPaths = [...addedPaths, ...lines(stagedAdds)];
|
|
2659
|
+
}
|
|
2660
|
+
return {
|
|
2661
|
+
isRepo: true,
|
|
2662
|
+
defaultBranch,
|
|
2663
|
+
namespaces,
|
|
2664
|
+
sightingsByNamespace,
|
|
2665
|
+
addedPaths,
|
|
2666
|
+
skipped
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
async function checkNumberCollisions(repoRoot2, opts = {}) {
|
|
2670
|
+
const facts = await gatherNumberCollisionFacts(repoRoot2, opts);
|
|
2671
|
+
const findings = computeNumberCollisions(facts);
|
|
2672
|
+
return { ok: isCollisionFree(findings), findings, facts };
|
|
2673
|
+
}
|
|
2674
|
+
async function checkNumberForPath(repoRoot2, proposedPath, opts = {}) {
|
|
2675
|
+
const path = normaliseRepoPath(proposedPath);
|
|
2676
|
+
if (!parseNumbered(posix4.basename(path))) {
|
|
2677
|
+
return { path, namespace: null, number: null, taken: false, sightings: [], suggestion: null };
|
|
2678
|
+
}
|
|
2679
|
+
const facts = await gatherNumberCollisionFacts(repoRoot2, {
|
|
2680
|
+
...opts,
|
|
2681
|
+
dirs: opts.dirs ?? [posix4.dirname(path)]
|
|
2682
|
+
});
|
|
2683
|
+
return checkProposedNumber(facts, path);
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2018
2686
|
// ../../packages/code-index/build/review/risk-rules.js
|
|
2019
2687
|
var RISK_RULES = [
|
|
2020
2688
|
{
|
|
@@ -2339,13 +3007,13 @@ var CodeIndexEngine = class {
|
|
|
2339
3007
|
await this.store.init();
|
|
2340
3008
|
const limit = clampLimit(opts.limit, 20, 100);
|
|
2341
3009
|
const hits = await this.store.searchFts(query, opts.kind ?? null, limit);
|
|
2342
|
-
const
|
|
3010
|
+
const out2 = [];
|
|
2343
3011
|
for (const h of hits) {
|
|
2344
3012
|
const n = await this.store.getNode(h.nodeKey);
|
|
2345
3013
|
if (n)
|
|
2346
|
-
|
|
3014
|
+
out2.push({ ...toHit(n), score: h.score });
|
|
2347
3015
|
}
|
|
2348
|
-
return
|
|
3016
|
+
return out2;
|
|
2349
3017
|
}
|
|
2350
3018
|
async semanticQuery(query, opts = {}) {
|
|
2351
3019
|
await this.store.init();
|
|
@@ -2355,13 +3023,13 @@ var CodeIndexEngine = class {
|
|
|
2355
3023
|
const qvec = await this.embedder.embedQuery(query);
|
|
2356
3024
|
const all = await this.store.allEmbeddings();
|
|
2357
3025
|
const scored = all.map((e) => ({ nodeKey: e.nodeKey, score: cosineSimilarity(qvec, e.vec) })).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
2358
|
-
const
|
|
3026
|
+
const out2 = [];
|
|
2359
3027
|
for (const s of scored) {
|
|
2360
3028
|
const n = await this.store.getNode(s.nodeKey);
|
|
2361
3029
|
if (n)
|
|
2362
|
-
|
|
3030
|
+
out2.push({ ...toHit(n), score: s.score });
|
|
2363
3031
|
}
|
|
2364
|
-
return
|
|
3032
|
+
return out2;
|
|
2365
3033
|
}
|
|
2366
3034
|
async tracePath(nodeKey, opts = {}) {
|
|
2367
3035
|
await this.store.init();
|
|
@@ -2403,7 +3071,7 @@ var CodeIndexEngine = class {
|
|
|
2403
3071
|
return null;
|
|
2404
3072
|
let source = "";
|
|
2405
3073
|
try {
|
|
2406
|
-
const full =
|
|
3074
|
+
const full = readFileSync5(posix5.join(this.repoRoot, n.filePath), "utf8");
|
|
2407
3075
|
source = full.slice(n.startByte, n.endByte);
|
|
2408
3076
|
} catch {
|
|
2409
3077
|
return null;
|
|
@@ -2471,6 +3139,15 @@ var CodeIndexEngine = class {
|
|
|
2471
3139
|
}
|
|
2472
3140
|
return readFileHistory(this.repoRoot, paths, opts);
|
|
2473
3141
|
}
|
|
3142
|
+
async checkDivergence(opts = {}) {
|
|
3143
|
+
return checkDivergence(this.repoRoot, opts);
|
|
3144
|
+
}
|
|
3145
|
+
async checkNumberCollisions(opts = {}) {
|
|
3146
|
+
return checkNumberCollisions(this.repoRoot, opts);
|
|
3147
|
+
}
|
|
3148
|
+
async checkNumberForPath(proposedPath, opts = {}) {
|
|
3149
|
+
return checkNumberForPath(this.repoRoot, proposedPath, opts);
|
|
3150
|
+
}
|
|
2474
3151
|
async reviewChanges(opts = {}) {
|
|
2475
3152
|
await this.store.init();
|
|
2476
3153
|
const rawPaths = enumerateChangedPaths(this.repoRoot, opts);
|
|
@@ -2534,7 +3211,7 @@ var CodeIndexEngine = class {
|
|
|
2534
3211
|
}
|
|
2535
3212
|
readRepoFile(relPath) {
|
|
2536
3213
|
try {
|
|
2537
|
-
return
|
|
3214
|
+
return readFileSync5(posix5.join(this.repoRoot, relPath), "utf8");
|
|
2538
3215
|
} catch {
|
|
2539
3216
|
return null;
|
|
2540
3217
|
}
|
|
@@ -2593,8 +3270,109 @@ function classifyReviewSymbol(row, fileStatus, fh, stale) {
|
|
|
2593
3270
|
return { ...row, change: "co_located", changedLineCount: 0, staleIndex: false };
|
|
2594
3271
|
}
|
|
2595
3272
|
|
|
3273
|
+
// ../../packages/code-index/build/store/sqlite-driver.js
|
|
3274
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
3275
|
+
var requireHost = createRequire2(import.meta.url);
|
|
3276
|
+
function loadBuiltin() {
|
|
3277
|
+
return requireHost("node:sqlite");
|
|
3278
|
+
}
|
|
3279
|
+
var builtinProbeCache = null;
|
|
3280
|
+
function probeBuiltinSqlite() {
|
|
3281
|
+
if (builtinProbeCache)
|
|
3282
|
+
return builtinProbeCache;
|
|
3283
|
+
try {
|
|
3284
|
+
const { DatabaseSync } = loadBuiltin();
|
|
3285
|
+
const db = new DatabaseSync(":memory:");
|
|
3286
|
+
try {
|
|
3287
|
+
db.exec("CREATE VIRTUAL TABLE probe_fts USING fts5(t)");
|
|
3288
|
+
db.exec("INSERT INTO probe_fts (t) VALUES ('probe')");
|
|
3289
|
+
db.prepare("SELECT bm25(probe_fts) AS s FROM probe_fts WHERE probe_fts MATCH 'probe'").get();
|
|
3290
|
+
builtinProbeCache = { ok: true };
|
|
3291
|
+
} finally {
|
|
3292
|
+
db.close();
|
|
3293
|
+
}
|
|
3294
|
+
} catch (err4) {
|
|
3295
|
+
builtinProbeCache = {
|
|
3296
|
+
ok: false,
|
|
3297
|
+
reason: err4 instanceof Error ? err4.message : String(err4)
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
return builtinProbeCache;
|
|
3301
|
+
}
|
|
3302
|
+
function openBuiltin(dbPath) {
|
|
3303
|
+
const { DatabaseSync } = loadBuiltin();
|
|
3304
|
+
const raw = new DatabaseSync(dbPath);
|
|
3305
|
+
return {
|
|
3306
|
+
prepare: (sql) => raw.prepare(sql),
|
|
3307
|
+
exec: (sql) => raw.exec(sql),
|
|
3308
|
+
close: () => raw.close(),
|
|
3309
|
+
// node:sqlite hat keinen .transaction()-Helper — manuelle Klammer.
|
|
3310
|
+
transaction: (fn) => () => {
|
|
3311
|
+
raw.exec("BEGIN");
|
|
3312
|
+
try {
|
|
3313
|
+
fn();
|
|
3314
|
+
raw.exec("COMMIT");
|
|
3315
|
+
} catch (err4) {
|
|
3316
|
+
try {
|
|
3317
|
+
raw.exec("ROLLBACK");
|
|
3318
|
+
} catch {
|
|
3319
|
+
}
|
|
3320
|
+
throw err4;
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
function openBetter(dbPath) {
|
|
3326
|
+
const Database = requireHost("better-sqlite3");
|
|
3327
|
+
const raw = new Database(dbPath);
|
|
3328
|
+
return {
|
|
3329
|
+
prepare: (sql) => raw.prepare(sql),
|
|
3330
|
+
exec: (sql) => {
|
|
3331
|
+
raw.exec(sql);
|
|
3332
|
+
},
|
|
3333
|
+
close: () => {
|
|
3334
|
+
raw.close();
|
|
3335
|
+
},
|
|
3336
|
+
transaction: (fn) => raw.transaction(fn)
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
function backendOverride() {
|
|
3340
|
+
const v = (process.env.MAIND_SQLITE_BACKEND ?? "").trim().toLowerCase();
|
|
3341
|
+
if (v === "builtin" || v === "better")
|
|
3342
|
+
return v;
|
|
3343
|
+
return null;
|
|
3344
|
+
}
|
|
3345
|
+
function openSqliteDatabase(dbPath) {
|
|
3346
|
+
const override = backendOverride();
|
|
3347
|
+
const openAs = (backend) => {
|
|
3348
|
+
const db = backend === "node:sqlite" ? openBuiltin(dbPath) : openBetter(dbPath);
|
|
3349
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
3350
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
3351
|
+
return { db, backend };
|
|
3352
|
+
};
|
|
3353
|
+
if (override === "builtin") {
|
|
3354
|
+
const probe2 = probeBuiltinSqlite();
|
|
3355
|
+
if (!probe2.ok) {
|
|
3356
|
+
throw new Error(`MAIND_SQLITE_BACKEND=builtin, but node:sqlite is unusable: ${probe2.reason}`);
|
|
3357
|
+
}
|
|
3358
|
+
return openAs("node:sqlite");
|
|
3359
|
+
}
|
|
3360
|
+
if (override === "better") {
|
|
3361
|
+
return openAs("better-sqlite3");
|
|
3362
|
+
}
|
|
3363
|
+
const probe = probeBuiltinSqlite();
|
|
3364
|
+
if (probe.ok) {
|
|
3365
|
+
return openAs("node:sqlite");
|
|
3366
|
+
}
|
|
3367
|
+
try {
|
|
3368
|
+
return openAs("better-sqlite3");
|
|
3369
|
+
} catch (err4) {
|
|
3370
|
+
const betterReason = err4 instanceof Error ? err4.message : String(err4);
|
|
3371
|
+
throw new Error(`no usable SQLite backend: node:sqlite unavailable (${probe.reason}); better-sqlite3 unavailable (${betterReason})`);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
|
|
2596
3375
|
// ../../packages/code-index/build/store/node-sqlite.js
|
|
2597
|
-
import Database from "better-sqlite3";
|
|
2598
3376
|
function toNodeRow(r) {
|
|
2599
3377
|
return {
|
|
2600
3378
|
nodeKey: r.node_key,
|
|
@@ -2614,10 +3392,12 @@ function toNodeRow(r) {
|
|
|
2614
3392
|
var CHUNK = 400;
|
|
2615
3393
|
var NodeSqliteStore = class {
|
|
2616
3394
|
db;
|
|
3395
|
+
/** Welches Backend diesen Store trägt — für Diagnose/Status-Ausgaben. */
|
|
3396
|
+
backend;
|
|
2617
3397
|
constructor(dbPath) {
|
|
2618
|
-
|
|
2619
|
-
this.db
|
|
2620
|
-
this.
|
|
3398
|
+
const opened = openSqliteDatabase(dbPath);
|
|
3399
|
+
this.db = opened.db;
|
|
3400
|
+
this.backend = opened.backend;
|
|
2621
3401
|
}
|
|
2622
3402
|
async init() {
|
|
2623
3403
|
this.db.exec(SCHEMA_SQL);
|
|
@@ -2748,7 +3528,7 @@ var NodeSqliteStore = class {
|
|
|
2748
3528
|
}
|
|
2749
3529
|
async pruneOrphanEmbeddings() {
|
|
2750
3530
|
const info = this.db.prepare("DELETE FROM embeddings WHERE node_key NOT IN (SELECT node_key FROM nodes)").run();
|
|
2751
|
-
return info.changes;
|
|
3531
|
+
return Number(info.changes);
|
|
2752
3532
|
}
|
|
2753
3533
|
async countEmbeddings() {
|
|
2754
3534
|
return this.db.prepare("SELECT COUNT(*) AS c FROM embeddings").get().c;
|
|
@@ -3090,10 +3870,10 @@ var CodeIndexSync = class {
|
|
|
3090
3870
|
};
|
|
3091
3871
|
var EMBED_CHUNK = 250;
|
|
3092
3872
|
function chunkArray(items, size) {
|
|
3093
|
-
const
|
|
3873
|
+
const out2 = [];
|
|
3094
3874
|
for (let i = 0; i < items.length; i += size)
|
|
3095
|
-
|
|
3096
|
-
return
|
|
3875
|
+
out2.push(items.slice(i, i + size));
|
|
3876
|
+
return out2;
|
|
3097
3877
|
}
|
|
3098
3878
|
function numOrNull(v) {
|
|
3099
3879
|
if (v === null)
|
|
@@ -3108,8 +3888,8 @@ function numOr0(v) {
|
|
|
3108
3888
|
// ../../packages/code-index/build/node.js
|
|
3109
3889
|
var DEFAULT_DB_RELPATH = ".maind/code-index.sqlite";
|
|
3110
3890
|
function createNodeCodeIndex(opts) {
|
|
3111
|
-
const dbPath = opts.dbPath ??
|
|
3112
|
-
mkdirSync3(
|
|
3891
|
+
const dbPath = opts.dbPath ?? posix6.join(opts.repoRoot, DEFAULT_DB_RELPATH);
|
|
3892
|
+
mkdirSync3(posix6.dirname(dbPath), { recursive: true });
|
|
3113
3893
|
const store = new NodeSqliteStore(dbPath);
|
|
3114
3894
|
const parser = new TreeSitterParser();
|
|
3115
3895
|
return new CodeIndexEngine({
|
|
@@ -3120,7 +3900,7 @@ function createNodeCodeIndex(opts) {
|
|
|
3120
3900
|
});
|
|
3121
3901
|
}
|
|
3122
3902
|
function createCodeIndexSync(opts) {
|
|
3123
|
-
const dbPath = opts.dbPath ??
|
|
3903
|
+
const dbPath = opts.dbPath ?? posix6.join(opts.repoRoot, DEFAULT_DB_RELPATH);
|
|
3124
3904
|
const projectKey = opts.projectKey ?? computeProjectKey(opts.repoRoot) ?? void 0;
|
|
3125
3905
|
if (!projectKey) {
|
|
3126
3906
|
throw new Error("No project_key \u2014 the repo has no git remote. Cloud sync scopes by sha256(remote); commit a remote or pass an explicit key.");
|
|
@@ -3163,8 +3943,8 @@ function createEmbedder() {
|
|
|
3163
3943
|
if (texts.length === 0)
|
|
3164
3944
|
return [];
|
|
3165
3945
|
const extractor = await getExtractor();
|
|
3166
|
-
const
|
|
3167
|
-
return
|
|
3946
|
+
const out2 = await extractor(texts, { pooling: "cls", normalize: true });
|
|
3947
|
+
return out2.tolist().map((v) => Float32Array.from(v));
|
|
3168
3948
|
},
|
|
3169
3949
|
async embedQuery(text) {
|
|
3170
3950
|
const [vec] = await embedder.embedDocuments([
|
|
@@ -3209,7 +3989,7 @@ function runOrtIsolated(argv) {
|
|
|
3209
3989
|
stdio: ["ignore", "inherit", "inherit"],
|
|
3210
3990
|
env: { ...process.env, [OK_FILE_ENV]: okFile }
|
|
3211
3991
|
});
|
|
3212
|
-
const ok =
|
|
3992
|
+
const ok = existsSync4(okFile);
|
|
3213
3993
|
try {
|
|
3214
3994
|
rmSync(dirname3(okFile), { recursive: true, force: true });
|
|
3215
3995
|
} catch {
|
|
@@ -3237,7 +4017,7 @@ function value(argv, name) {
|
|
|
3237
4017
|
function resolveRoot(explicit) {
|
|
3238
4018
|
if (explicit) return explicit;
|
|
3239
4019
|
try {
|
|
3240
|
-
return
|
|
4020
|
+
return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
3241
4021
|
encoding: "utf8",
|
|
3242
4022
|
stdio: ["ignore", "pipe", "ignore"]
|
|
3243
4023
|
}).trim();
|
|
@@ -3411,40 +4191,1397 @@ async function runIndexArch(opts) {
|
|
|
3411
4191
|
}
|
|
3412
4192
|
}
|
|
3413
4193
|
|
|
3414
|
-
// src/
|
|
3415
|
-
import { execFileSync as
|
|
3416
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3417
|
-
import {
|
|
4194
|
+
// src/launch-cmd.ts
|
|
4195
|
+
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync6 } from "node:child_process";
|
|
4196
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4197
|
+
import { homedir as homedir2 } from "node:os";
|
|
4198
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
3418
4199
|
|
|
3419
|
-
// ../../
|
|
3420
|
-
|
|
4200
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
|
|
4201
|
+
function getLineColFromPtr(string, ptr) {
|
|
4202
|
+
let lines2 = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
4203
|
+
return [lines2.length, lines2.pop().length + 1];
|
|
4204
|
+
}
|
|
4205
|
+
function makeCodeBlock(string, line, column) {
|
|
4206
|
+
let lines2 = string.split(/\r\n|\n|\r/g);
|
|
4207
|
+
let codeblock = "";
|
|
4208
|
+
let numberLen = (Math.log10(line + 1) | 0) + 1;
|
|
4209
|
+
for (let i = line - 1; i <= line + 1; i++) {
|
|
4210
|
+
let l = lines2[i - 1];
|
|
4211
|
+
if (!l)
|
|
4212
|
+
continue;
|
|
4213
|
+
codeblock += i.toString().padEnd(numberLen, " ");
|
|
4214
|
+
codeblock += ": ";
|
|
4215
|
+
codeblock += l;
|
|
4216
|
+
codeblock += "\n";
|
|
4217
|
+
if (i === line) {
|
|
4218
|
+
codeblock += " ".repeat(numberLen + column + 2);
|
|
4219
|
+
codeblock += "^\n";
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
return codeblock;
|
|
4223
|
+
}
|
|
4224
|
+
var TomlError = class extends Error {
|
|
4225
|
+
line;
|
|
4226
|
+
column;
|
|
4227
|
+
codeblock;
|
|
4228
|
+
constructor(message, options) {
|
|
4229
|
+
const [line, column] = getLineColFromPtr(options.toml, options.ptr);
|
|
4230
|
+
const codeblock = makeCodeBlock(options.toml, line, column);
|
|
4231
|
+
super(`Invalid TOML document: ${message}
|
|
3421
4232
|
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
4233
|
+
${codeblock}`, options);
|
|
4234
|
+
this.line = line;
|
|
4235
|
+
this.column = column;
|
|
4236
|
+
this.codeblock = codeblock;
|
|
4237
|
+
}
|
|
4238
|
+
};
|
|
4239
|
+
|
|
4240
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/util.js
|
|
4241
|
+
function isEscaped(str, ptr) {
|
|
4242
|
+
let i = 0;
|
|
4243
|
+
while (str[ptr - ++i] === "\\")
|
|
4244
|
+
;
|
|
4245
|
+
return --i && i % 2;
|
|
4246
|
+
}
|
|
4247
|
+
function indexOfNewline(str, start = 0, end = str.length) {
|
|
4248
|
+
let idx = str.indexOf("\n", start);
|
|
4249
|
+
if (str[idx - 1] === "\r")
|
|
4250
|
+
idx--;
|
|
4251
|
+
return idx <= end ? idx : -1;
|
|
4252
|
+
}
|
|
4253
|
+
function skipComment(str, ptr) {
|
|
4254
|
+
for (let i = ptr; i < str.length; i++) {
|
|
4255
|
+
let c = str[i];
|
|
4256
|
+
if (c === "\n")
|
|
4257
|
+
return i;
|
|
4258
|
+
if (c === "\r" && str[i + 1] === "\n")
|
|
4259
|
+
return i + 1;
|
|
4260
|
+
if (c < " " && c !== " " || c === "\x7F") {
|
|
4261
|
+
throw new TomlError("control characters are not allowed in comments", {
|
|
4262
|
+
toml: str,
|
|
4263
|
+
ptr
|
|
4264
|
+
});
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
return str.length;
|
|
4268
|
+
}
|
|
4269
|
+
function skipVoid(str, ptr, banNewLines, banComments) {
|
|
4270
|
+
let c;
|
|
4271
|
+
while (1) {
|
|
4272
|
+
while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
|
|
4273
|
+
ptr++;
|
|
4274
|
+
if (banComments || c !== "#")
|
|
4275
|
+
break;
|
|
4276
|
+
ptr = skipComment(str, ptr);
|
|
4277
|
+
}
|
|
4278
|
+
return ptr;
|
|
4279
|
+
}
|
|
4280
|
+
function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
4281
|
+
if (!end) {
|
|
4282
|
+
ptr = indexOfNewline(str, ptr);
|
|
4283
|
+
return ptr < 0 ? str.length : ptr;
|
|
4284
|
+
}
|
|
4285
|
+
for (let i = ptr; i < str.length; i++) {
|
|
4286
|
+
let c = str[i];
|
|
4287
|
+
if (c === "#") {
|
|
4288
|
+
i = indexOfNewline(str, i);
|
|
4289
|
+
} else if (c === sep2) {
|
|
4290
|
+
return i + 1;
|
|
4291
|
+
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
4292
|
+
return i;
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
throw new TomlError("cannot find end of structure", {
|
|
4296
|
+
toml: str,
|
|
4297
|
+
ptr
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
function getStringEnd(str, seek) {
|
|
4301
|
+
let first = str[seek];
|
|
4302
|
+
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2] ? str.slice(seek, seek + 3) : first;
|
|
4303
|
+
seek += target.length - 1;
|
|
4304
|
+
do
|
|
4305
|
+
seek = str.indexOf(target, ++seek);
|
|
4306
|
+
while (seek > -1 && first !== "'" && isEscaped(str, seek));
|
|
4307
|
+
if (seek > -1) {
|
|
4308
|
+
seek += target.length;
|
|
4309
|
+
if (target.length > 1) {
|
|
4310
|
+
if (str[seek] === first)
|
|
4311
|
+
seek++;
|
|
4312
|
+
if (str[seek] === first)
|
|
4313
|
+
seek++;
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
return seek;
|
|
4317
|
+
}
|
|
4318
|
+
|
|
4319
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/date.js
|
|
4320
|
+
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
4321
|
+
var TomlDate = class _TomlDate extends Date {
|
|
4322
|
+
#hasDate = false;
|
|
4323
|
+
#hasTime = false;
|
|
4324
|
+
#offset = null;
|
|
4325
|
+
constructor(date) {
|
|
4326
|
+
let hasDate = true;
|
|
4327
|
+
let hasTime = true;
|
|
4328
|
+
let offset = "Z";
|
|
4329
|
+
if (typeof date === "string") {
|
|
4330
|
+
let match = date.match(DATE_TIME_RE);
|
|
4331
|
+
if (match) {
|
|
4332
|
+
if (!match[1]) {
|
|
4333
|
+
hasDate = false;
|
|
4334
|
+
date = `0000-01-01T${date}`;
|
|
4335
|
+
}
|
|
4336
|
+
hasTime = !!match[2];
|
|
4337
|
+
hasTime && date[10] === " " && (date = date.replace(" ", "T"));
|
|
4338
|
+
if (match[2] && +match[2] > 23) {
|
|
4339
|
+
date = "";
|
|
4340
|
+
} else {
|
|
4341
|
+
offset = match[3] || null;
|
|
4342
|
+
date = date.toUpperCase();
|
|
4343
|
+
if (!offset && hasTime)
|
|
4344
|
+
date += "Z";
|
|
4345
|
+
}
|
|
4346
|
+
} else {
|
|
4347
|
+
date = "";
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
super(date);
|
|
4351
|
+
if (!isNaN(this.getTime())) {
|
|
4352
|
+
this.#hasDate = hasDate;
|
|
4353
|
+
this.#hasTime = hasTime;
|
|
4354
|
+
this.#offset = offset;
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
isDateTime() {
|
|
4358
|
+
return this.#hasDate && this.#hasTime;
|
|
4359
|
+
}
|
|
4360
|
+
isLocal() {
|
|
4361
|
+
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
4362
|
+
}
|
|
4363
|
+
isDate() {
|
|
4364
|
+
return this.#hasDate && !this.#hasTime;
|
|
4365
|
+
}
|
|
4366
|
+
isTime() {
|
|
4367
|
+
return this.#hasTime && !this.#hasDate;
|
|
4368
|
+
}
|
|
4369
|
+
isValid() {
|
|
4370
|
+
return this.#hasDate || this.#hasTime;
|
|
4371
|
+
}
|
|
4372
|
+
toISOString() {
|
|
4373
|
+
let iso = super.toISOString();
|
|
4374
|
+
if (this.isDate())
|
|
4375
|
+
return iso.slice(0, 10);
|
|
4376
|
+
if (this.isTime())
|
|
4377
|
+
return iso.slice(11, 23);
|
|
4378
|
+
if (this.#offset === null)
|
|
4379
|
+
return iso.slice(0, -1);
|
|
4380
|
+
if (this.#offset === "Z")
|
|
4381
|
+
return iso;
|
|
4382
|
+
let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
|
|
4383
|
+
offset = this.#offset[0] === "-" ? offset : -offset;
|
|
4384
|
+
let offsetDate = new Date(this.getTime() - offset * 6e4);
|
|
4385
|
+
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
4386
|
+
}
|
|
4387
|
+
static wrapAsOffsetDateTime(jsDate, offset = "Z") {
|
|
4388
|
+
let date = new _TomlDate(jsDate);
|
|
4389
|
+
date.#offset = offset;
|
|
4390
|
+
return date;
|
|
4391
|
+
}
|
|
4392
|
+
static wrapAsLocalDateTime(jsDate) {
|
|
4393
|
+
let date = new _TomlDate(jsDate);
|
|
4394
|
+
date.#offset = null;
|
|
4395
|
+
return date;
|
|
4396
|
+
}
|
|
4397
|
+
static wrapAsLocalDate(jsDate) {
|
|
4398
|
+
let date = new _TomlDate(jsDate);
|
|
4399
|
+
date.#hasTime = false;
|
|
4400
|
+
date.#offset = null;
|
|
4401
|
+
return date;
|
|
4402
|
+
}
|
|
4403
|
+
static wrapAsLocalTime(jsDate) {
|
|
4404
|
+
let date = new _TomlDate(jsDate);
|
|
4405
|
+
date.#hasDate = false;
|
|
4406
|
+
date.#offset = null;
|
|
4407
|
+
return date;
|
|
4408
|
+
}
|
|
4409
|
+
};
|
|
4410
|
+
|
|
4411
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/primitive.js
|
|
4412
|
+
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
4413
|
+
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
4414
|
+
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
4415
|
+
var ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
|
|
4416
|
+
var ESC_MAP = {
|
|
4417
|
+
b: "\b",
|
|
4418
|
+
t: " ",
|
|
4419
|
+
n: "\n",
|
|
4420
|
+
f: "\f",
|
|
4421
|
+
r: "\r",
|
|
4422
|
+
e: "\x1B",
|
|
4423
|
+
'"': '"',
|
|
4424
|
+
"\\": "\\"
|
|
4425
|
+
};
|
|
4426
|
+
function parseString(str, ptr = 0, endPtr = str.length) {
|
|
4427
|
+
let isLiteral = str[ptr] === "'";
|
|
4428
|
+
let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1];
|
|
4429
|
+
if (isMultiline) {
|
|
4430
|
+
endPtr -= 2;
|
|
4431
|
+
if (str[ptr += 2] === "\r")
|
|
4432
|
+
ptr++;
|
|
4433
|
+
if (str[ptr] === "\n")
|
|
4434
|
+
ptr++;
|
|
4435
|
+
}
|
|
4436
|
+
let tmp = 0;
|
|
4437
|
+
let isEscape;
|
|
4438
|
+
let parsed = "";
|
|
4439
|
+
let sliceStart = ptr;
|
|
4440
|
+
while (ptr < endPtr - 1) {
|
|
4441
|
+
let c = str[ptr++];
|
|
4442
|
+
if (c === "\n" || c === "\r" && str[ptr] === "\n") {
|
|
4443
|
+
if (!isMultiline) {
|
|
4444
|
+
throw new TomlError("newlines are not allowed in strings", {
|
|
4445
|
+
toml: str,
|
|
4446
|
+
ptr: ptr - 1
|
|
4447
|
+
});
|
|
4448
|
+
}
|
|
4449
|
+
} else if (c < " " && c !== " " || c === "\x7F") {
|
|
4450
|
+
throw new TomlError("control characters are not allowed in strings", {
|
|
4451
|
+
toml: str,
|
|
4452
|
+
ptr: ptr - 1
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
if (isEscape) {
|
|
4456
|
+
isEscape = false;
|
|
4457
|
+
if (c === "x" || c === "u" || c === "U") {
|
|
4458
|
+
let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
|
|
4459
|
+
if (!ESCAPE_REGEX.test(code)) {
|
|
4460
|
+
throw new TomlError("invalid unicode escape", {
|
|
4461
|
+
toml: str,
|
|
4462
|
+
ptr: tmp
|
|
4463
|
+
});
|
|
4464
|
+
}
|
|
4465
|
+
try {
|
|
4466
|
+
parsed += String.fromCodePoint(parseInt(code, 16));
|
|
4467
|
+
} catch {
|
|
4468
|
+
throw new TomlError("invalid unicode escape", {
|
|
4469
|
+
toml: str,
|
|
4470
|
+
ptr: tmp
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
} else if (isMultiline && (c === "\n" || c === " " || c === " " || c === "\r")) {
|
|
4474
|
+
ptr = skipVoid(str, ptr - 1, true);
|
|
4475
|
+
if (str[ptr] !== "\n" && str[ptr] !== "\r") {
|
|
4476
|
+
throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
|
|
4477
|
+
toml: str,
|
|
4478
|
+
ptr: tmp
|
|
4479
|
+
});
|
|
4480
|
+
}
|
|
4481
|
+
ptr = skipVoid(str, ptr);
|
|
4482
|
+
} else if (c in ESC_MAP) {
|
|
4483
|
+
parsed += ESC_MAP[c];
|
|
4484
|
+
} else {
|
|
4485
|
+
throw new TomlError("unrecognized escape sequence", {
|
|
4486
|
+
toml: str,
|
|
4487
|
+
ptr: tmp
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
sliceStart = ptr;
|
|
4491
|
+
} else if (!isLiteral && c === "\\") {
|
|
4492
|
+
tmp = ptr - 1;
|
|
4493
|
+
isEscape = true;
|
|
4494
|
+
parsed += str.slice(sliceStart, tmp);
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
return parsed + str.slice(sliceStart, endPtr - 1);
|
|
4498
|
+
}
|
|
4499
|
+
function parseValue(value2, toml, ptr, integersAsBigInt) {
|
|
4500
|
+
if (value2 === "true")
|
|
4501
|
+
return true;
|
|
4502
|
+
if (value2 === "false")
|
|
4503
|
+
return false;
|
|
4504
|
+
if (value2 === "-inf")
|
|
4505
|
+
return -Infinity;
|
|
4506
|
+
if (value2 === "inf" || value2 === "+inf")
|
|
4507
|
+
return Infinity;
|
|
4508
|
+
if (value2 === "nan" || value2 === "+nan" || value2 === "-nan")
|
|
4509
|
+
return NaN;
|
|
4510
|
+
if (value2 === "-0")
|
|
4511
|
+
return integersAsBigInt ? 0n : 0;
|
|
4512
|
+
let isInt = INT_REGEX.test(value2);
|
|
4513
|
+
if (isInt || FLOAT_REGEX.test(value2)) {
|
|
4514
|
+
if (LEADING_ZERO.test(value2)) {
|
|
4515
|
+
throw new TomlError("leading zeroes are not allowed", {
|
|
4516
|
+
toml,
|
|
4517
|
+
ptr
|
|
4518
|
+
});
|
|
4519
|
+
}
|
|
4520
|
+
value2 = value2.replace(/_/g, "");
|
|
4521
|
+
let numeric = +value2;
|
|
4522
|
+
if (isNaN(numeric)) {
|
|
4523
|
+
throw new TomlError("invalid number", {
|
|
4524
|
+
toml,
|
|
4525
|
+
ptr
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4528
|
+
if (isInt) {
|
|
4529
|
+
if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
|
|
4530
|
+
throw new TomlError("integer value cannot be represented losslessly", {
|
|
4531
|
+
toml,
|
|
4532
|
+
ptr
|
|
4533
|
+
});
|
|
4534
|
+
}
|
|
4535
|
+
if (isInt || integersAsBigInt === true)
|
|
4536
|
+
numeric = BigInt(value2);
|
|
4537
|
+
}
|
|
4538
|
+
return numeric;
|
|
4539
|
+
}
|
|
4540
|
+
const date = new TomlDate(value2);
|
|
4541
|
+
if (!date.isValid()) {
|
|
4542
|
+
throw new TomlError("invalid value", {
|
|
4543
|
+
toml,
|
|
4544
|
+
ptr
|
|
4545
|
+
});
|
|
4546
|
+
}
|
|
4547
|
+
return date;
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/extract.js
|
|
4551
|
+
function sliceAndTrimEndOf(str, startPtr, endPtr) {
|
|
4552
|
+
let value2 = str.slice(startPtr, endPtr);
|
|
4553
|
+
let commentIdx = value2.indexOf("#");
|
|
4554
|
+
if (commentIdx > -1) {
|
|
4555
|
+
skipComment(str, commentIdx);
|
|
4556
|
+
value2 = value2.slice(0, commentIdx);
|
|
4557
|
+
}
|
|
4558
|
+
return [value2.trimEnd(), commentIdx];
|
|
4559
|
+
}
|
|
4560
|
+
function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
4561
|
+
if (depth === 0) {
|
|
4562
|
+
throw new TomlError("document contains excessively nested structures. aborting.", {
|
|
4563
|
+
toml: str,
|
|
4564
|
+
ptr
|
|
4565
|
+
});
|
|
4566
|
+
}
|
|
4567
|
+
let c = str[ptr];
|
|
4568
|
+
if (c === "[" || c === "{") {
|
|
4569
|
+
let [value2, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
|
|
4570
|
+
if (end) {
|
|
4571
|
+
endPtr2 = skipVoid(str, endPtr2);
|
|
4572
|
+
if (str[endPtr2] === ",")
|
|
4573
|
+
endPtr2++;
|
|
4574
|
+
else if (str[endPtr2] !== end) {
|
|
4575
|
+
throw new TomlError("expected comma or end of structure", {
|
|
4576
|
+
toml: str,
|
|
4577
|
+
ptr: endPtr2
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
return [value2, endPtr2];
|
|
4582
|
+
}
|
|
4583
|
+
let endPtr;
|
|
4584
|
+
if (c === '"' || c === "'") {
|
|
4585
|
+
endPtr = getStringEnd(str, ptr);
|
|
4586
|
+
let parsed = parseString(str, ptr, endPtr);
|
|
4587
|
+
if (end) {
|
|
4588
|
+
endPtr = skipVoid(str, endPtr);
|
|
4589
|
+
if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") {
|
|
4590
|
+
throw new TomlError("unexpected character encountered", {
|
|
4591
|
+
toml: str,
|
|
4592
|
+
ptr: endPtr
|
|
4593
|
+
});
|
|
4594
|
+
}
|
|
4595
|
+
endPtr += +(str[endPtr] === ",");
|
|
4596
|
+
}
|
|
4597
|
+
return [parsed, endPtr];
|
|
4598
|
+
}
|
|
4599
|
+
endPtr = skipUntil(str, ptr, ",", end);
|
|
4600
|
+
let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","));
|
|
4601
|
+
if (!slice[0]) {
|
|
4602
|
+
throw new TomlError("incomplete key-value declaration: no value specified", {
|
|
4603
|
+
toml: str,
|
|
4604
|
+
ptr
|
|
4605
|
+
});
|
|
4606
|
+
}
|
|
4607
|
+
if (end && slice[1] > -1) {
|
|
4608
|
+
endPtr = skipVoid(str, ptr + slice[1]);
|
|
4609
|
+
endPtr += +(str[endPtr] === ",");
|
|
4610
|
+
}
|
|
4611
|
+
return [
|
|
4612
|
+
parseValue(slice[0], str, ptr, integersAsBigInt),
|
|
4613
|
+
endPtr
|
|
4614
|
+
];
|
|
4615
|
+
}
|
|
4616
|
+
|
|
4617
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/struct.js
|
|
4618
|
+
var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
4619
|
+
function parseKey(str, ptr, end = "=") {
|
|
4620
|
+
let dot = ptr - 1;
|
|
4621
|
+
let parsed = [];
|
|
4622
|
+
let endPtr = str.indexOf(end, ptr);
|
|
4623
|
+
if (endPtr < 0) {
|
|
4624
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
4625
|
+
toml: str,
|
|
4626
|
+
ptr
|
|
4627
|
+
});
|
|
4628
|
+
}
|
|
4629
|
+
do {
|
|
4630
|
+
let c = str[ptr = ++dot];
|
|
4631
|
+
if (c !== " " && c !== " ") {
|
|
4632
|
+
if (c === '"' || c === "'") {
|
|
4633
|
+
if (c === str[ptr + 1] && c === str[ptr + 2]) {
|
|
4634
|
+
throw new TomlError("multiline strings are not allowed in keys", {
|
|
4635
|
+
toml: str,
|
|
4636
|
+
ptr
|
|
4637
|
+
});
|
|
4638
|
+
}
|
|
4639
|
+
let eos = getStringEnd(str, ptr);
|
|
4640
|
+
if (eos < 0) {
|
|
4641
|
+
throw new TomlError("unfinished string encountered", {
|
|
4642
|
+
toml: str,
|
|
4643
|
+
ptr
|
|
4644
|
+
});
|
|
4645
|
+
}
|
|
4646
|
+
dot = str.indexOf(".", eos);
|
|
4647
|
+
let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
4648
|
+
let newLine = indexOfNewline(strEnd);
|
|
4649
|
+
if (newLine > -1) {
|
|
4650
|
+
throw new TomlError("newlines are not allowed in keys", {
|
|
4651
|
+
toml: str,
|
|
4652
|
+
ptr: ptr + dot + newLine
|
|
4653
|
+
});
|
|
4654
|
+
}
|
|
4655
|
+
if (strEnd.trimStart()) {
|
|
4656
|
+
throw new TomlError("found extra tokens after the string part", {
|
|
4657
|
+
toml: str,
|
|
4658
|
+
ptr: eos
|
|
4659
|
+
});
|
|
4660
|
+
}
|
|
4661
|
+
if (endPtr < eos) {
|
|
4662
|
+
endPtr = str.indexOf(end, eos);
|
|
4663
|
+
if (endPtr < 0) {
|
|
4664
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
4665
|
+
toml: str,
|
|
4666
|
+
ptr
|
|
4667
|
+
});
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
parsed.push(parseString(str, ptr, eos));
|
|
4671
|
+
} else {
|
|
4672
|
+
dot = str.indexOf(".", ptr);
|
|
4673
|
+
let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
4674
|
+
if (!KEY_PART_RE.test(part)) {
|
|
4675
|
+
throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
|
|
4676
|
+
toml: str,
|
|
4677
|
+
ptr
|
|
4678
|
+
});
|
|
4679
|
+
}
|
|
4680
|
+
parsed.push(part.trimEnd());
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
} while (dot + 1 && dot < endPtr);
|
|
4684
|
+
return [parsed, skipVoid(str, endPtr + 1, true, true)];
|
|
4685
|
+
}
|
|
4686
|
+
function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
4687
|
+
let res = {};
|
|
4688
|
+
let seen = /* @__PURE__ */ new Set();
|
|
4689
|
+
let c;
|
|
4690
|
+
ptr++;
|
|
4691
|
+
while ((c = str[ptr++]) !== "}" && c) {
|
|
4692
|
+
if (c === ",") {
|
|
4693
|
+
throw new TomlError("expected value, found comma", {
|
|
4694
|
+
toml: str,
|
|
4695
|
+
ptr: ptr - 1
|
|
4696
|
+
});
|
|
4697
|
+
} else if (c === "#")
|
|
4698
|
+
ptr = skipComment(str, ptr);
|
|
4699
|
+
else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
|
|
4700
|
+
let k;
|
|
4701
|
+
let t = res;
|
|
4702
|
+
let hasOwn = false;
|
|
4703
|
+
let [key, keyEndPtr] = parseKey(str, ptr - 1);
|
|
4704
|
+
for (let i = 0; i < key.length; i++) {
|
|
4705
|
+
if (i)
|
|
4706
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
4707
|
+
k = key[i];
|
|
4708
|
+
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
|
|
4709
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
4710
|
+
toml: str,
|
|
4711
|
+
ptr
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
if (!hasOwn && k === "__proto__") {
|
|
4715
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
if (hasOwn) {
|
|
4719
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
4720
|
+
toml: str,
|
|
4721
|
+
ptr
|
|
4722
|
+
});
|
|
4723
|
+
}
|
|
4724
|
+
let [value2, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt);
|
|
4725
|
+
seen.add(value2);
|
|
4726
|
+
t[k] = value2;
|
|
4727
|
+
ptr = valueEndPtr;
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
if (!c) {
|
|
4731
|
+
throw new TomlError("unfinished table encountered", {
|
|
4732
|
+
toml: str,
|
|
4733
|
+
ptr
|
|
4734
|
+
});
|
|
4735
|
+
}
|
|
4736
|
+
return [res, ptr];
|
|
4737
|
+
}
|
|
4738
|
+
function parseArray(str, ptr, depth, integersAsBigInt) {
|
|
4739
|
+
let res = [];
|
|
4740
|
+
let c;
|
|
4741
|
+
ptr++;
|
|
4742
|
+
while ((c = str[ptr++]) !== "]" && c) {
|
|
4743
|
+
if (c === ",") {
|
|
4744
|
+
throw new TomlError("expected value, found comma", {
|
|
4745
|
+
toml: str,
|
|
4746
|
+
ptr: ptr - 1
|
|
4747
|
+
});
|
|
4748
|
+
} else if (c === "#")
|
|
4749
|
+
ptr = skipComment(str, ptr);
|
|
4750
|
+
else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
|
|
4751
|
+
let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
|
|
4752
|
+
res.push(e[0]);
|
|
4753
|
+
ptr = e[1];
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
if (!c) {
|
|
4757
|
+
throw new TomlError("unfinished array encountered", {
|
|
4758
|
+
toml: str,
|
|
4759
|
+
ptr
|
|
4760
|
+
});
|
|
4761
|
+
}
|
|
4762
|
+
return [res, ptr];
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/parse.js
|
|
4766
|
+
function peekTable(key, table, meta, type) {
|
|
4767
|
+
let t = table;
|
|
4768
|
+
let m = meta;
|
|
4769
|
+
let k;
|
|
4770
|
+
let hasOwn = false;
|
|
4771
|
+
let state;
|
|
4772
|
+
for (let i = 0; i < key.length; i++) {
|
|
4773
|
+
if (i) {
|
|
4774
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
4775
|
+
m = (state = m[k]).c;
|
|
4776
|
+
if (type === 0 && (state.t === 1 || state.t === 2)) {
|
|
4777
|
+
return null;
|
|
4778
|
+
}
|
|
4779
|
+
if (state.t === 2) {
|
|
4780
|
+
let l = t.length - 1;
|
|
4781
|
+
t = t[l];
|
|
4782
|
+
m = m[l].c;
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
k = key[i];
|
|
4786
|
+
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
|
|
4787
|
+
return null;
|
|
4788
|
+
}
|
|
4789
|
+
if (!hasOwn) {
|
|
4790
|
+
if (k === "__proto__") {
|
|
4791
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
4792
|
+
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
4793
|
+
}
|
|
4794
|
+
m[k] = {
|
|
4795
|
+
t: i < key.length - 1 && type === 2 ? 3 : type,
|
|
4796
|
+
d: false,
|
|
4797
|
+
i: 0,
|
|
4798
|
+
c: {}
|
|
4799
|
+
};
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
state = m[k];
|
|
4803
|
+
if (state.t !== type && !(type === 1 && state.t === 3)) {
|
|
4804
|
+
return null;
|
|
4805
|
+
}
|
|
4806
|
+
if (type === 2) {
|
|
4807
|
+
if (!state.d) {
|
|
4808
|
+
state.d = true;
|
|
4809
|
+
t[k] = [];
|
|
4810
|
+
}
|
|
4811
|
+
t[k].push(t = {});
|
|
4812
|
+
state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
|
|
4813
|
+
}
|
|
4814
|
+
if (state.d) {
|
|
4815
|
+
return null;
|
|
4816
|
+
}
|
|
4817
|
+
state.d = true;
|
|
4818
|
+
if (type === 1) {
|
|
4819
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
4820
|
+
} else if (type === 0 && hasOwn) {
|
|
4821
|
+
return null;
|
|
4822
|
+
}
|
|
4823
|
+
return [k, t, state.c];
|
|
4824
|
+
}
|
|
4825
|
+
function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
4826
|
+
let res = {};
|
|
4827
|
+
let meta = {};
|
|
4828
|
+
let tbl = res;
|
|
4829
|
+
let m = meta;
|
|
4830
|
+
for (let ptr = skipVoid(toml, 0); ptr < toml.length; ) {
|
|
4831
|
+
if (toml[ptr] === "[") {
|
|
4832
|
+
let isTableArray = toml[++ptr] === "[";
|
|
4833
|
+
let k = parseKey(toml, ptr += +isTableArray, "]");
|
|
4834
|
+
if (isTableArray) {
|
|
4835
|
+
if (toml[k[1] - 1] !== "]") {
|
|
4836
|
+
throw new TomlError("expected end of table declaration", {
|
|
4837
|
+
toml,
|
|
4838
|
+
ptr: k[1] - 1
|
|
4839
|
+
});
|
|
4840
|
+
}
|
|
4841
|
+
k[1]++;
|
|
4842
|
+
}
|
|
4843
|
+
let p = peekTable(
|
|
4844
|
+
k[0],
|
|
4845
|
+
res,
|
|
4846
|
+
meta,
|
|
4847
|
+
isTableArray ? 2 : 1
|
|
4848
|
+
/* Type.EXPLICIT */
|
|
4849
|
+
);
|
|
4850
|
+
if (!p) {
|
|
4851
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
4852
|
+
toml,
|
|
4853
|
+
ptr
|
|
4854
|
+
});
|
|
4855
|
+
}
|
|
4856
|
+
m = p[2];
|
|
4857
|
+
tbl = p[1];
|
|
4858
|
+
ptr = k[1];
|
|
4859
|
+
} else {
|
|
4860
|
+
let k = parseKey(toml, ptr);
|
|
4861
|
+
let p = peekTable(
|
|
4862
|
+
k[0],
|
|
4863
|
+
tbl,
|
|
4864
|
+
m,
|
|
4865
|
+
0
|
|
4866
|
+
/* Type.DOTTED */
|
|
4867
|
+
);
|
|
4868
|
+
if (!p) {
|
|
4869
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
4870
|
+
toml,
|
|
4871
|
+
ptr
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
let v = extractValue(toml, k[1], void 0, maxDepth, integersAsBigInt);
|
|
4875
|
+
p[1][p[0]] = v[0];
|
|
4876
|
+
ptr = v[1];
|
|
4877
|
+
}
|
|
4878
|
+
ptr = skipVoid(toml, ptr, true);
|
|
4879
|
+
if (toml[ptr] && toml[ptr] !== "\n" && toml[ptr] !== "\r") {
|
|
4880
|
+
throw new TomlError("each key-value declaration must be followed by an end-of-line", {
|
|
4881
|
+
toml,
|
|
4882
|
+
ptr
|
|
4883
|
+
});
|
|
4884
|
+
}
|
|
4885
|
+
ptr = skipVoid(toml, ptr);
|
|
4886
|
+
}
|
|
4887
|
+
return res;
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/stringify.js
|
|
4891
|
+
var BARE_KEY = /^[a-z0-9-_]+$/i;
|
|
4892
|
+
function extendedTypeOf(obj) {
|
|
4893
|
+
let type = typeof obj;
|
|
4894
|
+
if (type === "object") {
|
|
4895
|
+
if (Array.isArray(obj))
|
|
4896
|
+
return "array";
|
|
4897
|
+
if (obj instanceof Date)
|
|
4898
|
+
return "date";
|
|
4899
|
+
}
|
|
4900
|
+
return type;
|
|
4901
|
+
}
|
|
4902
|
+
function isArrayOfTables(obj) {
|
|
4903
|
+
for (let i = 0; i < obj.length; i++) {
|
|
4904
|
+
if (extendedTypeOf(obj[i]) !== "object")
|
|
4905
|
+
return false;
|
|
4906
|
+
}
|
|
4907
|
+
return obj.length != 0;
|
|
4908
|
+
}
|
|
4909
|
+
function formatString(s) {
|
|
4910
|
+
return JSON.stringify(s).replace(/\x7f/g, "\\u007f");
|
|
4911
|
+
}
|
|
4912
|
+
function stringifyValue(val, type, depth, numberAsFloat) {
|
|
4913
|
+
if (depth === 0) {
|
|
4914
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
4915
|
+
}
|
|
4916
|
+
if (type === "number") {
|
|
4917
|
+
if (isNaN(val))
|
|
4918
|
+
return "nan";
|
|
4919
|
+
if (val === Infinity)
|
|
4920
|
+
return "inf";
|
|
4921
|
+
if (val === -Infinity)
|
|
4922
|
+
return "-inf";
|
|
4923
|
+
if (numberAsFloat && Number.isInteger(val))
|
|
4924
|
+
return val.toFixed(1);
|
|
4925
|
+
return val.toString();
|
|
4926
|
+
}
|
|
4927
|
+
if (type === "bigint" || type === "boolean") {
|
|
4928
|
+
return val.toString();
|
|
4929
|
+
}
|
|
4930
|
+
if (type === "string") {
|
|
4931
|
+
return formatString(val);
|
|
4932
|
+
}
|
|
4933
|
+
if (type === "date") {
|
|
4934
|
+
if (isNaN(val.getTime())) {
|
|
4935
|
+
throw new TypeError("cannot serialize invalid date");
|
|
4936
|
+
}
|
|
4937
|
+
return val.toISOString();
|
|
4938
|
+
}
|
|
4939
|
+
if (type === "object") {
|
|
4940
|
+
return stringifyInlineTable(val, depth, numberAsFloat);
|
|
4941
|
+
}
|
|
4942
|
+
if (type === "array") {
|
|
4943
|
+
return stringifyArray(val, depth, numberAsFloat);
|
|
4944
|
+
}
|
|
4945
|
+
}
|
|
4946
|
+
function stringifyInlineTable(obj, depth, numberAsFloat) {
|
|
4947
|
+
let keys = Object.keys(obj);
|
|
4948
|
+
if (keys.length === 0)
|
|
4949
|
+
return "{}";
|
|
4950
|
+
let res = "{ ";
|
|
4951
|
+
for (let i = 0; i < keys.length; i++) {
|
|
4952
|
+
let k = keys[i];
|
|
4953
|
+
if (i)
|
|
4954
|
+
res += ", ";
|
|
4955
|
+
res += BARE_KEY.test(k) ? k : formatString(k);
|
|
4956
|
+
res += " = ";
|
|
4957
|
+
res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
|
|
4958
|
+
}
|
|
4959
|
+
return res + " }";
|
|
4960
|
+
}
|
|
4961
|
+
function stringifyArray(array, depth, numberAsFloat) {
|
|
4962
|
+
if (array.length === 0)
|
|
4963
|
+
return "[]";
|
|
4964
|
+
let res = "[ ";
|
|
4965
|
+
for (let i = 0; i < array.length; i++) {
|
|
4966
|
+
if (i)
|
|
4967
|
+
res += ", ";
|
|
4968
|
+
if (array[i] === null || array[i] === void 0) {
|
|
4969
|
+
throw new TypeError("arrays cannot contain null or undefined values");
|
|
4970
|
+
}
|
|
4971
|
+
res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat);
|
|
4972
|
+
}
|
|
4973
|
+
return res + " ]";
|
|
4974
|
+
}
|
|
4975
|
+
function stringifyArrayTable(array, key, depth, numberAsFloat) {
|
|
4976
|
+
if (depth === 0) {
|
|
4977
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
4978
|
+
}
|
|
4979
|
+
let res = "";
|
|
4980
|
+
for (let i = 0; i < array.length; i++) {
|
|
4981
|
+
res += `${res && "\n"}[[${key}]]
|
|
4982
|
+
`;
|
|
4983
|
+
res += stringifyTable(0, array[i], key, depth, numberAsFloat);
|
|
4984
|
+
}
|
|
4985
|
+
return res;
|
|
4986
|
+
}
|
|
4987
|
+
function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
|
|
4988
|
+
if (depth === 0) {
|
|
4989
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
4990
|
+
}
|
|
4991
|
+
let preamble = "";
|
|
4992
|
+
let tables = "";
|
|
4993
|
+
let keys = Object.keys(obj);
|
|
4994
|
+
for (let i = 0; i < keys.length; i++) {
|
|
4995
|
+
let k = keys[i];
|
|
4996
|
+
if (obj[k] !== null && obj[k] !== void 0) {
|
|
4997
|
+
let type = extendedTypeOf(obj[k]);
|
|
4998
|
+
if (type === "symbol" || type === "function") {
|
|
4999
|
+
throw new TypeError(`cannot serialize values of type '${type}'`);
|
|
5000
|
+
}
|
|
5001
|
+
let key = BARE_KEY.test(k) ? k : formatString(k);
|
|
5002
|
+
if (type === "array" && isArrayOfTables(obj[k])) {
|
|
5003
|
+
tables += (tables && "\n") + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
|
|
5004
|
+
} else if (type === "object") {
|
|
5005
|
+
let tblKey = prefix ? `${prefix}.${key}` : key;
|
|
5006
|
+
tables += (tables && "\n") + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
|
|
5007
|
+
} else {
|
|
5008
|
+
preamble += key;
|
|
5009
|
+
preamble += " = ";
|
|
5010
|
+
preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
|
|
5011
|
+
preamble += "\n";
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
if (tableKey && (preamble || !tables))
|
|
5016
|
+
preamble = preamble ? `[${tableKey}]
|
|
5017
|
+
${preamble}` : `[${tableKey}]`;
|
|
5018
|
+
return preamble && tables ? `${preamble}
|
|
5019
|
+
${tables}` : preamble || tables;
|
|
5020
|
+
}
|
|
5021
|
+
function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
5022
|
+
if (extendedTypeOf(obj) !== "object") {
|
|
5023
|
+
throw new TypeError("stringify can only be called with an object");
|
|
5024
|
+
}
|
|
5025
|
+
let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat);
|
|
5026
|
+
if (str[str.length - 1] !== "\n")
|
|
5027
|
+
return str + "\n";
|
|
5028
|
+
return str;
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
// src/switchyard.ts
|
|
5032
|
+
var SWITCHYARD_BIN = "switchyard";
|
|
5033
|
+
var SWITCHYARD_INSTALL_ARGV = [
|
|
5034
|
+
"tool",
|
|
5035
|
+
"install",
|
|
5036
|
+
"--python",
|
|
5037
|
+
"3.12",
|
|
5038
|
+
"nemo-switchyard[cli]"
|
|
5039
|
+
];
|
|
5040
|
+
var SWITCHYARD_INSTALL_ONELINER = 'uv tool install --python 3.12 "nemo-switchyard[cli]"';
|
|
5041
|
+
var UV_INSTALL_DOCS = "https://docs.astral.sh/uv/getting-started/installation/";
|
|
5042
|
+
var DEFAULT_ROUTE = "switchyard";
|
|
5043
|
+
var ROUTES_FILENAME = "switchyard-routes.toml";
|
|
5044
|
+
var STARTUP_GRACE_MS = 5e3;
|
|
5045
|
+
var LAUNCH_TEMPLATE_ID = "tmpl_switchyard_routes_default";
|
|
5046
|
+
var ANTHROPIC_CAPABLE_ID = "anthropic/claude-opus-5";
|
|
5047
|
+
var ANTHROPIC_EFFICIENT_ID = "anthropic/claude-haiku-4-5-20251001";
|
|
5048
|
+
var OPENROUTER_CAPABLE_ID = "openrouter/anthropic/claude-opus-5";
|
|
5049
|
+
var OPENROUTER_EFFICIENT_ID = "openrouter/anthropic/claude-haiku-4-5";
|
|
5050
|
+
var SUPPORTED_AGENTS = ["claude"];
|
|
5051
|
+
var PLANNED_AGENTS = ["codex", "openclaw"];
|
|
5052
|
+
function classifyAgent(agent) {
|
|
5053
|
+
if (agent === "claude") return { kind: "supported", agent };
|
|
5054
|
+
if (agent !== null && PLANNED_AGENTS.includes(agent)) return { kind: "planned", agent };
|
|
5055
|
+
return { kind: "unknown", agent };
|
|
5056
|
+
}
|
|
5057
|
+
function chooseBackend(env) {
|
|
5058
|
+
const hasAnthropic = (env.ANTHROPIC_API_KEY ?? "").trim().length > 0;
|
|
5059
|
+
const hasOpenRouter = (env.OPENROUTER_API_KEY ?? "").trim().length > 0;
|
|
5060
|
+
if (hasAnthropic) {
|
|
5061
|
+
return {
|
|
5062
|
+
kind: "anthropic",
|
|
5063
|
+
capableId: ANTHROPIC_CAPABLE_ID,
|
|
5064
|
+
efficientId: ANTHROPIC_EFFICIENT_ID,
|
|
5065
|
+
warning: null
|
|
5066
|
+
};
|
|
5067
|
+
}
|
|
5068
|
+
if (hasOpenRouter) {
|
|
5069
|
+
return {
|
|
5070
|
+
kind: "openrouter",
|
|
5071
|
+
capableId: OPENROUTER_CAPABLE_ID,
|
|
5072
|
+
efficientId: OPENROUTER_EFFICIENT_ID,
|
|
5073
|
+
warning: null
|
|
5074
|
+
};
|
|
5075
|
+
}
|
|
5076
|
+
return {
|
|
5077
|
+
kind: "none",
|
|
5078
|
+
capableId: OPENROUTER_CAPABLE_ID,
|
|
5079
|
+
efficientId: OPENROUTER_EFFICIENT_ID,
|
|
5080
|
+
warning: "no backend API key found (ANTHROPIC_API_KEY or OPENROUTER_API_KEY) \u2014 the proxy will start but upstream requests will fail until one is exported."
|
|
5081
|
+
};
|
|
5082
|
+
}
|
|
5083
|
+
var CAPABLE_TARGET = "capable";
|
|
5084
|
+
var EFFICIENT_TARGET = "efficient";
|
|
5085
|
+
function policyHeader(backend, policySource) {
|
|
5086
|
+
return [
|
|
5087
|
+
"# Generated by `maind launch` (Agent Switchyard Phase 1, maind.dev \u2014 ADR-272).",
|
|
5088
|
+
"# Regenerated on every launch; do not hand-edit. For a custom policy use",
|
|
5089
|
+
"# `maind launch <agent> --config <path>`.",
|
|
5090
|
+
"#",
|
|
5091
|
+
`# Routing policy: ${policySource === "curated" ? "curated (maind content plane)" : "builtin default"}`,
|
|
5092
|
+
"# \u2014 deterministic stage routing (NVIDIA NeMo Switchyard `stage_router`).",
|
|
5093
|
+
"# effort low/medium -> efficient_target (fast tier)",
|
|
5094
|
+
"# effort high/xhigh/max -> capable_target (strong tier)",
|
|
5095
|
+
"# picker `efficient_first` + confidence_threshold 0.5: steady write progress",
|
|
5096
|
+
"# keeps the efficient tier; corroborating error/exploration signals escalate",
|
|
5097
|
+
"# to the capable tier.",
|
|
5098
|
+
"#",
|
|
5099
|
+
`# Backend: ${backend.kind === "none" ? "openrouter (EXAMPLE \u2014 no API key detected)" : backend.kind}`,
|
|
5100
|
+
"# \u2014 detected locally from the environment, never from the policy source.",
|
|
5101
|
+
"# API keys are read from the environment by the proxy at runtime \u2014 never",
|
|
5102
|
+
"# stored in this file.",
|
|
5103
|
+
""
|
|
5104
|
+
].join("\n");
|
|
5105
|
+
}
|
|
5106
|
+
function defaultRouteTable(routeName) {
|
|
5107
|
+
return {
|
|
5108
|
+
[routeName]: {
|
|
5109
|
+
type: "stage_router",
|
|
5110
|
+
capable_target: CAPABLE_TARGET,
|
|
5111
|
+
efficient_target: EFFICIENT_TARGET,
|
|
5112
|
+
picker: "efficient_first",
|
|
5113
|
+
confidence_threshold: 0.5
|
|
5114
|
+
}
|
|
5115
|
+
};
|
|
5116
|
+
}
|
|
5117
|
+
function buildRoutesToml(routeName, backend, curatedRoutes = null) {
|
|
5118
|
+
const doc = {
|
|
5119
|
+
targets: {
|
|
5120
|
+
[CAPABLE_TARGET]: { id: backend.capableId },
|
|
5121
|
+
[EFFICIENT_TARGET]: { id: backend.efficientId }
|
|
5122
|
+
},
|
|
5123
|
+
routes: curatedRoutes ?? defaultRouteTable(routeName)
|
|
5124
|
+
};
|
|
5125
|
+
return policyHeader(backend, curatedRoutes ? "curated" : "builtin") + stringify(doc) + "\n";
|
|
5126
|
+
}
|
|
5127
|
+
function acceptPolicyTemplate(body, routeName) {
|
|
5128
|
+
let parsed;
|
|
5129
|
+
try {
|
|
5130
|
+
parsed = parse(body);
|
|
5131
|
+
} catch {
|
|
5132
|
+
return null;
|
|
5133
|
+
}
|
|
5134
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
5135
|
+
const doc = parsed;
|
|
5136
|
+
if ("targets" in doc) return null;
|
|
5137
|
+
const routes = doc.routes;
|
|
5138
|
+
if (typeof routes !== "object" || routes === null) return null;
|
|
5139
|
+
const table = routes;
|
|
5140
|
+
const route = table[routeName];
|
|
5141
|
+
if (typeof route !== "object" || route === null) return null;
|
|
5142
|
+
for (const key of ["capable_target", "efficient_target"]) {
|
|
5143
|
+
const ref = route[key];
|
|
5144
|
+
if (ref !== void 0 && ref !== CAPABLE_TARGET && ref !== EFFICIENT_TARGET) return null;
|
|
5145
|
+
}
|
|
5146
|
+
return table;
|
|
5147
|
+
}
|
|
5148
|
+
function acceptRoutesToml(body, routeName) {
|
|
5149
|
+
let parsed;
|
|
5150
|
+
try {
|
|
5151
|
+
parsed = parse(body);
|
|
5152
|
+
} catch {
|
|
5153
|
+
return null;
|
|
5154
|
+
}
|
|
5155
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
5156
|
+
const routes = parsed.routes;
|
|
5157
|
+
if (typeof routes !== "object" || routes === null) return null;
|
|
5158
|
+
if (!(routeName in routes)) return null;
|
|
5159
|
+
return body;
|
|
5160
|
+
}
|
|
5161
|
+
function buildSwitchyardArgv(bin, agent, routeName, configPath) {
|
|
5162
|
+
return [bin, "launch", agent, "--model", routeName, "--config", configPath];
|
|
5163
|
+
}
|
|
5164
|
+
function buildDirectArgv(agent) {
|
|
5165
|
+
return [agent];
|
|
5166
|
+
}
|
|
5167
|
+
var SIGNAL_NUMBERS = {
|
|
5168
|
+
SIGHUP: 1,
|
|
5169
|
+
SIGINT: 2,
|
|
5170
|
+
SIGQUIT: 3,
|
|
5171
|
+
SIGKILL: 9,
|
|
5172
|
+
SIGTERM: 15
|
|
5173
|
+
};
|
|
5174
|
+
function exitCodeFromChild(code, signal) {
|
|
5175
|
+
if (code !== null) return code;
|
|
5176
|
+
if (signal !== null) {
|
|
5177
|
+
const num2 = SIGNAL_NUMBERS[signal];
|
|
5178
|
+
return num2 !== void 0 ? 128 + num2 : 1;
|
|
5179
|
+
}
|
|
5180
|
+
return 1;
|
|
5181
|
+
}
|
|
5182
|
+
function classifyChildExit(code, signal, elapsedMs) {
|
|
5183
|
+
if (signal !== null) return "propagate";
|
|
5184
|
+
if (code === 0) return "propagate";
|
|
5185
|
+
return elapsedMs < STARTUP_GRACE_MS ? "fallback-eligible" : "propagate";
|
|
5186
|
+
}
|
|
5187
|
+
function buildInstallAnnouncement() {
|
|
5188
|
+
return `switchyard not found \u2014 installing via uv: ${SWITCHYARD_INSTALL_ONELINER}`;
|
|
5189
|
+
}
|
|
5190
|
+
function buildUvMissingMessage() {
|
|
5191
|
+
return `switchyard is not installed and \`uv\` is missing, so maind will not install it for you.
|
|
5192
|
+
maind: install uv (${UV_INSTALL_DOCS}), then run:
|
|
5193
|
+
maind: ${SWITCHYARD_INSTALL_ONELINER}
|
|
5194
|
+
maind: and re-run \`maind launch claude\`. (Or launch without routing: \`maind launch claude --direct\`.)`;
|
|
5195
|
+
}
|
|
5196
|
+
function buildInstallStillMissingMessage() {
|
|
5197
|
+
return "switchyard still not reachable after install \u2014 uv's bin dir may be missing from PATH (try `uv tool update-shell`, then open a new terminal).";
|
|
5198
|
+
}
|
|
5199
|
+
function buildFallbackBanner(reason) {
|
|
5200
|
+
return [
|
|
5201
|
+
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
5202
|
+
` switchyard proxy unavailable: ${reason}`,
|
|
5203
|
+
" launching claude DIRECTLY \u2014 no model routing for this session.",
|
|
5204
|
+
" (disable this fallback with --no-fallback)",
|
|
5205
|
+
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
5206
|
+
].join("\n");
|
|
5207
|
+
}
|
|
5208
|
+
function buildDirectBanner() {
|
|
5209
|
+
return "launching claude directly (--direct) \u2014 no switchyard proxy, no model routing.";
|
|
5210
|
+
}
|
|
5211
|
+
function renderDryRun(plan, json) {
|
|
5212
|
+
if (json) {
|
|
5213
|
+
const payload = {
|
|
5214
|
+
agent: plan.agent,
|
|
5215
|
+
config_path: plan.configPath,
|
|
5216
|
+
config_source: plan.configSource,
|
|
5217
|
+
toml: plan.toml,
|
|
5218
|
+
argv: plan.argv,
|
|
5219
|
+
backend: plan.backendKind,
|
|
5220
|
+
switchyard_detected: plan.switchyardDetected
|
|
5221
|
+
};
|
|
5222
|
+
return { stdout: JSON.stringify(payload, null, 2) + "\n", stderr: "" };
|
|
5223
|
+
}
|
|
5224
|
+
const stderr = [
|
|
5225
|
+
`maind: dry-run \u2014 nothing installed, written, or spawned.`,
|
|
5226
|
+
`maind: config ${plan.configPath} (${plan.configSource})`,
|
|
5227
|
+
`maind: switchyard detected: ${plan.switchyardDetected ? "yes" : "no"}`,
|
|
5228
|
+
`maind: would run: ${plan.argv.join(" ")}`
|
|
5229
|
+
].join("\n");
|
|
5230
|
+
return {
|
|
5231
|
+
stdout: plan.toml ?? `(using existing config: ${plan.configPath})
|
|
5232
|
+
`,
|
|
5233
|
+
stderr: stderr + "\n"
|
|
5234
|
+
};
|
|
5235
|
+
}
|
|
5236
|
+
|
|
5237
|
+
// src/version.ts
|
|
5238
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
5239
|
+
function resolveVersion() {
|
|
5240
|
+
if (true) return "0.10.0";
|
|
5241
|
+
try {
|
|
5242
|
+
const require2 = createRequire3(import.meta.url);
|
|
5243
|
+
return require2("../package.json").version;
|
|
5244
|
+
} catch {
|
|
5245
|
+
return "0.0.0-unknown";
|
|
5246
|
+
}
|
|
5247
|
+
}
|
|
5248
|
+
var VERSION = resolveVersion();
|
|
5249
|
+
|
|
5250
|
+
// src/launch-cmd.ts
|
|
5251
|
+
var TEMPLATE_FETCH_TIMEOUT_MS = 5e3;
|
|
5252
|
+
function err(line) {
|
|
5253
|
+
process.stderr.write(`maind: ${line}
|
|
5254
|
+
`);
|
|
5255
|
+
}
|
|
5256
|
+
function msg5(e) {
|
|
5257
|
+
return e instanceof Error ? e.message : String(e);
|
|
5258
|
+
}
|
|
5259
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set(["--config", "--route", "--mcp-url"]);
|
|
5260
|
+
function parseLaunchArgs(argv) {
|
|
5261
|
+
const get = (name) => {
|
|
5262
|
+
const i = argv.indexOf(name);
|
|
5263
|
+
if (i === -1 || i + 1 >= argv.length) return void 0;
|
|
5264
|
+
return argv[i + 1];
|
|
5265
|
+
};
|
|
5266
|
+
const has = (name) => argv.includes(name);
|
|
5267
|
+
let agent = null;
|
|
5268
|
+
for (let i = 0; i < argv.length; i++) {
|
|
5269
|
+
const tok = argv[i];
|
|
5270
|
+
if (tok.startsWith("-")) {
|
|
5271
|
+
if (VALUE_FLAGS.has(tok)) i++;
|
|
5272
|
+
continue;
|
|
5273
|
+
}
|
|
5274
|
+
agent = tok;
|
|
5275
|
+
break;
|
|
5276
|
+
}
|
|
5277
|
+
return {
|
|
5278
|
+
agent,
|
|
5279
|
+
dryRun: has("--dry-run"),
|
|
5280
|
+
config: get("--config") ?? null,
|
|
5281
|
+
route: get("--route") ?? DEFAULT_ROUTE,
|
|
5282
|
+
direct: has("--direct"),
|
|
5283
|
+
noFallback: has("--no-fallback"),
|
|
5284
|
+
noInstall: has("--no-install"),
|
|
5285
|
+
json: has("--json"),
|
|
5286
|
+
mcpUrl: get("--mcp-url") ?? null
|
|
5287
|
+
};
|
|
5288
|
+
}
|
|
5289
|
+
var realLaunchDeps = {
|
|
5290
|
+
probe(bin, args) {
|
|
5291
|
+
const res = spawnSync2(bin, args, {
|
|
5292
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5293
|
+
encoding: "utf8",
|
|
5294
|
+
timeout: 1e4
|
|
5295
|
+
});
|
|
5296
|
+
const ok = res.status === 0 && !res.error;
|
|
5297
|
+
const out2 = typeof res.stdout === "string" ? res.stdout.trim() : "";
|
|
5298
|
+
return { ok, stdout: out2.length > 0 ? out2 : null };
|
|
5299
|
+
},
|
|
5300
|
+
install(argv) {
|
|
5301
|
+
const res = spawnSync2("uv", [...argv], { stdio: ["ignore", "inherit", "inherit"] });
|
|
5302
|
+
return res.status;
|
|
5303
|
+
},
|
|
5304
|
+
spawnInteractive(argv) {
|
|
5305
|
+
const start = Date.now();
|
|
5306
|
+
return new Promise((resolve) => {
|
|
5307
|
+
let settled = false;
|
|
5308
|
+
const child = spawn(argv[0], argv.slice(1), { stdio: "inherit", env: process.env });
|
|
5309
|
+
const onSigint = () => {
|
|
5310
|
+
};
|
|
5311
|
+
const makeForward = (sig) => () => {
|
|
5312
|
+
try {
|
|
5313
|
+
child.kill(sig);
|
|
5314
|
+
} catch {
|
|
5315
|
+
}
|
|
5316
|
+
};
|
|
5317
|
+
const onSigterm = makeForward("SIGTERM");
|
|
5318
|
+
const onSighup = makeForward("SIGHUP");
|
|
5319
|
+
process.on("SIGINT", onSigint);
|
|
5320
|
+
process.on("SIGTERM", onSigterm);
|
|
5321
|
+
process.on("SIGHUP", onSighup);
|
|
5322
|
+
const cleanup = () => {
|
|
5323
|
+
process.removeListener("SIGINT", onSigint);
|
|
5324
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
5325
|
+
process.removeListener("SIGHUP", onSighup);
|
|
5326
|
+
};
|
|
5327
|
+
child.on("error", (e) => {
|
|
5328
|
+
if (settled) return;
|
|
5329
|
+
settled = true;
|
|
5330
|
+
cleanup();
|
|
5331
|
+
resolve({ code: null, signal: null, elapsedMs: Date.now() - start, spawnError: e.message });
|
|
5332
|
+
});
|
|
5333
|
+
child.on("exit", (code, signal) => {
|
|
5334
|
+
if (settled) return;
|
|
5335
|
+
settled = true;
|
|
5336
|
+
cleanup();
|
|
5337
|
+
resolve({ code, signal, elapsedMs: Date.now() - start, spawnError: null });
|
|
5338
|
+
});
|
|
5339
|
+
});
|
|
5340
|
+
},
|
|
5341
|
+
async fetchTemplate(mcpUrl, projectKey) {
|
|
5342
|
+
let auth;
|
|
5343
|
+
try {
|
|
5344
|
+
auth = resolveAuth();
|
|
5345
|
+
} catch (e) {
|
|
5346
|
+
if (e instanceof MissingApiKeyError) return null;
|
|
5347
|
+
throw e;
|
|
5348
|
+
}
|
|
5349
|
+
try {
|
|
5350
|
+
const client = new McpClient({
|
|
5351
|
+
apiKey: auth.apiKey,
|
|
5352
|
+
deviceId: auth.deviceId,
|
|
5353
|
+
clientVersion: VERSION,
|
|
5354
|
+
mcpUrl: mcpUrl ?? void 0,
|
|
5355
|
+
projectKey
|
|
5356
|
+
});
|
|
5357
|
+
const timeout = new Promise((resolve) => {
|
|
5358
|
+
const t = setTimeout(() => resolve(null), TEMPLATE_FETCH_TIMEOUT_MS);
|
|
5359
|
+
t.unref();
|
|
5360
|
+
});
|
|
5361
|
+
const call = client.callTool("get_template", { template_id: LAUNCH_TEMPLATE_ID }).then(
|
|
5362
|
+
(r) => typeof r.template_body === "string" && r.template_body.trim().length > 0 ? r.template_body : null
|
|
5363
|
+
).catch(() => null);
|
|
5364
|
+
return await Promise.race([call, timeout]);
|
|
5365
|
+
} catch {
|
|
5366
|
+
return null;
|
|
5367
|
+
}
|
|
5368
|
+
},
|
|
5369
|
+
writeConfig(path, content) {
|
|
5370
|
+
const dir = dirname4(path);
|
|
5371
|
+
if (dir && dir !== ".") mkdirSync4(dir, { recursive: true });
|
|
5372
|
+
writeFileSync4(path, content, "utf8");
|
|
5373
|
+
},
|
|
5374
|
+
readFile(path) {
|
|
5375
|
+
try {
|
|
5376
|
+
return readFileSync6(path, "utf8");
|
|
5377
|
+
} catch {
|
|
5378
|
+
return null;
|
|
5379
|
+
}
|
|
5380
|
+
},
|
|
5381
|
+
fileExists(path) {
|
|
5382
|
+
return existsSync5(path);
|
|
5383
|
+
},
|
|
5384
|
+
repoRoot() {
|
|
5385
|
+
try {
|
|
5386
|
+
const out2 = execFileSync6("git", ["rev-parse", "--show-toplevel"], {
|
|
5387
|
+
encoding: "utf8",
|
|
5388
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
5389
|
+
timeout: 5e3
|
|
5390
|
+
}).trim();
|
|
5391
|
+
return out2.length > 0 ? out2 : null;
|
|
5392
|
+
} catch {
|
|
5393
|
+
return null;
|
|
5394
|
+
}
|
|
5395
|
+
},
|
|
5396
|
+
projectKey() {
|
|
5397
|
+
return gatherGitState().projectKey;
|
|
5398
|
+
},
|
|
5399
|
+
userConfigDir() {
|
|
5400
|
+
return configDir();
|
|
5401
|
+
},
|
|
5402
|
+
homeDir() {
|
|
5403
|
+
return homedir2();
|
|
5404
|
+
},
|
|
5405
|
+
env: process.env,
|
|
5406
|
+
platform: process.platform
|
|
5407
|
+
};
|
|
5408
|
+
function resolveRoutesPath(repoRoot2, userConfigDir) {
|
|
5409
|
+
return repoRoot2 !== null ? join5(repoRoot2, ".maind", ROUTES_FILENAME) : join5(userConfigDir, ROUTES_FILENAME);
|
|
5410
|
+
}
|
|
5411
|
+
async function runLaunch(opts, deps = realLaunchDeps) {
|
|
5412
|
+
const verdict = classifyAgent(opts.agent);
|
|
5413
|
+
if (verdict.kind === "unknown") {
|
|
5414
|
+
err(
|
|
5415
|
+
`usage: maind launch <agent> [--dry-run] [--config <path>] [--route <name>] [--direct] [--no-fallback] [--no-install] \u2014 supported: ${SUPPORTED_AGENTS.join(", ")}`
|
|
5416
|
+
);
|
|
5417
|
+
return 2;
|
|
5418
|
+
}
|
|
5419
|
+
if (verdict.kind === "planned") {
|
|
5420
|
+
err(
|
|
5421
|
+
`agent '${verdict.agent}' is planned but not yet supported \u2014 v1 supports: ${SUPPORTED_AGENTS.join(", ")}.`
|
|
5422
|
+
);
|
|
5423
|
+
return 2;
|
|
5424
|
+
}
|
|
5425
|
+
if (opts.json && !opts.dryRun) {
|
|
5426
|
+
err("--json is only valid together with --dry-run (stdout belongs to the agent session).");
|
|
5427
|
+
return 2;
|
|
5428
|
+
}
|
|
5429
|
+
const agent = verdict.agent;
|
|
5430
|
+
if (opts.direct) {
|
|
5431
|
+
err(buildDirectBanner());
|
|
5432
|
+
return spawnAndPropagate(deps, buildDirectArgv(agent));
|
|
5433
|
+
}
|
|
5434
|
+
const backend = chooseBackend(deps.env);
|
|
5435
|
+
if (backend.warning !== null) err(backend.warning);
|
|
5436
|
+
let configPath;
|
|
5437
|
+
let toml;
|
|
5438
|
+
let source;
|
|
5439
|
+
if (opts.config !== null) {
|
|
5440
|
+
if (!deps.fileExists(opts.config)) {
|
|
5441
|
+
err(`config not found: ${opts.config}`);
|
|
5442
|
+
return 2;
|
|
5443
|
+
}
|
|
5444
|
+
const body = deps.readFile(opts.config);
|
|
5445
|
+
if (body === null || acceptRoutesToml(body, opts.route) === null) {
|
|
5446
|
+
err(`config ${opts.config} is not valid routes TOML with a [routes.${opts.route}] entry.`);
|
|
5447
|
+
return 2;
|
|
5448
|
+
}
|
|
5449
|
+
configPath = opts.config;
|
|
5450
|
+
toml = null;
|
|
5451
|
+
source = "flag";
|
|
5452
|
+
} else {
|
|
5453
|
+
const template = await deps.fetchTemplate(opts.mcpUrl, deps.projectKey());
|
|
5454
|
+
const curatedRoutes = template === null ? null : acceptPolicyTemplate(template, opts.route);
|
|
5455
|
+
toml = buildRoutesToml(opts.route, backend, curatedRoutes);
|
|
5456
|
+
if (curatedRoutes !== null) {
|
|
5457
|
+
source = "template";
|
|
5458
|
+
} else {
|
|
5459
|
+
source = "builtin";
|
|
5460
|
+
const reason = template === null ? "no curated template (offline, unauthenticated, or not published)" : `curated template unusable (missing route '${opts.route}', or it pins its own targets)`;
|
|
5461
|
+
err(`using builtin routing policy (${reason}).`);
|
|
5462
|
+
}
|
|
5463
|
+
configPath = resolveRoutesPath(deps.repoRoot(), deps.userConfigDir());
|
|
5464
|
+
}
|
|
5465
|
+
let bin = SWITCHYARD_BIN;
|
|
5466
|
+
let detected = deps.probe(SWITCHYARD_BIN, ["--version"]).ok;
|
|
5467
|
+
if (opts.dryRun) {
|
|
5468
|
+
const plan = {
|
|
5469
|
+
agent,
|
|
5470
|
+
configPath,
|
|
5471
|
+
configSource: source,
|
|
5472
|
+
toml,
|
|
5473
|
+
argv: buildSwitchyardArgv(bin, agent, opts.route, configPath),
|
|
5474
|
+
backendKind: backend.kind,
|
|
5475
|
+
switchyardDetected: detected
|
|
5476
|
+
};
|
|
5477
|
+
const out2 = renderDryRun(plan, opts.json);
|
|
5478
|
+
if (out2.stderr.length > 0) process.stderr.write(out2.stderr);
|
|
5479
|
+
process.stdout.write(out2.stdout);
|
|
5480
|
+
return 0;
|
|
5481
|
+
}
|
|
5482
|
+
if (!detected) {
|
|
5483
|
+
if (opts.noInstall) {
|
|
5484
|
+
err(`switchyard not found (--no-install). Install it with:
|
|
5485
|
+
maind: ${SWITCHYARD_INSTALL_ONELINER}`);
|
|
5486
|
+
return 1;
|
|
5487
|
+
}
|
|
5488
|
+
if (!deps.probe("uv", ["--version"]).ok) {
|
|
5489
|
+
err(buildUvMissingMessage());
|
|
5490
|
+
return 1;
|
|
5491
|
+
}
|
|
5492
|
+
err(buildInstallAnnouncement());
|
|
5493
|
+
deps.install(SWITCHYARD_INSTALL_ARGV);
|
|
5494
|
+
if (deps.probe(SWITCHYARD_BIN, ["--version"]).ok) {
|
|
5495
|
+
detected = true;
|
|
5496
|
+
} else {
|
|
5497
|
+
const shim = join5(
|
|
5498
|
+
deps.homeDir(),
|
|
5499
|
+
".local",
|
|
5500
|
+
"bin",
|
|
5501
|
+
deps.platform === "win32" ? "switchyard.exe" : "switchyard"
|
|
5502
|
+
);
|
|
5503
|
+
if (deps.probe(shim, ["--version"]).ok) {
|
|
5504
|
+
bin = shim;
|
|
5505
|
+
detected = true;
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
if (!detected) {
|
|
5509
|
+
return degradeOrFail(deps, opts, agent, buildInstallStillMissingMessage());
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
if (source !== "flag" && toml !== null) {
|
|
5513
|
+
try {
|
|
5514
|
+
deps.writeConfig(configPath, toml);
|
|
5515
|
+
} catch (e) {
|
|
5516
|
+
err(`could not write ${configPath}: ${msg5(e)}`);
|
|
5517
|
+
return 1;
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
const result = await deps.spawnInteractive(buildSwitchyardArgv(bin, agent, opts.route, configPath));
|
|
5521
|
+
if (result.spawnError !== null) {
|
|
5522
|
+
return degradeOrFail(deps, opts, agent, `could not start switchyard (${result.spawnError})`);
|
|
5523
|
+
}
|
|
5524
|
+
if (classifyChildExit(result.code, result.signal, result.elapsedMs) === "fallback-eligible") {
|
|
5525
|
+
return degradeOrFail(
|
|
5526
|
+
deps,
|
|
5527
|
+
opts,
|
|
5528
|
+
agent,
|
|
5529
|
+
`switchyard exited with code ${result.code} after ${(result.elapsedMs / 1e3).toFixed(1)}s`
|
|
5530
|
+
);
|
|
5531
|
+
}
|
|
5532
|
+
return exitCodeFromChild(result.code, result.signal);
|
|
5533
|
+
}
|
|
5534
|
+
async function degradeOrFail(deps, opts, agent, reason) {
|
|
5535
|
+
if (opts.noFallback) {
|
|
5536
|
+
err(`${reason} (--no-fallback set \u2014 not degrading to a direct spawn).`);
|
|
5537
|
+
return 1;
|
|
5538
|
+
}
|
|
5539
|
+
process.stderr.write(buildFallbackBanner(reason) + "\n");
|
|
5540
|
+
return spawnAndPropagate(deps, buildDirectArgv(agent));
|
|
5541
|
+
}
|
|
5542
|
+
async function spawnAndPropagate(deps, argv) {
|
|
5543
|
+
const result = await deps.spawnInteractive(argv);
|
|
5544
|
+
if (result.spawnError !== null) {
|
|
5545
|
+
err(`'${argv[0]}' not found on PATH \u2014 install the agent CLI first (${result.spawnError}).`);
|
|
5546
|
+
return 1;
|
|
5547
|
+
}
|
|
5548
|
+
return exitCodeFromChild(result.code, result.signal);
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
// src/review.ts
|
|
5552
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
5553
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
5554
|
+
import { dirname as dirname5 } from "node:path";
|
|
5555
|
+
|
|
5556
|
+
// ../../packages/code-index/build/index.js
|
|
5557
|
+
var CODE_INDEX_VERSION = "0.1.0";
|
|
5558
|
+
|
|
5559
|
+
// src/review-render.ts
|
|
5560
|
+
var COMMENT_MARKER = "<!-- maind-review -->";
|
|
5561
|
+
var COMMENT_FINDINGS_CAP = 30;
|
|
5562
|
+
var FOOTER = "maind review \xB7 deterministic, provenance-backed";
|
|
5563
|
+
function buildReviewManifest(data, verdict, meta) {
|
|
5564
|
+
const review = data.review;
|
|
5565
|
+
return {
|
|
5566
|
+
schema: "maind.review.manifest/v1",
|
|
5567
|
+
generated_at: meta.generatedAt,
|
|
5568
|
+
key_prefix: meta.keyPrefix,
|
|
5569
|
+
run_ref: meta.runRef,
|
|
5570
|
+
base: meta.base,
|
|
5571
|
+
diff_stats: meta.diffStats,
|
|
5572
|
+
symbol_count: meta.symbolCount,
|
|
5573
|
+
risk_flags: review?.risk_flags ?? [],
|
|
5574
|
+
findings: (review?.convention_findings ?? []).map((f) => ({
|
|
5575
|
+
convention_id: f.convention_id,
|
|
5576
|
+
version: f.convention_version,
|
|
5577
|
+
severity: f.severity,
|
|
5578
|
+
grade: f.grade,
|
|
5579
|
+
matched_symbol_count: f.matched_symbols.length,
|
|
5580
|
+
matched_changed_symbol_count: f.matched_changed_symbols?.length ?? 0
|
|
5581
|
+
})),
|
|
5582
|
+
served: (review?.lesson_hints ?? []).map((l) => ({
|
|
5583
|
+
id: l.id,
|
|
5584
|
+
version: typeof l.version === "number" ? l.version : null,
|
|
3448
5585
|
tier: l.tier ?? null
|
|
3449
5586
|
})),
|
|
3450
5587
|
verdict: verdict?.ok === true && typeof verdict.verdict === "string" ? {
|
|
@@ -3500,132 +5637,132 @@ function renderReviewMarkdown(data, verdict, meta) {
|
|
|
3500
5637
|
const findings = review?.convention_findings ?? [];
|
|
3501
5638
|
const hints = review?.lesson_hints ?? [];
|
|
3502
5639
|
const riskFlags = review?.risk_flags ?? [];
|
|
3503
|
-
const
|
|
3504
|
-
|
|
3505
|
-
|
|
5640
|
+
const lines2 = [];
|
|
5641
|
+
lines2.push("<!-- Generated by `maind review` \u2014 do not edit by hand. -->");
|
|
5642
|
+
lines2.push(
|
|
3506
5643
|
`<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`
|
|
3507
5644
|
);
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
5645
|
+
lines2.push("");
|
|
5646
|
+
lines2.push("# maind review");
|
|
5647
|
+
lines2.push("");
|
|
5648
|
+
lines2.push(
|
|
3512
5649
|
`Reviewed ${baseLabel(meta)}: ${meta.symbolCount} changed symbol(s) across ${meta.diffStats?.changed_file_count ?? 0} file(s) \xB7 findings: ${findings.length} \xB7 risk flags: ${riskFlags.length} \xB7 lesson hints: ${hints.length}`
|
|
3513
5650
|
);
|
|
3514
|
-
|
|
5651
|
+
lines2.push("");
|
|
3515
5652
|
if (verdict?.ok === true && verdict.verdict) {
|
|
3516
|
-
|
|
5653
|
+
lines2.push(verdictLabel(verdict));
|
|
3517
5654
|
const attest = attestationLine(verdict);
|
|
3518
5655
|
if (attest) {
|
|
3519
|
-
|
|
3520
|
-
|
|
5656
|
+
lines2.push("");
|
|
5657
|
+
lines2.push(attest);
|
|
3521
5658
|
}
|
|
3522
5659
|
if (verdict.trust_note) {
|
|
3523
|
-
|
|
3524
|
-
|
|
5660
|
+
lines2.push("");
|
|
5661
|
+
lines2.push(`_${verdict.trust_note}_`);
|
|
3525
5662
|
}
|
|
3526
|
-
|
|
5663
|
+
lines2.push("");
|
|
3527
5664
|
}
|
|
3528
5665
|
if (findings.length > 0) {
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
for (const f of findings)
|
|
3532
|
-
|
|
5666
|
+
lines2.push("## Convention findings");
|
|
5667
|
+
lines2.push("");
|
|
5668
|
+
for (const f of findings) lines2.push(findingLine(f));
|
|
5669
|
+
lines2.push("");
|
|
3533
5670
|
} else {
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
5671
|
+
lines2.push("## Convention findings");
|
|
5672
|
+
lines2.push("");
|
|
5673
|
+
lines2.push("_No convention findings for this change set._");
|
|
5674
|
+
lines2.push("");
|
|
3538
5675
|
}
|
|
3539
5676
|
if (riskFlags.length > 0) {
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
5677
|
+
lines2.push("## Risk flags");
|
|
5678
|
+
lines2.push("");
|
|
5679
|
+
lines2.push(
|
|
3543
5680
|
"_Path heuristics from the local diff \u2014 informational, they never block._"
|
|
3544
5681
|
);
|
|
3545
|
-
|
|
3546
|
-
for (const flag2 of riskFlags)
|
|
3547
|
-
|
|
5682
|
+
lines2.push("");
|
|
5683
|
+
for (const flag2 of riskFlags) lines2.push(`- \`${flag2}\``);
|
|
5684
|
+
lines2.push("");
|
|
3548
5685
|
}
|
|
3549
5686
|
if (hints.length > 0) {
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
5687
|
+
lines2.push("## Lesson hints");
|
|
5688
|
+
lines2.push("");
|
|
5689
|
+
lines2.push("_Semantic suggestions \u2014 advisory, never part of a verdict._");
|
|
5690
|
+
lines2.push("");
|
|
3554
5691
|
for (const h of hints) {
|
|
3555
5692
|
const parts = [`\`${h.id}\``];
|
|
3556
5693
|
if (h.tier) parts.push(h.tier);
|
|
3557
5694
|
if (h.match_reason) parts.push(h.match_reason);
|
|
3558
|
-
|
|
5695
|
+
lines2.push(`- **${h.title}** (${parts.join(" \xB7 ")})`);
|
|
3559
5696
|
}
|
|
3560
|
-
|
|
5697
|
+
lines2.push("");
|
|
3561
5698
|
}
|
|
3562
5699
|
const rc = review?.regression_context;
|
|
3563
5700
|
if (rc && (rc.hot_files ?? 0) > 0) {
|
|
3564
|
-
|
|
3565
|
-
|
|
5701
|
+
lines2.push("");
|
|
5702
|
+
lines2.push(
|
|
3566
5703
|
`_History: ${rc.hot_files}/${rc.files_scanned ?? "?"} changed file(s) high-churn (max ${rc.max_recent_commits ?? "?"} commits in the last ${rc.window_commits ?? "?"}-commit window, ${rc.distinct_authors ?? "?"} author(s))._`
|
|
3567
5704
|
);
|
|
3568
5705
|
}
|
|
3569
5706
|
if (review?.coverage_note) {
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
5707
|
+
lines2.push("## Coverage");
|
|
5708
|
+
lines2.push("");
|
|
5709
|
+
lines2.push(`> ${review.coverage_note}`);
|
|
5710
|
+
lines2.push("");
|
|
3574
5711
|
}
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
return
|
|
5712
|
+
lines2.push("---");
|
|
5713
|
+
lines2.push(FOOTER);
|
|
5714
|
+
lines2.push("");
|
|
5715
|
+
return lines2.join("\n");
|
|
3579
5716
|
}
|
|
3580
5717
|
function renderReviewComment(data, verdict, meta, reportPath) {
|
|
3581
5718
|
const review = data.review;
|
|
3582
5719
|
const findings = review?.convention_findings ?? [];
|
|
3583
5720
|
const riskFlags = review?.risk_flags ?? [];
|
|
3584
5721
|
const hints = review?.lesson_hints ?? [];
|
|
3585
|
-
const
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
5722
|
+
const lines2 = [];
|
|
5723
|
+
lines2.push(COMMENT_MARKER);
|
|
5724
|
+
lines2.push("## maind review");
|
|
5725
|
+
lines2.push("");
|
|
3589
5726
|
if (verdict?.ok === true && verdict.verdict) {
|
|
3590
|
-
|
|
5727
|
+
lines2.push(verdictLabel(verdict));
|
|
3591
5728
|
const attest = attestationLine(verdict);
|
|
3592
|
-
if (attest)
|
|
3593
|
-
|
|
5729
|
+
if (attest) lines2.push(attest);
|
|
5730
|
+
lines2.push("");
|
|
3594
5731
|
}
|
|
3595
5732
|
if (findings.length === 0) {
|
|
3596
|
-
|
|
5733
|
+
lines2.push(
|
|
3597
5734
|
`No convention findings for this change set (${meta.symbolCount} changed symbol(s) reviewed ${baseLabel(meta)}).`
|
|
3598
5735
|
);
|
|
3599
|
-
|
|
5736
|
+
lines2.push("");
|
|
3600
5737
|
} else {
|
|
3601
5738
|
const shown = findings.slice(0, COMMENT_FINDINGS_CAP);
|
|
3602
|
-
for (const f of shown)
|
|
5739
|
+
for (const f of shown) lines2.push(findingLine(f));
|
|
3603
5740
|
if (findings.length > shown.length) {
|
|
3604
|
-
|
|
3605
|
-
|
|
5741
|
+
lines2.push("");
|
|
5742
|
+
lines2.push(
|
|
3606
5743
|
`\u2026and ${findings.length - shown.length} more finding(s) \u2014 see the \`${reportPath}\` build artifact.`
|
|
3607
5744
|
);
|
|
3608
5745
|
}
|
|
3609
|
-
|
|
5746
|
+
lines2.push("");
|
|
3610
5747
|
}
|
|
3611
5748
|
if (riskFlags.length > 0) {
|
|
3612
|
-
|
|
5749
|
+
lines2.push(
|
|
3613
5750
|
`**Risk flags:** ${riskFlags.map((f) => `\`${f}\``).join(" \xB7 ")} _(heuristics, never blocking)_`
|
|
3614
5751
|
);
|
|
3615
|
-
|
|
5752
|
+
lines2.push("");
|
|
3616
5753
|
}
|
|
3617
5754
|
if (hints.length > 0) {
|
|
3618
|
-
|
|
3619
|
-
|
|
5755
|
+
lines2.push("**Related lessons:** " + hints.map((h) => `\`${h.id}\``).join(" \xB7 "));
|
|
5756
|
+
lines2.push("");
|
|
3620
5757
|
}
|
|
3621
5758
|
if (review?.coverage_note) {
|
|
3622
|
-
|
|
3623
|
-
|
|
5759
|
+
lines2.push(`> ${review.coverage_note}`);
|
|
5760
|
+
lines2.push("");
|
|
3624
5761
|
}
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
return
|
|
5762
|
+
lines2.push("---");
|
|
5763
|
+
lines2.push(`_${FOOTER}_`);
|
|
5764
|
+
lines2.push("");
|
|
5765
|
+
return lines2.join("\n");
|
|
3629
5766
|
}
|
|
3630
5767
|
function renderFailOpenReview(meta, reason) {
|
|
3631
5768
|
return [
|
|
@@ -3655,7 +5792,7 @@ function renderFailOpenComment(meta, reason) {
|
|
|
3655
5792
|
|
|
3656
5793
|
// src/review.ts
|
|
3657
5794
|
var PROJECT_KEY_RE3 = /^[a-f0-9]{64}$/;
|
|
3658
|
-
var CLI_CLIENT_VERSION =
|
|
5795
|
+
var CLI_CLIENT_VERSION = VERSION;
|
|
3659
5796
|
var DEFAULT_COMMENT_PATH = ".maind/review-comment.md";
|
|
3660
5797
|
var SYMBOL_CAP = 64;
|
|
3661
5798
|
function parseReviewArgs(argv) {
|
|
@@ -3694,12 +5831,12 @@ function nowIso3() {
|
|
|
3694
5831
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
3695
5832
|
}
|
|
3696
5833
|
function writeArtifact3(path, content) {
|
|
3697
|
-
const dir =
|
|
3698
|
-
if (dir && dir !== ".")
|
|
3699
|
-
|
|
5834
|
+
const dir = dirname5(path);
|
|
5835
|
+
if (dir && dir !== ".") mkdirSync5(dir, { recursive: true });
|
|
5836
|
+
writeFileSync5(path, content, "utf8");
|
|
3700
5837
|
}
|
|
3701
|
-
function manifestPathFor(
|
|
3702
|
-
return
|
|
5838
|
+
function manifestPathFor(out2) {
|
|
5839
|
+
return out2.endsWith(".md") ? out2.slice(0, -3) + ".json" : out2 + ".json";
|
|
3703
5840
|
}
|
|
3704
5841
|
function verdictCrossesFailOn(verdict, failOn) {
|
|
3705
5842
|
return failOn === "block" ? verdict === "block" : verdict === "block" || verdict === "warn";
|
|
@@ -3721,7 +5858,7 @@ function buildDiffStats(result) {
|
|
|
3721
5858
|
}
|
|
3722
5859
|
function resolveRepoRoot() {
|
|
3723
5860
|
try {
|
|
3724
|
-
return
|
|
5861
|
+
return execFileSync7("git", ["rev-parse", "--show-toplevel"], {
|
|
3725
5862
|
encoding: "utf8",
|
|
3726
5863
|
stdio: ["ignore", "pipe", "ignore"]
|
|
3727
5864
|
}).trim();
|
|
@@ -3744,25 +5881,27 @@ async function runReview(opts) {
|
|
|
3744
5881
|
if (opts.failOn && !opts.gate) {
|
|
3745
5882
|
warn5("--fail-on has no effect without --gate (advisory runs always exit 0).");
|
|
3746
5883
|
}
|
|
3747
|
-
const failOpen = (reason) => {
|
|
3748
|
-
|
|
5884
|
+
const failOpen = (reason, klass = "infra") => {
|
|
5885
|
+
const hard = opts.strict && klass === "infra";
|
|
5886
|
+
warn5(`review unavailable, ${hard ? "failing (--strict)" : "continuing"}: ${reason}`);
|
|
3749
5887
|
try {
|
|
3750
5888
|
writeArtifact3(opts.out, renderFailOpenReview(meta, reason));
|
|
3751
5889
|
const manifest = buildReviewManifest({}, null, meta);
|
|
3752
5890
|
manifest.error = reason;
|
|
5891
|
+
manifest.error_class = klass;
|
|
3753
5892
|
writeArtifact3(manifestPathFor(opts.out), JSON.stringify(manifest, null, 2) + "\n");
|
|
3754
5893
|
if (opts.comment) writeArtifact3(opts.comment, renderFailOpenComment(meta, reason));
|
|
3755
5894
|
if (opts.json) process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
|
|
3756
5895
|
} catch (writeErr) {
|
|
3757
|
-
warn5(`could not write fail-open artifacts: ${
|
|
5896
|
+
warn5(`could not write fail-open artifacts: ${msg6(writeErr)}`);
|
|
3758
5897
|
return opts.strict ? 2 : 1;
|
|
3759
5898
|
}
|
|
3760
|
-
return
|
|
5899
|
+
return hard ? 2 : 0;
|
|
3761
5900
|
};
|
|
3762
5901
|
try {
|
|
3763
5902
|
const root = resolveRepoRoot();
|
|
3764
5903
|
if (!root) {
|
|
3765
|
-
return failOpen("not a git repository \u2014 review is diff-based.");
|
|
5904
|
+
return failOpen("not a git repository \u2014 review is diff-based.", "expected");
|
|
3766
5905
|
}
|
|
3767
5906
|
const engine = createNodeCodeIndex({ repoRoot: root });
|
|
3768
5907
|
let result;
|
|
@@ -3843,7 +5982,8 @@ async function runReview(opts) {
|
|
|
3843
5982
|
const review = await client.callTool("review_changes", reviewArgs);
|
|
3844
5983
|
if (review.ok !== true || !review.review) {
|
|
3845
5984
|
return failOpen(
|
|
3846
|
-
`review_changes rejected: ${review.error ?? "unknown_error"}${review.message ? ` \u2014 ${review.message}` : ""}
|
|
5985
|
+
`review_changes rejected: ${review.error ?? "unknown_error"}${review.message ? ` \u2014 ${review.message}` : ""}`,
|
|
5986
|
+
classifyServerError(review.error)
|
|
3847
5987
|
);
|
|
3848
5988
|
}
|
|
3849
5989
|
let verdict = null;
|
|
@@ -3875,9 +6015,11 @@ async function runReview(opts) {
|
|
|
3875
6015
|
);
|
|
3876
6016
|
verdict = null;
|
|
3877
6017
|
} else {
|
|
3878
|
-
|
|
6018
|
+
const klass = classifyServerError(verdict.error);
|
|
6019
|
+
if (opts.strict && klass === "infra") {
|
|
3879
6020
|
return failOpen(
|
|
3880
|
-
`review_verdict failed: ${verdict.error ?? "unknown_error"}${verdict.message ? ` \u2014 ${verdict.message}` : ""}
|
|
6021
|
+
`review_verdict failed: ${verdict.error ?? "unknown_error"}${verdict.message ? ` \u2014 ${verdict.message}` : ""}`,
|
|
6022
|
+
klass
|
|
3881
6023
|
);
|
|
3882
6024
|
}
|
|
3883
6025
|
warn5(
|
|
@@ -3886,9 +6028,9 @@ async function runReview(opts) {
|
|
|
3886
6028
|
verdict = null;
|
|
3887
6029
|
}
|
|
3888
6030
|
}
|
|
3889
|
-
} catch (
|
|
3890
|
-
if (opts.strict) return failOpen(`review_verdict failed: ${
|
|
3891
|
-
warn5(`gate unavailable (continuing advisory): ${
|
|
6031
|
+
} catch (err4) {
|
|
6032
|
+
if (opts.strict) return failOpen(`review_verdict failed: ${msg6(err4)}`, "infra");
|
|
6033
|
+
warn5(`gate unavailable (continuing advisory): ${msg6(err4)}`);
|
|
3892
6034
|
verdict = null;
|
|
3893
6035
|
}
|
|
3894
6036
|
}
|
|
@@ -3909,10 +6051,21 @@ async function runReview(opts) {
|
|
|
3909
6051
|
}
|
|
3910
6052
|
}
|
|
3911
6053
|
return 0;
|
|
3912
|
-
} catch (
|
|
3913
|
-
|
|
3914
|
-
return failOpen(
|
|
3915
|
-
}
|
|
6054
|
+
} catch (err4) {
|
|
6055
|
+
if (err4 instanceof MissingApiKeyError) return failOpen(err4.message, "expected");
|
|
6056
|
+
return failOpen(msg6(err4), "infra");
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
var EXPECTED_SERVER_ERRORS = /* @__PURE__ */ new Set([
|
|
6060
|
+
"unauthorized",
|
|
6061
|
+
"invalid_api_key",
|
|
6062
|
+
"requires_team",
|
|
6063
|
+
"requires_enterprise",
|
|
6064
|
+
"scope_not_in_plan",
|
|
6065
|
+
"rate_limited"
|
|
6066
|
+
]);
|
|
6067
|
+
function classifyServerError(code) {
|
|
6068
|
+
return code && EXPECTED_SERVER_ERRORS.has(code) ? "expected" : "infra";
|
|
3916
6069
|
}
|
|
3917
6070
|
function writeAll(review, verdict, meta, opts) {
|
|
3918
6071
|
writeArtifact3(opts.out, renderReviewMarkdown(review, verdict, meta));
|
|
@@ -3923,25 +6076,524 @@ function writeAll(review, verdict, meta, opts) {
|
|
|
3923
6076
|
}
|
|
3924
6077
|
if (opts.json) process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
|
|
3925
6078
|
}
|
|
3926
|
-
function
|
|
3927
|
-
return
|
|
6079
|
+
function msg6(err4) {
|
|
6080
|
+
return err4 instanceof Error ? err4.message : String(err4);
|
|
3928
6081
|
}
|
|
3929
6082
|
function warn5(line) {
|
|
3930
6083
|
process.stderr.write(`maind review: ${line}
|
|
3931
6084
|
`);
|
|
3932
6085
|
}
|
|
3933
6086
|
|
|
6087
|
+
// ../../packages/claude-code-telemetry/dist/session-analysis.js
|
|
6088
|
+
import { createReadStream, existsSync as existsSync6 } from "node:fs";
|
|
6089
|
+
import { createInterface } from "node:readline";
|
|
6090
|
+
|
|
6091
|
+
// ../../packages/claude-code-telemetry/dist/effort.js
|
|
6092
|
+
var LEVELS = /* @__PURE__ */ new Set([
|
|
6093
|
+
"low",
|
|
6094
|
+
"medium",
|
|
6095
|
+
"high",
|
|
6096
|
+
"xhigh",
|
|
6097
|
+
"max"
|
|
6098
|
+
]);
|
|
6099
|
+
var DIRECTIVE_RE = /(?:^|\s)\/(low|medium|high|xhigh|max)(?=\s|$|[!?.,;:])/gi;
|
|
6100
|
+
function detectEffortLevel(messageText) {
|
|
6101
|
+
if (!messageText)
|
|
6102
|
+
return null;
|
|
6103
|
+
let last = null;
|
|
6104
|
+
for (const match of messageText.matchAll(DIRECTIVE_RE)) {
|
|
6105
|
+
const lvl = match[1].toLowerCase();
|
|
6106
|
+
if (LEVELS.has(lvl))
|
|
6107
|
+
last = lvl;
|
|
6108
|
+
}
|
|
6109
|
+
return last;
|
|
6110
|
+
}
|
|
6111
|
+
|
|
6112
|
+
// ../../packages/claude-code-telemetry/dist/session-analysis.js
|
|
6113
|
+
var ANALYZER_VERSION = "2026-08-04";
|
|
6114
|
+
var REWORK_RE = /(\brückgängig\b|zurücknehmen|\bnochmal(s)?\b|noch mal|\brevert\b|\bundo\b|\bredo\b|stattdessen|\binstead\b|mach(e)? das (nochmal|neu)|try again)/i;
|
|
6115
|
+
var CORRECTION_RE = /(\bnein\b|\bfalsch\b|stimmt nicht|das ist nicht|so nicht|\bnicht so\b|warum hast du|wieso hast du|\bno\b|\bnope\b|\bwrong\b|\bincorrect\b|that'?s not|that is not|\bdon'?t\b|\bdoesn'?t\b|shouldn'?t)/i;
|
|
6116
|
+
var ACCEPTANCE_RE = /(\bdanke\b|\bperfekt\b|\bsuper\b|\bpasst\b|\bgenau\b|sieht gut|\bcommit\b|\bship\b|\bmerge\b|\bpush\b|looks good|\blgtm\b|\bthanks\b|thank you|\bperfect\b|\bgreat\b|\bexactly\b|\bnice\b|well done|👍|✅|🎉)/i;
|
|
6117
|
+
var INTERRUPT_MARKER = "[Request interrupted by user";
|
|
6118
|
+
var DENIAL_MARKER = "The user doesn't want to proceed with this tool use";
|
|
6119
|
+
var SYNTHETIC_MODEL = "<synthetic>";
|
|
6120
|
+
var MOMENTUM_DECAY = 0.7;
|
|
6121
|
+
var MAG = {
|
|
6122
|
+
denial: 1.5,
|
|
6123
|
+
interrupt: 1.25,
|
|
6124
|
+
rework: 1.5,
|
|
6125
|
+
correction: 1,
|
|
6126
|
+
acceptance: 1,
|
|
6127
|
+
repeat: 0.75
|
|
6128
|
+
};
|
|
6129
|
+
function num(v) {
|
|
6130
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
6131
|
+
}
|
|
6132
|
+
function bumpModel(acc, model, sidechain, outputTokens, ts) {
|
|
6133
|
+
const key = `${sidechain ? "s" : "m"}:${model}`;
|
|
6134
|
+
const cur = acc.get(key);
|
|
6135
|
+
if (!cur) {
|
|
6136
|
+
acc.set(key, {
|
|
6137
|
+
model,
|
|
6138
|
+
turns: 1,
|
|
6139
|
+
output_tokens: outputTokens,
|
|
6140
|
+
first_ts: ts,
|
|
6141
|
+
last_ts: ts,
|
|
6142
|
+
sidechain
|
|
6143
|
+
});
|
|
6144
|
+
return;
|
|
6145
|
+
}
|
|
6146
|
+
cur.turns++;
|
|
6147
|
+
cur.output_tokens += outputTokens;
|
|
6148
|
+
if (ts > 0) {
|
|
6149
|
+
if (cur.first_ts === 0 || ts < cur.first_ts)
|
|
6150
|
+
cur.first_ts = ts;
|
|
6151
|
+
if (ts > cur.last_ts)
|
|
6152
|
+
cur.last_ts = ts;
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
function asBlocks(content) {
|
|
6156
|
+
if (typeof content === "string")
|
|
6157
|
+
return [{ type: "text", text: content }];
|
|
6158
|
+
if (!Array.isArray(content))
|
|
6159
|
+
return [];
|
|
6160
|
+
return content.filter((b) => b !== null && typeof b === "object");
|
|
6161
|
+
}
|
|
6162
|
+
function textOf(blocks) {
|
|
6163
|
+
const parts = [];
|
|
6164
|
+
for (const b of blocks) {
|
|
6165
|
+
if (b.type === "text" && typeof b.text === "string")
|
|
6166
|
+
parts.push(b.text);
|
|
6167
|
+
}
|
|
6168
|
+
return parts.join("\n");
|
|
6169
|
+
}
|
|
6170
|
+
function isRepeat(prev, cur) {
|
|
6171
|
+
const toks = (s) => new Set(s.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length > 2));
|
|
6172
|
+
const a = toks(prev);
|
|
6173
|
+
const b = toks(cur);
|
|
6174
|
+
if (a.size < 3 || b.size < 3)
|
|
6175
|
+
return false;
|
|
6176
|
+
const [small, big] = a.size <= b.size ? [a, b] : [b, a];
|
|
6177
|
+
let shared = 0;
|
|
6178
|
+
for (const w of small)
|
|
6179
|
+
if (big.has(w))
|
|
6180
|
+
shared++;
|
|
6181
|
+
return shared / small.size >= 0.8;
|
|
6182
|
+
}
|
|
6183
|
+
function classifyGenuine(text, prevText, priorWork) {
|
|
6184
|
+
if (REWORK_RE.test(text))
|
|
6185
|
+
return { polarity: -1, magnitude: MAG.rework, kind: "rework" };
|
|
6186
|
+
if (priorWork && CORRECTION_RE.test(text))
|
|
6187
|
+
return { polarity: -1, magnitude: MAG.correction, kind: "correction" };
|
|
6188
|
+
if (ACCEPTANCE_RE.test(text))
|
|
6189
|
+
return { polarity: 1, magnitude: MAG.acceptance, kind: "acceptance" };
|
|
6190
|
+
if (prevText !== null && isRepeat(prevText, text))
|
|
6191
|
+
return { polarity: -1, magnitude: MAG.repeat, kind: "repeat" };
|
|
6192
|
+
return { polarity: 0, magnitude: 0, kind: "neutral" };
|
|
6193
|
+
}
|
|
6194
|
+
function emptyAnalysis(sessionId) {
|
|
6195
|
+
return {
|
|
6196
|
+
session_id: sessionId,
|
|
6197
|
+
user_turns: 0,
|
|
6198
|
+
assistant_turns: 0,
|
|
6199
|
+
tool_calls: 0,
|
|
6200
|
+
agent_questions: 0,
|
|
6201
|
+
input_tokens: 0,
|
|
6202
|
+
output_tokens: 0,
|
|
6203
|
+
cache_read_tokens: 0,
|
|
6204
|
+
cache_creation_tokens: 0,
|
|
6205
|
+
user_chars: 0,
|
|
6206
|
+
user_token_estimate: 0,
|
|
6207
|
+
human_share: 0,
|
|
6208
|
+
reactions_pos: 0,
|
|
6209
|
+
reactions_neg: 0,
|
|
6210
|
+
net_sentiment: 0,
|
|
6211
|
+
corrections: 0,
|
|
6212
|
+
reworks: 0,
|
|
6213
|
+
interrupts: 0,
|
|
6214
|
+
tool_denials: 0,
|
|
6215
|
+
repeated_instructions: 0,
|
|
6216
|
+
acceptances: 0,
|
|
6217
|
+
momentum_series: [],
|
|
6218
|
+
momentum_final: 0,
|
|
6219
|
+
momentum_auc: 0,
|
|
6220
|
+
ended_positive: false,
|
|
6221
|
+
recovery_events: 0,
|
|
6222
|
+
effort_at_end: null,
|
|
6223
|
+
model_mix: [],
|
|
6224
|
+
ai_title: null,
|
|
6225
|
+
session_slug: null,
|
|
6226
|
+
lines_seen: 0,
|
|
6227
|
+
analyzer_version: ANALYZER_VERSION
|
|
6228
|
+
};
|
|
6229
|
+
}
|
|
6230
|
+
async function analyzeSession(path) {
|
|
6231
|
+
const a = emptyAnalysis("");
|
|
6232
|
+
if (!path || !existsSync6(path))
|
|
6233
|
+
return a;
|
|
6234
|
+
const reactions = [];
|
|
6235
|
+
const modelMix = /* @__PURE__ */ new Map();
|
|
6236
|
+
let effort = null;
|
|
6237
|
+
let priorAssistantWorked = false;
|
|
6238
|
+
let prevUserText = null;
|
|
6239
|
+
await new Promise((resolve) => {
|
|
6240
|
+
const stream = createReadStream(path, { encoding: "utf-8" });
|
|
6241
|
+
const rl = createInterface({ input: stream });
|
|
6242
|
+
const processLine = (line) => {
|
|
6243
|
+
const trimmed = line.trim();
|
|
6244
|
+
if (!trimmed)
|
|
6245
|
+
return;
|
|
6246
|
+
let parsed;
|
|
6247
|
+
try {
|
|
6248
|
+
parsed = JSON.parse(trimmed);
|
|
6249
|
+
} catch {
|
|
6250
|
+
return;
|
|
6251
|
+
}
|
|
6252
|
+
a.lines_seen++;
|
|
6253
|
+
if (!a.session_id) {
|
|
6254
|
+
const sid = parsed.sessionId ?? parsed.session_id;
|
|
6255
|
+
if (typeof sid === "string" && sid)
|
|
6256
|
+
a.session_id = sid;
|
|
6257
|
+
}
|
|
6258
|
+
if (parsed.type === "ai-title" && typeof parsed.aiTitle === "string" && parsed.aiTitle) {
|
|
6259
|
+
a.ai_title = parsed.aiTitle;
|
|
6260
|
+
}
|
|
6261
|
+
if (typeof parsed.slug === "string" && parsed.slug)
|
|
6262
|
+
a.session_slug = parsed.slug;
|
|
6263
|
+
const role = parsed.message?.role;
|
|
6264
|
+
const isSide = parsed.isSidechain === true;
|
|
6265
|
+
const usage = parsed.message?.usage;
|
|
6266
|
+
if (role === "assistant" && usage) {
|
|
6267
|
+
a.input_tokens += num(usage.input_tokens);
|
|
6268
|
+
a.output_tokens += num(usage.output_tokens);
|
|
6269
|
+
a.cache_read_tokens += num(usage.cache_read_input_tokens);
|
|
6270
|
+
a.cache_creation_tokens += num(usage.cache_creation_input_tokens);
|
|
6271
|
+
}
|
|
6272
|
+
if (role === "assistant" && typeof parsed.message?.model === "string" && parsed.message.model && parsed.message.model !== SYNTHETIC_MODEL) {
|
|
6273
|
+
const rawTs = parsed.timestamp ? Date.parse(parsed.timestamp) : Number.NaN;
|
|
6274
|
+
bumpModel(modelMix, parsed.message.model, isSide, num(usage?.output_tokens), Number.isFinite(rawTs) ? rawTs : 0);
|
|
6275
|
+
}
|
|
6276
|
+
if (isSide)
|
|
6277
|
+
return;
|
|
6278
|
+
if (role === "assistant") {
|
|
6279
|
+
a.assistant_turns++;
|
|
6280
|
+
const blocks2 = asBlocks(parsed.message?.content);
|
|
6281
|
+
let worked = false;
|
|
6282
|
+
for (const b of blocks2) {
|
|
6283
|
+
if (b.type === "tool_use") {
|
|
6284
|
+
a.tool_calls++;
|
|
6285
|
+
worked = true;
|
|
6286
|
+
if (b.name === "AskUserQuestion")
|
|
6287
|
+
a.agent_questions++;
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
priorAssistantWorked = worked;
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
6293
|
+
if (role !== "user")
|
|
6294
|
+
return;
|
|
6295
|
+
const blocks = asBlocks(parsed.message?.content);
|
|
6296
|
+
const text = textOf(blocks);
|
|
6297
|
+
if (text.includes(INTERRUPT_MARKER)) {
|
|
6298
|
+
a.interrupts++;
|
|
6299
|
+
reactions.push({ polarity: -1, magnitude: MAG.interrupt, kind: "interrupt" });
|
|
6300
|
+
return;
|
|
6301
|
+
}
|
|
6302
|
+
const denied = blocks.some((b) => b.type === "tool_result" && b.is_error === true && typeof b.content === "string" && b.content.includes(DENIAL_MARKER));
|
|
6303
|
+
if (denied) {
|
|
6304
|
+
a.tool_denials++;
|
|
6305
|
+
reactions.push({ polarity: -1, magnitude: MAG.denial, kind: "denial" });
|
|
6306
|
+
return;
|
|
6307
|
+
}
|
|
6308
|
+
if (blocks.some((b) => b.type === "tool_result"))
|
|
6309
|
+
return;
|
|
6310
|
+
if (!text.trim())
|
|
6311
|
+
return;
|
|
6312
|
+
a.user_turns++;
|
|
6313
|
+
a.user_chars += text.length;
|
|
6314
|
+
const directive = detectEffortLevel(text);
|
|
6315
|
+
if (directive)
|
|
6316
|
+
effort = directive;
|
|
6317
|
+
const r = classifyGenuine(text, prevUserText, priorAssistantWorked);
|
|
6318
|
+
prevUserText = text;
|
|
6319
|
+
if (r.kind === "correction")
|
|
6320
|
+
a.corrections++;
|
|
6321
|
+
else if (r.kind === "rework")
|
|
6322
|
+
a.reworks++;
|
|
6323
|
+
else if (r.kind === "repeat")
|
|
6324
|
+
a.repeated_instructions++;
|
|
6325
|
+
else if (r.kind === "acceptance")
|
|
6326
|
+
a.acceptances++;
|
|
6327
|
+
if (r.polarity !== 0)
|
|
6328
|
+
reactions.push(r);
|
|
6329
|
+
priorAssistantWorked = false;
|
|
6330
|
+
};
|
|
6331
|
+
rl.on("line", (line) => {
|
|
6332
|
+
try {
|
|
6333
|
+
processLine(line);
|
|
6334
|
+
} catch {
|
|
6335
|
+
}
|
|
6336
|
+
});
|
|
6337
|
+
rl.on("close", () => resolve());
|
|
6338
|
+
rl.on("error", () => resolve());
|
|
6339
|
+
stream.on("error", () => resolve());
|
|
6340
|
+
});
|
|
6341
|
+
a.effort_at_end = effort;
|
|
6342
|
+
a.model_mix = [...modelMix.values()].sort((x, y) => x.first_ts - y.first_ts || x.model.localeCompare(y.model));
|
|
6343
|
+
a.user_token_estimate = Math.round(a.user_chars / 4);
|
|
6344
|
+
const denom = a.user_token_estimate + a.output_tokens;
|
|
6345
|
+
a.human_share = denom > 0 ? Number((a.user_token_estimate / denom).toFixed(4)) : 0;
|
|
6346
|
+
let momentum = 0;
|
|
6347
|
+
let auc = 0;
|
|
6348
|
+
const series = [];
|
|
6349
|
+
reactions.forEach((r, i) => {
|
|
6350
|
+
if (r.polarity > 0)
|
|
6351
|
+
a.reactions_pos++;
|
|
6352
|
+
else if (r.polarity < 0)
|
|
6353
|
+
a.reactions_neg++;
|
|
6354
|
+
momentum = momentum * MOMENTUM_DECAY + r.polarity * r.magnitude;
|
|
6355
|
+
const value2 = Number(momentum.toFixed(3));
|
|
6356
|
+
series.push({
|
|
6357
|
+
t: i,
|
|
6358
|
+
value: value2,
|
|
6359
|
+
...r.kind !== "neutral" ? { kind: r.kind } : {}
|
|
6360
|
+
});
|
|
6361
|
+
auc += value2;
|
|
6362
|
+
if (i > 0 && reactions[i - 1].polarity < 0 && r.polarity > 0)
|
|
6363
|
+
a.recovery_events++;
|
|
6364
|
+
});
|
|
6365
|
+
a.net_sentiment = a.reactions_pos - a.reactions_neg;
|
|
6366
|
+
a.momentum_series = series;
|
|
6367
|
+
a.momentum_final = series.length ? series[series.length - 1].value : 0;
|
|
6368
|
+
a.momentum_auc = Number(auc.toFixed(3));
|
|
6369
|
+
a.ended_positive = a.momentum_final > 0;
|
|
6370
|
+
return a;
|
|
6371
|
+
}
|
|
6372
|
+
|
|
6373
|
+
// ../../packages/claude-code-telemetry/dist/ingest.js
|
|
6374
|
+
function ingestBody(analytics, span, ctx) {
|
|
6375
|
+
const body = { session_analytics: analytics };
|
|
6376
|
+
if (span && span.started_at) {
|
|
6377
|
+
body.session_span = ctx ? { ...span, ...ctx } : span;
|
|
6378
|
+
}
|
|
6379
|
+
return body;
|
|
6380
|
+
}
|
|
6381
|
+
function ingestHeaders(auth) {
|
|
6382
|
+
const headers = {
|
|
6383
|
+
"content-type": "application/json",
|
|
6384
|
+
authorization: `Bearer ${auth.apiKey}`
|
|
6385
|
+
};
|
|
6386
|
+
if (auth.deviceId)
|
|
6387
|
+
headers["x-maind-device-id"] = auth.deviceId;
|
|
6388
|
+
return headers;
|
|
6389
|
+
}
|
|
6390
|
+
async function postSessionAnalytics(target, analytics, span, ctx) {
|
|
6391
|
+
try {
|
|
6392
|
+
const res = await fetch(target.endpoint, {
|
|
6393
|
+
method: "POST",
|
|
6394
|
+
headers: ingestHeaders(target),
|
|
6395
|
+
body: JSON.stringify(ingestBody(analytics, span, ctx))
|
|
6396
|
+
});
|
|
6397
|
+
if (!res.ok) {
|
|
6398
|
+
const text = await res.text().catch(() => "");
|
|
6399
|
+
return { ok: false, inserted: 0, status: res.status, error: text.slice(0, 500) };
|
|
6400
|
+
}
|
|
6401
|
+
const json = await res.json().catch(() => null);
|
|
6402
|
+
return {
|
|
6403
|
+
ok: true,
|
|
6404
|
+
inserted: json?.analytics_stored ? 1 : 0,
|
|
6405
|
+
spanStored: json?.span_stored === true,
|
|
6406
|
+
status: res.status
|
|
6407
|
+
};
|
|
6408
|
+
} catch (err4) {
|
|
6409
|
+
return {
|
|
6410
|
+
ok: false,
|
|
6411
|
+
inserted: 0,
|
|
6412
|
+
error: err4 instanceof Error ? err4.message : String(err4)
|
|
6413
|
+
};
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
|
|
6417
|
+
// ../../packages/claude-code-telemetry/dist/windows.js
|
|
6418
|
+
var FIVE_HOURS_MS = 5 * 60 * 60 * 1e3;
|
|
6419
|
+
var WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
6420
|
+
|
|
6421
|
+
// ../../packages/claude-code-telemetry/dist/projects.js
|
|
6422
|
+
import { readdir, stat } from "node:fs/promises";
|
|
6423
|
+
import { homedir as homedir3 } from "node:os";
|
|
6424
|
+
import { join as join6 } from "node:path";
|
|
6425
|
+
var HOME = homedir3();
|
|
6426
|
+
var DEFAULT_PROJECTS_DIR = join6(HOME, ".claude", "projects");
|
|
6427
|
+
function cwdFromProjectDir(encoded) {
|
|
6428
|
+
if (!encoded.startsWith("-"))
|
|
6429
|
+
return encoded;
|
|
6430
|
+
return encoded.replace(/-/g, "/");
|
|
6431
|
+
}
|
|
6432
|
+
async function listProjects(projectsDir = DEFAULT_PROJECTS_DIR) {
|
|
6433
|
+
let entries;
|
|
6434
|
+
try {
|
|
6435
|
+
entries = await readdir(projectsDir);
|
|
6436
|
+
} catch {
|
|
6437
|
+
return [];
|
|
6438
|
+
}
|
|
6439
|
+
const out2 = [];
|
|
6440
|
+
for (const entry of entries) {
|
|
6441
|
+
if (!entry.startsWith("-"))
|
|
6442
|
+
continue;
|
|
6443
|
+
const dir = join6(projectsDir, entry);
|
|
6444
|
+
let files;
|
|
6445
|
+
try {
|
|
6446
|
+
files = await readdir(dir);
|
|
6447
|
+
} catch {
|
|
6448
|
+
continue;
|
|
6449
|
+
}
|
|
6450
|
+
const transcripts = files.filter((f) => f.endsWith(".jsonl"));
|
|
6451
|
+
if (transcripts.length === 0)
|
|
6452
|
+
continue;
|
|
6453
|
+
let mtimeMs = 0;
|
|
6454
|
+
for (const f of transcripts) {
|
|
6455
|
+
try {
|
|
6456
|
+
const st = await stat(join6(dir, f));
|
|
6457
|
+
if (st.mtimeMs > mtimeMs)
|
|
6458
|
+
mtimeMs = st.mtimeMs;
|
|
6459
|
+
} catch {
|
|
6460
|
+
}
|
|
6461
|
+
}
|
|
6462
|
+
out2.push({
|
|
6463
|
+
encodedDir: entry,
|
|
6464
|
+
cwd: cwdFromProjectDir(entry),
|
|
6465
|
+
transcripts: transcripts.map((f) => join6(dir, f)),
|
|
6466
|
+
mtimeMs
|
|
6467
|
+
});
|
|
6468
|
+
}
|
|
6469
|
+
out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
6470
|
+
return out2.map(({ mtimeMs: _mtime, ...rest }) => rest);
|
|
6471
|
+
}
|
|
6472
|
+
|
|
6473
|
+
// src/sessions-cmd.ts
|
|
6474
|
+
var DEFAULT_ENDPOINT = "https://vsddmiosisvfrairepxv.supabase.co/functions/v1/mcp-client-metrics-ingest";
|
|
6475
|
+
var THROTTLE_MS = 120;
|
|
6476
|
+
function err2(line) {
|
|
6477
|
+
process.stderr.write(`maind: ${line}
|
|
6478
|
+
`);
|
|
6479
|
+
}
|
|
6480
|
+
function out(line) {
|
|
6481
|
+
process.stdout.write(line + "\n");
|
|
6482
|
+
}
|
|
6483
|
+
function parseSessionsArgs(argv) {
|
|
6484
|
+
const args = { dryRun: false, since: null, json: false };
|
|
6485
|
+
for (let i = 0; i < argv.length; i++) {
|
|
6486
|
+
const a = argv[i];
|
|
6487
|
+
if (a === "--dry-run") args.dryRun = true;
|
|
6488
|
+
else if (a === "--json") args.json = true;
|
|
6489
|
+
else if (a === "--since") {
|
|
6490
|
+
const raw = argv[++i];
|
|
6491
|
+
const ts = raw ? Date.parse(raw) : Number.NaN;
|
|
6492
|
+
if (Number.isFinite(ts)) args.since = ts;
|
|
6493
|
+
else err2(`ignoring unparseable --since '${raw ?? ""}' (expected YYYY-MM-DD)`);
|
|
6494
|
+
}
|
|
6495
|
+
}
|
|
6496
|
+
return args;
|
|
6497
|
+
}
|
|
6498
|
+
function endpoint() {
|
|
6499
|
+
const raw = process.env.MAIND_INGEST_URL?.trim();
|
|
6500
|
+
return raw && raw.length > 0 ? raw : DEFAULT_ENDPOINT;
|
|
6501
|
+
}
|
|
6502
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
6503
|
+
async function runSessionsBackfill(args) {
|
|
6504
|
+
let auth;
|
|
6505
|
+
try {
|
|
6506
|
+
auth = resolveAuth();
|
|
6507
|
+
} catch (e) {
|
|
6508
|
+
if (e instanceof MissingApiKeyError) {
|
|
6509
|
+
err2(e.message);
|
|
6510
|
+
return 1;
|
|
6511
|
+
}
|
|
6512
|
+
throw e;
|
|
6513
|
+
}
|
|
6514
|
+
const projects = await listProjects();
|
|
6515
|
+
const transcripts = projects.flatMap((p) => p.transcripts);
|
|
6516
|
+
if (transcripts.length === 0) {
|
|
6517
|
+
err2("no Claude Code transcripts found under ~/.claude/projects.");
|
|
6518
|
+
return 0;
|
|
6519
|
+
}
|
|
6520
|
+
const target = endpoint();
|
|
6521
|
+
const rows = [];
|
|
6522
|
+
let skipped = 0;
|
|
6523
|
+
let failed = 0;
|
|
6524
|
+
for (const path of transcripts) {
|
|
6525
|
+
const analysis = await analyzeSession(path);
|
|
6526
|
+
if (!analysis.session_id) {
|
|
6527
|
+
skipped++;
|
|
6528
|
+
continue;
|
|
6529
|
+
}
|
|
6530
|
+
if (args.since !== null) {
|
|
6531
|
+
const last = analysis.model_mix.reduce((m, s) => Math.max(m, s.last_ts), 0);
|
|
6532
|
+
if (last > 0 && last < args.since) {
|
|
6533
|
+
skipped++;
|
|
6534
|
+
continue;
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6537
|
+
const main2 = analysis.model_mix.filter((m) => !m.sidechain);
|
|
6538
|
+
const row = {
|
|
6539
|
+
session_id: analysis.session_id,
|
|
6540
|
+
title: analysis.ai_title,
|
|
6541
|
+
models: main2.map((m) => `${m.model}\xD7${m.turns}`).join(", ") || "\u2014",
|
|
6542
|
+
turns: analysis.assistant_turns,
|
|
6543
|
+
lines: analysis.lines_seen,
|
|
6544
|
+
posted: false
|
|
6545
|
+
};
|
|
6546
|
+
if (!args.dryRun) {
|
|
6547
|
+
const res = await postSessionAnalytics(
|
|
6548
|
+
{ endpoint: target, apiKey: auth.apiKey, deviceId: auth.deviceId },
|
|
6549
|
+
analysis
|
|
6550
|
+
);
|
|
6551
|
+
row.posted = res.ok;
|
|
6552
|
+
if (!res.ok) {
|
|
6553
|
+
failed++;
|
|
6554
|
+
row.error = res.error ?? `status ${res.status ?? "?"}`;
|
|
6555
|
+
}
|
|
6556
|
+
await sleep(THROTTLE_MS);
|
|
6557
|
+
}
|
|
6558
|
+
rows.push(row);
|
|
6559
|
+
}
|
|
6560
|
+
if (args.json) {
|
|
6561
|
+
out(JSON.stringify({ ok: failed === 0, dry_run: args.dryRun, skipped, failed, sessions: rows }, null, 2));
|
|
6562
|
+
return failed === 0 ? 0 : 1;
|
|
6563
|
+
}
|
|
6564
|
+
out(
|
|
6565
|
+
args.dryRun ? `Would upload ${rows.length} session(s) to ${target}:` : `Uploaded ${rows.length - failed}/${rows.length} session(s) to ${target}:`
|
|
6566
|
+
);
|
|
6567
|
+
out("");
|
|
6568
|
+
for (const r of rows) {
|
|
6569
|
+
const mark = args.dryRun ? " " : r.posted ? "\u2713" : "\u2717";
|
|
6570
|
+
out(`${mark} ${r.session_id.slice(0, 8)} ${String(r.turns).padStart(4)} turns ${r.models}`);
|
|
6571
|
+
out(` title: ${r.title ?? "\u2014"}`);
|
|
6572
|
+
if (r.error) out(` error: ${r.error}`);
|
|
6573
|
+
}
|
|
6574
|
+
out("");
|
|
6575
|
+
if (skipped > 0) out(`${skipped} transcript(s) skipped (no session id, or older than --since).`);
|
|
6576
|
+
if (args.dryRun) {
|
|
6577
|
+
out("Dry run \u2014 nothing was uploaded.");
|
|
6578
|
+
out(
|
|
6579
|
+
"Note: a real run uploads the session TITLES listed above (content-derived) for every project on this machine. They are only stored if you enabled 'Session titles' in the consent center; without it the numbers land and the titles are dropped."
|
|
6580
|
+
);
|
|
6581
|
+
}
|
|
6582
|
+
if (failed > 0) err2(`${failed} session(s) failed \u2014 re-running is safe, the server upserts.`);
|
|
6583
|
+
return failed === 0 ? 0 : 1;
|
|
6584
|
+
}
|
|
6585
|
+
|
|
3934
6586
|
// src/sync-cmd.ts
|
|
3935
|
-
import { execFileSync as
|
|
6587
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
3936
6588
|
var CONSENT_POLICY_VERSION = "2026-07-05";
|
|
3937
6589
|
var DEFAULT_DASHBOARD_URL = "https://app.maind.dev";
|
|
3938
|
-
function
|
|
6590
|
+
function err3(line) {
|
|
3939
6591
|
process.stderr.write(`maind: ${line}
|
|
3940
6592
|
`);
|
|
3941
6593
|
}
|
|
3942
6594
|
function repoRoot() {
|
|
3943
6595
|
try {
|
|
3944
|
-
return
|
|
6596
|
+
return execFileSync8("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
3945
6597
|
} catch {
|
|
3946
6598
|
return process.cwd();
|
|
3947
6599
|
}
|
|
@@ -3956,7 +6608,7 @@ function makeSync(json) {
|
|
|
3956
6608
|
auth = resolveAuth();
|
|
3957
6609
|
} catch (e) {
|
|
3958
6610
|
if (e instanceof MissingApiKeyError) {
|
|
3959
|
-
|
|
6611
|
+
err3(e.message);
|
|
3960
6612
|
return null;
|
|
3961
6613
|
}
|
|
3962
6614
|
throw e;
|
|
@@ -3970,11 +6622,11 @@ function makeSync(json) {
|
|
|
3970
6622
|
onProgress: json ? void 0 : (p) => process.stderr.write(` ${p.phase}: ${p.done}/${p.total}\r`)
|
|
3971
6623
|
});
|
|
3972
6624
|
} catch (e) {
|
|
3973
|
-
|
|
6625
|
+
err3(e instanceof Error ? e.message : String(e));
|
|
3974
6626
|
return null;
|
|
3975
6627
|
}
|
|
3976
6628
|
}
|
|
3977
|
-
function
|
|
6629
|
+
function msg7(e) {
|
|
3978
6630
|
return e instanceof Error ? e.message : String(e);
|
|
3979
6631
|
}
|
|
3980
6632
|
function parseSyncArgs(argv) {
|
|
@@ -3998,10 +6650,10 @@ async function runSync(opts) {
|
|
|
3998
6650
|
}
|
|
3999
6651
|
} catch (e) {
|
|
4000
6652
|
if (e instanceof SyncError && e.code === "consent_required") {
|
|
4001
|
-
|
|
6653
|
+
err3("codebase cloud-sync is not enabled \u2014 run `maind sync enable` first.");
|
|
4002
6654
|
return 1;
|
|
4003
6655
|
}
|
|
4004
|
-
|
|
6656
|
+
err3(`sync failed: ${msg7(e)}`);
|
|
4005
6657
|
return 1;
|
|
4006
6658
|
} finally {
|
|
4007
6659
|
await sync.close().catch(() => {
|
|
@@ -4037,7 +6689,7 @@ cloud: ${remote.indexed ? `${remote.node_count ?? "?"} symbols \xB7 ${remote.ed
|
|
|
4037
6689
|
async function push(sync, json) {
|
|
4038
6690
|
const local = await sync.localStatus();
|
|
4039
6691
|
if (!local.indexed || local.nodeCount === 0) {
|
|
4040
|
-
|
|
6692
|
+
err3("no local index found \u2014 run `maind index` first.");
|
|
4041
6693
|
return 1;
|
|
4042
6694
|
}
|
|
4043
6695
|
const res = await sync.push();
|
|
@@ -4054,7 +6706,6 @@ async function push(sync, json) {
|
|
|
4054
6706
|
}
|
|
4055
6707
|
|
|
4056
6708
|
// src/index.ts
|
|
4057
|
-
var VERSION = "0.7.0";
|
|
4058
6709
|
var HELP = `maind ${VERSION} \u2014 headless maind CLI for CI + git context
|
|
4059
6710
|
|
|
4060
6711
|
Usage:
|
|
@@ -4069,6 +6720,8 @@ Usage:
|
|
|
4069
6720
|
maind sync Push the local index to the cloud (opt-in; hashed rows only)
|
|
4070
6721
|
maind sync status [--json] Show local vs cloud sync state
|
|
4071
6722
|
maind sync enable|disable Opt in/out of codebase cloud-sync (consent)
|
|
6723
|
+
maind sessions backfill [.] Recompute session analytics for transcripts already on disk
|
|
6724
|
+
maind launch <agent> [..] Plug&play: local Switchyard model-routing proxy + agent behind it (v1: claude)
|
|
4072
6725
|
maind review [options] Provenance-backed review findings for a change set (advisory by default)
|
|
4073
6726
|
maind ci-report [options] Report a CI failure as a draft-lesson candidate (on failure)
|
|
4074
6727
|
maind ci-result [options] Report a build outcome + served manifest (end of job)
|
|
@@ -4086,13 +6739,34 @@ review options (advisory by default \u2014 exits 0 unless --gate + --fail-on):
|
|
|
4086
6739
|
--json Print the review manifest JSON to stdout (pipeable)
|
|
4087
6740
|
--gate Aggregate findings into a server-side pass|warn|block verdict (Team plan)
|
|
4088
6741
|
--fail-on block|warn With --gate: exit 1 when the verdict crosses this threshold
|
|
4089
|
-
--strict Infra errors
|
|
6742
|
+
--strict Infra errors (native module / transport / unexpected server
|
|
6743
|
+
error) exit 2 instead of failing open with exit 0. Expected
|
|
6744
|
+
unavailability \u2014 missing or unauthorized API key, rate limit,
|
|
6745
|
+
non-git dir \u2014 still exits 0 with a loud warning.
|
|
4090
6746
|
--no-report With --gate: omit the commit anchors so the server does NOT
|
|
4091
6747
|
persist a review_runs attestation record (ADR-230) \u2014 the
|
|
4092
6748
|
verdict is then advisory-only, without a replayable proof
|
|
4093
6749
|
--project-key <hex> Repo project_key override (default: auto-detect from git)
|
|
4094
6750
|
--mcp-url <url> Override MCP endpoint (default $MAIND_MCP_URL or production)
|
|
4095
6751
|
|
|
6752
|
+
launch options (v1 agent: claude; codex/openclaw planned \u2014 ADR-272):
|
|
6753
|
+
--dry-run Print the generated routes TOML (stdout) + config path, detection
|
|
6754
|
+
state and the exact spawn command (stderr); installs, writes and
|
|
6755
|
+
spawns nothing
|
|
6756
|
+
--json With --dry-run: machine-readable plan JSON on stdout
|
|
6757
|
+
--config <path> Use an existing routes.toml \u2014 skips generation + template fetch
|
|
6758
|
+
--route <name> Route name for --model and the generated [routes.<name>] (default: switchyard)
|
|
6759
|
+
--direct Skip the proxy \u2014 spawn the agent directly (no routing, visible banner)
|
|
6760
|
+
--no-fallback On switchyard startup failure exit 1 instead of degrading to a
|
|
6761
|
+
direct agent spawn
|
|
6762
|
+
--no-install Never auto-install; print the install one-liner and exit 1 when missing
|
|
6763
|
+
--mcp-url <url> Override MCP endpoint for the curated-template fetch
|
|
6764
|
+
Prereqs: installs nemo-switchyard[cli] via uv automatically when uv exists \u2014 never
|
|
6765
|
+
installs uv itself (prints the official instruction instead). The generated config
|
|
6766
|
+
lands in <repoRoot>/.maind/switchyard-routes.toml (or the maind user config dir
|
|
6767
|
+
outside a repo). Backend keys (ANTHROPIC_API_KEY / OPENROUTER_API_KEY) are read
|
|
6768
|
+
from the environment by the proxy at runtime \u2014 never written into the TOML.
|
|
6769
|
+
|
|
4096
6770
|
git-context options (run at session-start; reads local git + gh, all fail-open):
|
|
4097
6771
|
--out <path> Briefing markdown output (default .maind/git-context.md)
|
|
4098
6772
|
--manifest <path> Optional structured JSON output (git_context manifest)
|
|
@@ -4118,6 +6792,16 @@ ci-result options (run at the end of a job, success OR failure):
|
|
|
4118
6792
|
--run-url <url> Link to the CI run (default derived from GitHub env)
|
|
4119
6793
|
--result-url <url> Override ingest endpoint (default $MAIND_RESULT_URL or prod)
|
|
4120
6794
|
|
|
6795
|
+
sessions backfill options:
|
|
6796
|
+
--dry-run List what WOULD be uploaded (incl. the session titles) and stop.
|
|
6797
|
+
Recommended before the first real run \u2014 a backfill covers every
|
|
6798
|
+
project on this machine, and titles are content-derived.
|
|
6799
|
+
--since <YYYY-MM-DD> Only sessions whose last turn is on/after this date
|
|
6800
|
+
--json Machine-readable result
|
|
6801
|
+
Idempotent: the server upserts on (user_id, session_id), so re-running is safe.
|
|
6802
|
+
Titles are only stored if 'Session titles' is enabled in the consent center;
|
|
6803
|
+
otherwise the numbers land and the title is dropped server-side.
|
|
6804
|
+
|
|
4121
6805
|
context options:
|
|
4122
6806
|
--stack <a,b> Stack hint (seeds the platforms filter), e.g. nextjs,supabase
|
|
4123
6807
|
--platforms <a,b> Explicit platforms filter (overrides --stack)
|
|
@@ -4161,6 +6845,9 @@ async function main() {
|
|
|
4161
6845
|
process.stdout.write(key + "\n");
|
|
4162
6846
|
return 0;
|
|
4163
6847
|
}
|
|
6848
|
+
if (cmd === "launch") {
|
|
6849
|
+
return runLaunch(parseLaunchArgs(argv.slice(1)));
|
|
6850
|
+
}
|
|
4164
6851
|
const usesOrt = cmd === "index" && argv.includes("--embed") || cmd === "index-semantic";
|
|
4165
6852
|
if (usesOrt && !isOrtWorker(argv)) {
|
|
4166
6853
|
return runOrtIsolated(argv);
|
|
@@ -4183,6 +6870,13 @@ async function main() {
|
|
|
4183
6870
|
if (cmd === "sync") {
|
|
4184
6871
|
return runSync(parseSyncArgs(argv.slice(1)));
|
|
4185
6872
|
}
|
|
6873
|
+
if (cmd === "sessions") {
|
|
6874
|
+
if (argv[1] !== "backfill") {
|
|
6875
|
+
process.stderr.write("maind: usage: maind sessions backfill [--dry-run] [--since YYYY-MM-DD] [--json]\n");
|
|
6876
|
+
return 2;
|
|
6877
|
+
}
|
|
6878
|
+
return runSessionsBackfill(parseSessionsArgs(argv.slice(2)));
|
|
6879
|
+
}
|
|
4186
6880
|
if (cmd === "review") {
|
|
4187
6881
|
return runReview(parseReviewArgs(argv.slice(1)));
|
|
4188
6882
|
}
|
|
@@ -4196,8 +6890,271 @@ async function main() {
|
|
|
4196
6890
|
`);
|
|
4197
6891
|
return 2;
|
|
4198
6892
|
}
|
|
4199
|
-
main().then((code) => process.exit(code)).catch((
|
|
4200
|
-
process.stderr.write(`maind: fatal: ${
|
|
6893
|
+
main().then((code) => process.exit(code)).catch((err4) => {
|
|
6894
|
+
process.stderr.write(`maind: fatal: ${err4 instanceof Error ? err4.stack ?? err4.message : String(err4)}
|
|
4201
6895
|
`);
|
|
4202
6896
|
process.exit(1);
|
|
4203
6897
|
});
|
|
6898
|
+
/*! Bundled license information:
|
|
6899
|
+
|
|
6900
|
+
smol-toml/dist/error.js:
|
|
6901
|
+
(*!
|
|
6902
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
6903
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
6904
|
+
*
|
|
6905
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6906
|
+
* modification, are permitted provided that the following conditions are met:
|
|
6907
|
+
*
|
|
6908
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
6909
|
+
* list of conditions and the following disclaimer.
|
|
6910
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
6911
|
+
* this list of conditions and the following disclaimer in the
|
|
6912
|
+
* documentation and/or other materials provided with the distribution.
|
|
6913
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
6914
|
+
* may be used to endorse or promote products derived from this software without
|
|
6915
|
+
* specific prior written permission.
|
|
6916
|
+
*
|
|
6917
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
6918
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
6919
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
6920
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
6921
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
6922
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
6923
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
6924
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
6925
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
6926
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
6927
|
+
*)
|
|
6928
|
+
|
|
6929
|
+
smol-toml/dist/util.js:
|
|
6930
|
+
(*!
|
|
6931
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
6932
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
6933
|
+
*
|
|
6934
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6935
|
+
* modification, are permitted provided that the following conditions are met:
|
|
6936
|
+
*
|
|
6937
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
6938
|
+
* list of conditions and the following disclaimer.
|
|
6939
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
6940
|
+
* this list of conditions and the following disclaimer in the
|
|
6941
|
+
* documentation and/or other materials provided with the distribution.
|
|
6942
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
6943
|
+
* may be used to endorse or promote products derived from this software without
|
|
6944
|
+
* specific prior written permission.
|
|
6945
|
+
*
|
|
6946
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
6947
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
6948
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
6949
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
6950
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
6951
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
6952
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
6953
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
6954
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
6955
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
6956
|
+
*)
|
|
6957
|
+
|
|
6958
|
+
smol-toml/dist/date.js:
|
|
6959
|
+
(*!
|
|
6960
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
6961
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
6962
|
+
*
|
|
6963
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6964
|
+
* modification, are permitted provided that the following conditions are met:
|
|
6965
|
+
*
|
|
6966
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
6967
|
+
* list of conditions and the following disclaimer.
|
|
6968
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
6969
|
+
* this list of conditions and the following disclaimer in the
|
|
6970
|
+
* documentation and/or other materials provided with the distribution.
|
|
6971
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
6972
|
+
* may be used to endorse or promote products derived from this software without
|
|
6973
|
+
* specific prior written permission.
|
|
6974
|
+
*
|
|
6975
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
6976
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
6977
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
6978
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
6979
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
6980
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
6981
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
6982
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
6983
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
6984
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
6985
|
+
*)
|
|
6986
|
+
|
|
6987
|
+
smol-toml/dist/primitive.js:
|
|
6988
|
+
(*!
|
|
6989
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
6990
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
6991
|
+
*
|
|
6992
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6993
|
+
* modification, are permitted provided that the following conditions are met:
|
|
6994
|
+
*
|
|
6995
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
6996
|
+
* list of conditions and the following disclaimer.
|
|
6997
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
6998
|
+
* this list of conditions and the following disclaimer in the
|
|
6999
|
+
* documentation and/or other materials provided with the distribution.
|
|
7000
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
7001
|
+
* may be used to endorse or promote products derived from this software without
|
|
7002
|
+
* specific prior written permission.
|
|
7003
|
+
*
|
|
7004
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
7005
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
7006
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
7007
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
7008
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
7009
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
7010
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
7011
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
7012
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
7013
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7014
|
+
*)
|
|
7015
|
+
|
|
7016
|
+
smol-toml/dist/extract.js:
|
|
7017
|
+
(*!
|
|
7018
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
7019
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
7020
|
+
*
|
|
7021
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7022
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7023
|
+
*
|
|
7024
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
7025
|
+
* list of conditions and the following disclaimer.
|
|
7026
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
7027
|
+
* this list of conditions and the following disclaimer in the
|
|
7028
|
+
* documentation and/or other materials provided with the distribution.
|
|
7029
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
7030
|
+
* may be used to endorse or promote products derived from this software without
|
|
7031
|
+
* specific prior written permission.
|
|
7032
|
+
*
|
|
7033
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
7034
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
7035
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
7036
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
7037
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
7038
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
7039
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
7040
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
7041
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
7042
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7043
|
+
*)
|
|
7044
|
+
|
|
7045
|
+
smol-toml/dist/struct.js:
|
|
7046
|
+
(*!
|
|
7047
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
7048
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
7049
|
+
*
|
|
7050
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7051
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7052
|
+
*
|
|
7053
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
7054
|
+
* list of conditions and the following disclaimer.
|
|
7055
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
7056
|
+
* this list of conditions and the following disclaimer in the
|
|
7057
|
+
* documentation and/or other materials provided with the distribution.
|
|
7058
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
7059
|
+
* may be used to endorse or promote products derived from this software without
|
|
7060
|
+
* specific prior written permission.
|
|
7061
|
+
*
|
|
7062
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
7063
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
7064
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
7065
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
7066
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
7067
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
7068
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
7069
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
7070
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
7071
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7072
|
+
*)
|
|
7073
|
+
|
|
7074
|
+
smol-toml/dist/parse.js:
|
|
7075
|
+
(*!
|
|
7076
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
7077
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
7078
|
+
*
|
|
7079
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7080
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7081
|
+
*
|
|
7082
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
7083
|
+
* list of conditions and the following disclaimer.
|
|
7084
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
7085
|
+
* this list of conditions and the following disclaimer in the
|
|
7086
|
+
* documentation and/or other materials provided with the distribution.
|
|
7087
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
7088
|
+
* may be used to endorse or promote products derived from this software without
|
|
7089
|
+
* specific prior written permission.
|
|
7090
|
+
*
|
|
7091
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
7092
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
7093
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
7094
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
7095
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
7096
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
7097
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
7098
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
7099
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
7100
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7101
|
+
*)
|
|
7102
|
+
|
|
7103
|
+
smol-toml/dist/stringify.js:
|
|
7104
|
+
(*!
|
|
7105
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
7106
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
7107
|
+
*
|
|
7108
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7109
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7110
|
+
*
|
|
7111
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
7112
|
+
* list of conditions and the following disclaimer.
|
|
7113
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
7114
|
+
* this list of conditions and the following disclaimer in the
|
|
7115
|
+
* documentation and/or other materials provided with the distribution.
|
|
7116
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
7117
|
+
* may be used to endorse or promote products derived from this software without
|
|
7118
|
+
* specific prior written permission.
|
|
7119
|
+
*
|
|
7120
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
7121
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
7122
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
7123
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
7124
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
7125
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
7126
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
7127
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
7128
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
7129
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7130
|
+
*)
|
|
7131
|
+
|
|
7132
|
+
smol-toml/dist/index.js:
|
|
7133
|
+
(*!
|
|
7134
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
7135
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
7136
|
+
*
|
|
7137
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7138
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7139
|
+
*
|
|
7140
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
7141
|
+
* list of conditions and the following disclaimer.
|
|
7142
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
7143
|
+
* this list of conditions and the following disclaimer in the
|
|
7144
|
+
* documentation and/or other materials provided with the distribution.
|
|
7145
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
7146
|
+
* may be used to endorse or promote products derived from this software without
|
|
7147
|
+
* specific prior written permission.
|
|
7148
|
+
*
|
|
7149
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
7150
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
7151
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
7152
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
7153
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
7154
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
7155
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
7156
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
7157
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
7158
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7159
|
+
*)
|
|
7160
|
+
*/
|