truecourse 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/cli.mjs +921 -1002
- package/package.json +1 -1
- package/public/assets/{index-CCtFa3au.js → index-8ZCcQOMQ.js} +124 -124
- package/public/index.html +1 -1
- package/server.mjs +1123 -1127
package/cli.mjs
CHANGED
|
@@ -3492,9 +3492,7 @@ var require_src = __commonJS({
|
|
|
3492
3492
|
|
|
3493
3493
|
// node_modules/.pnpm/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
|
|
3494
3494
|
import { stdout as S, stdin as $ } from "node:process";
|
|
3495
|
-
import * as _ from "node:readline";
|
|
3496
3495
|
import P from "node:readline";
|
|
3497
|
-
import { ReadStream as D } from "node:tty";
|
|
3498
3496
|
function d(r, t2, e) {
|
|
3499
3497
|
if (!e.some((o) => !o.disabled)) return r;
|
|
3500
3498
|
const s = r + t2, i = Math.max(e.length - 1, 0), n = s < 0 ? i : s > i ? 0 : s;
|
|
@@ -3520,27 +3518,6 @@ function w(r, t2) {
|
|
|
3520
3518
|
const e = r;
|
|
3521
3519
|
e.isTTY && e.setRawMode(t2);
|
|
3522
3520
|
}
|
|
3523
|
-
function z({ input: r = $, output: t2 = S, overwrite: e = true, hideCursor: s = true } = {}) {
|
|
3524
|
-
const i = _.createInterface({ input: r, output: t2, prompt: "", tabSize: 1 });
|
|
3525
|
-
_.emitKeypressEvents(r, i), r instanceof D && r.isTTY && r.setRawMode(true);
|
|
3526
|
-
const n = (o, { name: a, sequence: h }) => {
|
|
3527
|
-
const l = String(o);
|
|
3528
|
-
if (V([l, a, h], "cancel")) {
|
|
3529
|
-
s && t2.write(import_sisteransi.cursor.show), process.exit(0);
|
|
3530
|
-
return;
|
|
3531
|
-
}
|
|
3532
|
-
if (!e) return;
|
|
3533
|
-
const f = a === "return" ? 0 : -1, v = a === "return" ? -1 : 0;
|
|
3534
|
-
_.moveCursor(t2, f, v, () => {
|
|
3535
|
-
_.clearLine(t2, 1, () => {
|
|
3536
|
-
r.once("keypress", n);
|
|
3537
|
-
});
|
|
3538
|
-
});
|
|
3539
|
-
};
|
|
3540
|
-
return s && t2.write(import_sisteransi.cursor.hide), r.once("keypress", n), () => {
|
|
3541
|
-
r.off("keypress", n), s && t2.write(import_sisteransi.cursor.show), r instanceof D && r.isTTY && !Y && r.setRawMode(false), i.terminal = false, i.close();
|
|
3542
|
-
};
|
|
3543
|
-
}
|
|
3544
3521
|
function R(r, t2, e, s = e) {
|
|
3545
3522
|
const i = O(r ?? S);
|
|
3546
3523
|
return wrapAnsi(t2, i - e.length, { hard: true, trim: false }).split(`
|
|
@@ -3734,7 +3711,7 @@ import P2 from "node:process";
|
|
|
3734
3711
|
function Ze() {
|
|
3735
3712
|
return P2.platform !== "win32" ? P2.env.TERM !== "linux" : !!P2.env.CI || !!P2.env.WT_SESSION || !!P2.env.TERMINUS_SUBLIME || P2.env.ConEmuTask === "{cmd::Cmder}" || P2.env.TERM_PROGRAM === "Terminus-Sublime" || P2.env.TERM_PROGRAM === "vscode" || P2.env.TERM === "xterm-256color" || P2.env.TERM === "alacritty" || P2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
3736
3713
|
}
|
|
3737
|
-
var import_sisteransi2, ee,
|
|
3714
|
+
var import_sisteransi2, ee, w2, _e, oe, ue, F, le, d2, E2, Ie, Ee, z2, H2, te, U, J, xe, se, ce, Ge, $e, de, Oe, he, pe, me, ge, V2, ye, et2, Y2, ot2, O2, pt, mt, gt, Ve, re, _t, je;
|
|
3738
3715
|
var init_dist4 = __esm({
|
|
3739
3716
|
"node_modules/.pnpm/@clack+prompts@1.2.0/node_modules/@clack/prompts/dist/index.mjs"() {
|
|
3740
3717
|
init_dist3();
|
|
@@ -3743,7 +3720,6 @@ var init_dist4 = __esm({
|
|
|
3743
3720
|
init_dist2();
|
|
3744
3721
|
import_sisteransi2 = __toESM(require_src(), 1);
|
|
3745
3722
|
ee = Ze();
|
|
3746
|
-
ae = () => process.env.CI === "true";
|
|
3747
3723
|
w2 = (e, i) => ee ? e : i;
|
|
3748
3724
|
_e = w2("\u25C6", "*");
|
|
3749
3725
|
oe = w2("\u25A0", "x");
|
|
@@ -3820,7 +3796,7 @@ var init_dist4 = __esm({
|
|
|
3820
3796
|
}
|
|
3821
3797
|
if (f > $2) {
|
|
3822
3798
|
let b = 0, x = 0, G2 = f;
|
|
3823
|
-
const M2 = e - v, R2 = (j2,
|
|
3799
|
+
const M2 = e - v, R2 = (j2, D) => et2(h, G2, j2, D, $2);
|
|
3824
3800
|
m ? ({ lineCount: G2, removals: b } = R2(0, M2), G2 > $2 && ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length))) : ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length), G2 > $2 && ({ lineCount: G2, removals: b } = R2(0, M2))), b > 0 && (m = true, h.splice(0, b)), x > 0 && (g = true, h.splice(h.length - x, x));
|
|
3825
3801
|
}
|
|
3826
3802
|
const C2 = [];
|
|
@@ -3899,59 +3875,6 @@ ${t("gray", E2)} ` : "";
|
|
|
3899
3875
|
|
|
3900
3876
|
`);
|
|
3901
3877
|
};
|
|
3902
|
-
Ct = (e) => t("magenta", e);
|
|
3903
|
-
fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, cancelMessage: r, errorMessage: u2, frames: n = ee ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: o = ee ? 80 : 120, signal: c2, ...a } = {}) => {
|
|
3904
|
-
const l = ae();
|
|
3905
|
-
let $2, y, p2 = false, m = false, g = "", S2, h = performance.now();
|
|
3906
|
-
const f = O(s), v = a?.styleFrame ?? Ct, T = (_2) => {
|
|
3907
|
-
const A2 = _2 > 1 ? u2 ?? u.messages.error : r ?? u.messages.cancel;
|
|
3908
|
-
m = _2 === 1, p2 && (W(A2, _2), m && typeof i == "function" && i());
|
|
3909
|
-
}, C2 = () => T(2), b = () => T(1), x = () => {
|
|
3910
|
-
process.on("uncaughtExceptionMonitor", C2), process.on("unhandledRejection", C2), process.on("SIGINT", b), process.on("SIGTERM", b), process.on("exit", T), c2 && c2.addEventListener("abort", b);
|
|
3911
|
-
}, G2 = () => {
|
|
3912
|
-
process.removeListener("uncaughtExceptionMonitor", C2), process.removeListener("unhandledRejection", C2), process.removeListener("SIGINT", b), process.removeListener("SIGTERM", b), process.removeListener("exit", T), c2 && c2.removeEventListener("abort", b);
|
|
3913
|
-
}, M2 = () => {
|
|
3914
|
-
if (S2 === void 0) return;
|
|
3915
|
-
l && s.write(`
|
|
3916
|
-
`);
|
|
3917
|
-
const _2 = wrapAnsi(S2, f, { hard: true, trim: false }).split(`
|
|
3918
|
-
`);
|
|
3919
|
-
_2.length > 1 && s.write(import_sisteransi2.cursor.up(_2.length - 1)), s.write(import_sisteransi2.cursor.to(0)), s.write(import_sisteransi2.erase.down());
|
|
3920
|
-
}, R2 = (_2) => _2.replace(/\.+$/, ""), j2 = (_2) => {
|
|
3921
|
-
const A2 = (performance.now() - _2) / 1e3, k = Math.floor(A2 / 60), L = Math.floor(A2 % 60);
|
|
3922
|
-
return k > 0 ? `[${k}m ${L}s]` : `[${L}s]`;
|
|
3923
|
-
}, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
|
|
3924
|
-
p2 = true, $2 = z({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${t("gray", d2)}
|
|
3925
|
-
`);
|
|
3926
|
-
let A2 = 0, k = 0;
|
|
3927
|
-
x(), y = setInterval(() => {
|
|
3928
|
-
if (l && g === S2) return;
|
|
3929
|
-
M2(), S2 = g;
|
|
3930
|
-
const L = v(n[A2]);
|
|
3931
|
-
let Z;
|
|
3932
|
-
if (l) Z = `${L} ${g}...`;
|
|
3933
|
-
else if (e === "timer") Z = `${L} ${g} ${j2(h)}`;
|
|
3934
|
-
else {
|
|
3935
|
-
const Be = ".".repeat(Math.floor(k)).slice(0, 3);
|
|
3936
|
-
Z = `${L} ${g}${Be}`;
|
|
3937
|
-
}
|
|
3938
|
-
const Ne = wrapAnsi(Z, f, { hard: true, trim: false });
|
|
3939
|
-
s.write(Ne), A2 = A2 + 1 < n.length ? A2 + 1 : 0, k = k < 4 ? k + 0.125 : 0;
|
|
3940
|
-
}, o);
|
|
3941
|
-
}, W = (_2 = "", A2 = 0, k = false) => {
|
|
3942
|
-
if (!p2) return;
|
|
3943
|
-
p2 = false, clearInterval(y), M2();
|
|
3944
|
-
const L = A2 === 0 ? t("green", F) : A2 === 1 ? t("red", oe) : t("red", ue);
|
|
3945
|
-
g = _2 ?? g, k || (e === "timer" ? s.write(`${L} ${g} ${j2(h)}
|
|
3946
|
-
`) : s.write(`${L} ${g}
|
|
3947
|
-
`)), G2(), $2();
|
|
3948
|
-
};
|
|
3949
|
-
return { start: ie, stop: (_2 = "") => W(_2, 0), message: (_2 = "") => {
|
|
3950
|
-
g = R2(_2 ?? g);
|
|
3951
|
-
}, cancel: (_2 = "") => W(_2, 1), error: (_2 = "") => W(_2, 2), clear: () => W("", 0, true), get isCancelled() {
|
|
3952
|
-
return m;
|
|
3953
|
-
} };
|
|
3954
|
-
};
|
|
3955
3878
|
Ve = { light: w2("\u2500", "-"), heavy: w2("\u2501", "="), block: w2("\u2588", "#") };
|
|
3956
3879
|
re = (e, i) => e.includes(`
|
|
3957
3880
|
`) ? e.split(`
|
|
@@ -4148,7 +4071,6 @@ __export(helpers_exports, {
|
|
|
4148
4071
|
renderDiffResultsSummary: () => renderDiffResultsSummary,
|
|
4149
4072
|
renderViolations: () => renderViolations,
|
|
4150
4073
|
renderViolationsSummary: () => renderViolationsSummary,
|
|
4151
|
-
requireDashboard: () => requireDashboard,
|
|
4152
4074
|
requireRegisteredRepo: () => requireRegisteredRepo,
|
|
4153
4075
|
severityColor: () => severityColor,
|
|
4154
4076
|
severityIcon: () => severityIcon,
|
|
@@ -4185,19 +4107,6 @@ function getServerUrl() {
|
|
|
4185
4107
|
const port = process.env.PORT || DEFAULT_PORT;
|
|
4186
4108
|
return `http://localhost:${port}`;
|
|
4187
4109
|
}
|
|
4188
|
-
async function requireDashboard() {
|
|
4189
|
-
const url = getServerUrl();
|
|
4190
|
-
try {
|
|
4191
|
-
const res = await fetch(`${url}/api/health`);
|
|
4192
|
-
if (!res.ok) throw new Error();
|
|
4193
|
-
} catch {
|
|
4194
|
-
O2.error(
|
|
4195
|
-
`TrueCourse dashboard is not running at ${url}.
|
|
4196
|
-
Start it first with: truecourse dashboard`
|
|
4197
|
-
);
|
|
4198
|
-
process.exit(1);
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
4110
|
function requireRegisteredRepo() {
|
|
4202
4111
|
const repoDir = resolveRepoDir(process.cwd());
|
|
4203
4112
|
if (!repoDir) {
|
|
@@ -4439,32 +4348,184 @@ var init_helpers = __esm({
|
|
|
4439
4348
|
}
|
|
4440
4349
|
});
|
|
4441
4350
|
|
|
4442
|
-
// apps/server/dist/lib/
|
|
4351
|
+
// apps/server/dist/lib/atomic-write.js
|
|
4443
4352
|
import fs4 from "node:fs";
|
|
4444
4353
|
import path4 from "node:path";
|
|
4354
|
+
function atomicWriteJson(targetPath, data) {
|
|
4355
|
+
fs4.mkdirSync(path4.dirname(targetPath), { recursive: true });
|
|
4356
|
+
const tmp = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
|
|
4357
|
+
fs4.writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
4358
|
+
fs4.renameSync(tmp, targetPath);
|
|
4359
|
+
}
|
|
4360
|
+
function lockPath(repoPath) {
|
|
4361
|
+
return path4.join(repoPath, ".truecourse", LOCK_FILENAME);
|
|
4362
|
+
}
|
|
4363
|
+
function acquireAnalyzeLock(repoPath) {
|
|
4364
|
+
const file = lockPath(repoPath);
|
|
4365
|
+
fs4.mkdirSync(path4.dirname(file), { recursive: true });
|
|
4366
|
+
try {
|
|
4367
|
+
const fd = fs4.openSync(file, "wx");
|
|
4368
|
+
fs4.writeSync(fd, `${process.pid}
|
|
4369
|
+
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
4370
|
+
`);
|
|
4371
|
+
fs4.closeSync(fd);
|
|
4372
|
+
} catch (err) {
|
|
4373
|
+
if (err.code === "EEXIST") {
|
|
4374
|
+
let owner = null;
|
|
4375
|
+
try {
|
|
4376
|
+
owner = parseInt(fs4.readFileSync(file, "utf-8").split("\n")[0], 10) || null;
|
|
4377
|
+
} catch {
|
|
4378
|
+
}
|
|
4379
|
+
throw new AnalyzeLockError(repoPath, owner);
|
|
4380
|
+
}
|
|
4381
|
+
throw err;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
function releaseAnalyzeLock(repoPath) {
|
|
4385
|
+
try {
|
|
4386
|
+
fs4.unlinkSync(lockPath(repoPath));
|
|
4387
|
+
} catch (err) {
|
|
4388
|
+
if (err.code !== "ENOENT")
|
|
4389
|
+
throw err;
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
var LOCK_FILENAME, AnalyzeLockError;
|
|
4393
|
+
var init_atomic_write = __esm({
|
|
4394
|
+
"apps/server/dist/lib/atomic-write.js"() {
|
|
4395
|
+
"use strict";
|
|
4396
|
+
LOCK_FILENAME = ".analyze.lock";
|
|
4397
|
+
AnalyzeLockError = class extends Error {
|
|
4398
|
+
constructor(repoPath, ownerPid) {
|
|
4399
|
+
const who = ownerPid != null ? ` (held by pid ${ownerPid})` : "";
|
|
4400
|
+
super(`Another analyze is already running for ${repoPath}${who}. If you're sure no analyze is in progress, remove ${lockPath(repoPath)} and retry.`);
|
|
4401
|
+
this.name = "AnalyzeLockError";
|
|
4402
|
+
}
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4405
|
+
});
|
|
4406
|
+
|
|
4407
|
+
// apps/server/dist/lib/analysis-store.js
|
|
4408
|
+
import fs5 from "node:fs";
|
|
4409
|
+
import path5 from "node:path";
|
|
4410
|
+
function storeDir(repoPath) {
|
|
4411
|
+
return path5.join(repoPath, TRUECOURSE_DIR2);
|
|
4412
|
+
}
|
|
4413
|
+
function analysesDir(repoPath) {
|
|
4414
|
+
return path5.join(storeDir(repoPath), ANALYSES_DIR);
|
|
4415
|
+
}
|
|
4416
|
+
function analysisFilePath(repoPath, filename) {
|
|
4417
|
+
return path5.join(analysesDir(repoPath), filename);
|
|
4418
|
+
}
|
|
4419
|
+
function latestPath(repoPath) {
|
|
4420
|
+
return path5.join(storeDir(repoPath), LATEST_FILE);
|
|
4421
|
+
}
|
|
4422
|
+
function historyPath(repoPath) {
|
|
4423
|
+
return path5.join(storeDir(repoPath), HISTORY_FILE);
|
|
4424
|
+
}
|
|
4425
|
+
function diffPath(repoPath) {
|
|
4426
|
+
return path5.join(storeDir(repoPath), DIFF_FILE);
|
|
4427
|
+
}
|
|
4428
|
+
function buildAnalysisFilename(analysisId, createdAt) {
|
|
4429
|
+
const iso = createdAt.replace(/[:.]/g, "-").replace(/-\d{3}Z$/, "Z");
|
|
4430
|
+
const shortId = analysisId.replace(/-/g, "").slice(0, 8);
|
|
4431
|
+
return `${iso}_${shortId}.json`;
|
|
4432
|
+
}
|
|
4433
|
+
function readLatest(repoPath) {
|
|
4434
|
+
const file = latestPath(repoPath);
|
|
4435
|
+
let mtime;
|
|
4436
|
+
try {
|
|
4437
|
+
mtime = fs5.statSync(file).mtimeMs;
|
|
4438
|
+
} catch (err) {
|
|
4439
|
+
if (err.code === "ENOENT") {
|
|
4440
|
+
latestCache.delete(repoPath);
|
|
4441
|
+
return null;
|
|
4442
|
+
}
|
|
4443
|
+
throw err;
|
|
4444
|
+
}
|
|
4445
|
+
const cached = latestCache.get(repoPath);
|
|
4446
|
+
if (cached && cached.mtime === mtime)
|
|
4447
|
+
return cached.data;
|
|
4448
|
+
const data = JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4449
|
+
latestCache.set(repoPath, { mtime, data });
|
|
4450
|
+
return data;
|
|
4451
|
+
}
|
|
4452
|
+
function writeLatest(repoPath, latest) {
|
|
4453
|
+
atomicWriteJson(latestPath(repoPath), latest);
|
|
4454
|
+
latestCache.delete(repoPath);
|
|
4455
|
+
}
|
|
4456
|
+
function writeAnalysis(repoPath, snapshot) {
|
|
4457
|
+
const filename = buildAnalysisFilename(snapshot.id, snapshot.createdAt);
|
|
4458
|
+
atomicWriteJson(analysisFilePath(repoPath, filename), snapshot);
|
|
4459
|
+
return { filename, snapshot };
|
|
4460
|
+
}
|
|
4461
|
+
function readHistory(repoPath) {
|
|
4462
|
+
const file = historyPath(repoPath);
|
|
4463
|
+
if (!fs5.existsSync(file))
|
|
4464
|
+
return { analyses: [] };
|
|
4465
|
+
return JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4466
|
+
}
|
|
4467
|
+
function appendHistory(repoPath, entry) {
|
|
4468
|
+
const history = readHistory(repoPath);
|
|
4469
|
+
history.analyses.push(entry);
|
|
4470
|
+
atomicWriteJson(historyPath(repoPath), history);
|
|
4471
|
+
}
|
|
4472
|
+
function readDiff(repoPath) {
|
|
4473
|
+
const file = diffPath(repoPath);
|
|
4474
|
+
if (!fs5.existsSync(file))
|
|
4475
|
+
return null;
|
|
4476
|
+
return JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4477
|
+
}
|
|
4478
|
+
function writeDiff(repoPath, diff) {
|
|
4479
|
+
atomicWriteJson(diffPath(repoPath), diff);
|
|
4480
|
+
}
|
|
4481
|
+
function deleteDiff(repoPath) {
|
|
4482
|
+
try {
|
|
4483
|
+
fs5.unlinkSync(diffPath(repoPath));
|
|
4484
|
+
} catch (err) {
|
|
4485
|
+
if (err.code !== "ENOENT")
|
|
4486
|
+
throw err;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
var TRUECOURSE_DIR2, ANALYSES_DIR, LATEST_FILE, HISTORY_FILE, DIFF_FILE, latestCache;
|
|
4490
|
+
var init_analysis_store = __esm({
|
|
4491
|
+
"apps/server/dist/lib/analysis-store.js"() {
|
|
4492
|
+
"use strict";
|
|
4493
|
+
init_atomic_write();
|
|
4494
|
+
TRUECOURSE_DIR2 = ".truecourse";
|
|
4495
|
+
ANALYSES_DIR = "analyses";
|
|
4496
|
+
LATEST_FILE = "LATEST.json";
|
|
4497
|
+
HISTORY_FILE = "history.json";
|
|
4498
|
+
DIFF_FILE = "diff.json";
|
|
4499
|
+
latestCache = /* @__PURE__ */ new Map();
|
|
4500
|
+
}
|
|
4501
|
+
});
|
|
4502
|
+
|
|
4503
|
+
// apps/server/dist/lib/logger.js
|
|
4504
|
+
import fs6 from "node:fs";
|
|
4505
|
+
import path6 from "node:path";
|
|
4445
4506
|
function rotateLog(filePath) {
|
|
4446
|
-
if (!
|
|
4507
|
+
if (!fs6.existsSync(filePath))
|
|
4447
4508
|
return;
|
|
4448
|
-
const stats =
|
|
4509
|
+
const stats = fs6.statSync(filePath);
|
|
4449
4510
|
if (stats.size < MAX_LOG_SIZE)
|
|
4450
4511
|
return;
|
|
4451
4512
|
for (let i = MAX_LOG_FILES; i >= 1; i--) {
|
|
4452
4513
|
const older = `${filePath}.${i}`;
|
|
4453
4514
|
if (i === MAX_LOG_FILES) {
|
|
4454
|
-
if (
|
|
4455
|
-
|
|
4515
|
+
if (fs6.existsSync(older))
|
|
4516
|
+
fs6.unlinkSync(older);
|
|
4456
4517
|
} else {
|
|
4457
4518
|
const newer = `${filePath}.${i + 1}`;
|
|
4458
|
-
if (
|
|
4459
|
-
|
|
4519
|
+
if (fs6.existsSync(older))
|
|
4520
|
+
fs6.renameSync(older, newer);
|
|
4460
4521
|
}
|
|
4461
4522
|
}
|
|
4462
|
-
|
|
4523
|
+
fs6.renameSync(filePath, `${filePath}.1`);
|
|
4463
4524
|
}
|
|
4464
4525
|
function openSink(config2) {
|
|
4465
|
-
|
|
4526
|
+
fs6.mkdirSync(path6.dirname(config2.filePath), { recursive: true });
|
|
4466
4527
|
rotateLog(config2.filePath);
|
|
4467
|
-
const stream =
|
|
4528
|
+
const stream = fs6.createWriteStream(config2.filePath, { flags: "a" });
|
|
4468
4529
|
stream.write(`
|
|
4469
4530
|
--- ${(/* @__PURE__ */ new Date()).toISOString()} ---
|
|
4470
4531
|
`);
|
|
@@ -5096,7 +5157,7 @@ var require_node = __commonJS({
|
|
|
5096
5157
|
exports.inspectOpts = Object.keys(process.env).filter((key) => {
|
|
5097
5158
|
return /^debug_/i.test(key);
|
|
5098
5159
|
}).reduce((obj, key) => {
|
|
5099
|
-
const prop = key.substring(6).toLowerCase().replace(/_([a-z])/g, (
|
|
5160
|
+
const prop = key.substring(6).toLowerCase().replace(/_([a-z])/g, (_, k) => {
|
|
5100
5161
|
return k.toUpperCase();
|
|
5101
5162
|
});
|
|
5102
5163
|
let val = process.env[key];
|
|
@@ -6572,7 +6633,7 @@ function deleteBranchTask(branch, forceDelete = false) {
|
|
|
6572
6633
|
parser(stdOut, stdErr) {
|
|
6573
6634
|
return parseBranchDeletions(stdOut, stdErr).branches[branch];
|
|
6574
6635
|
},
|
|
6575
|
-
onError({ exitCode, stdErr, stdOut }, error,
|
|
6636
|
+
onError({ exitCode, stdErr, stdOut }, error, _, fail) {
|
|
6576
6637
|
if (!hasBranchDeletionError(String(error), exitCode)) {
|
|
6577
6638
|
return fail(error);
|
|
6578
6639
|
}
|
|
@@ -9776,17 +9837,27 @@ var init_git = __esm({
|
|
|
9776
9837
|
});
|
|
9777
9838
|
|
|
9778
9839
|
// apps/server/dist/config/project-config.js
|
|
9779
|
-
import
|
|
9840
|
+
import fs7 from "node:fs";
|
|
9780
9841
|
function readProjectConfig(repoDir) {
|
|
9781
9842
|
const file = getRepoConfigPath(repoDir);
|
|
9782
|
-
if (!
|
|
9843
|
+
if (!fs7.existsSync(file))
|
|
9783
9844
|
return { ...EMPTY };
|
|
9784
9845
|
try {
|
|
9785
|
-
return JSON.parse(
|
|
9846
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
9786
9847
|
} catch {
|
|
9787
9848
|
return { ...EMPTY };
|
|
9788
9849
|
}
|
|
9789
9850
|
}
|
|
9851
|
+
function writeProjectConfig(repoDir, config2) {
|
|
9852
|
+
ensureRepoTruecourseDir(repoDir);
|
|
9853
|
+
fs7.writeFileSync(getRepoConfigPath(repoDir), JSON.stringify(config2, null, 2), "utf-8");
|
|
9854
|
+
}
|
|
9855
|
+
function updateProjectConfig(repoDir, patch) {
|
|
9856
|
+
const current = readProjectConfig(repoDir);
|
|
9857
|
+
const next = { ...current, ...patch };
|
|
9858
|
+
writeProjectConfig(repoDir, next);
|
|
9859
|
+
return next;
|
|
9860
|
+
}
|
|
9790
9861
|
var EMPTY;
|
|
9791
9862
|
var init_project_config = __esm({
|
|
9792
9863
|
"apps/server/dist/config/project-config.js"() {
|
|
@@ -9848,7 +9919,7 @@ var require_ignore = __commonJS({
|
|
|
9848
9919
|
// (a ) -> (a)
|
|
9849
9920
|
// (a \ ) -> (a )
|
|
9850
9921
|
/((?:\\\\)*?)(\\?\s+)$/,
|
|
9851
|
-
(
|
|
9922
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY2)
|
|
9852
9923
|
],
|
|
9853
9924
|
// Replace (\ ) with ' '
|
|
9854
9925
|
// (\ ) -> ' '
|
|
@@ -9856,7 +9927,7 @@ var require_ignore = __commonJS({
|
|
|
9856
9927
|
// (\\\ ) -> '\\ '
|
|
9857
9928
|
[
|
|
9858
9929
|
/(\\+?)\s/g,
|
|
9859
|
-
(
|
|
9930
|
+
(_, m1) => {
|
|
9860
9931
|
const { length } = m1;
|
|
9861
9932
|
return m1.slice(0, length - length % 2) + SPACE;
|
|
9862
9933
|
}
|
|
@@ -9927,7 +9998,7 @@ var require_ignore = __commonJS({
|
|
|
9927
9998
|
// Zero, one or several directories
|
|
9928
9999
|
// should not use '*', or it will be replaced by the next replacer
|
|
9929
10000
|
// Check if it is not the last `'/**'`
|
|
9930
|
-
(
|
|
10001
|
+
(_, index, str2) => index + 6 < str2.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
9931
10002
|
],
|
|
9932
10003
|
// normal intermediate wildcards
|
|
9933
10004
|
[
|
|
@@ -9939,7 +10010,7 @@ var require_ignore = __commonJS({
|
|
|
9939
10010
|
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
9940
10011
|
// '*.js' matches '.js'
|
|
9941
10012
|
// '*.js' doesn't match 'abc'
|
|
9942
|
-
(
|
|
10013
|
+
(_, p1, p2) => {
|
|
9943
10014
|
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
9944
10015
|
return p1 + unescaped;
|
|
9945
10016
|
}
|
|
@@ -9986,11 +10057,11 @@ var require_ignore = __commonJS({
|
|
|
9986
10057
|
var MODE_CHECK_IGNORE = "checkRegex";
|
|
9987
10058
|
var UNDERSCORE = "_";
|
|
9988
10059
|
var TRAILING_WILD_CARD_REPLACERS = {
|
|
9989
|
-
[MODE_IGNORE](
|
|
10060
|
+
[MODE_IGNORE](_, p1) {
|
|
9990
10061
|
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
9991
10062
|
return `${prefix}(?=$|\\/$)`;
|
|
9992
10063
|
},
|
|
9993
|
-
[MODE_CHECK_IGNORE](
|
|
10064
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
9994
10065
|
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
9995
10066
|
return `${prefix}(?=$|\\/$)`;
|
|
9996
10067
|
}
|
|
@@ -14776,7 +14847,7 @@ var init_ast = __esm({
|
|
|
14776
14847
|
if (!isExtglobAST(this)) {
|
|
14777
14848
|
const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
|
|
14778
14849
|
const src = this.#parts.map((p2) => {
|
|
14779
|
-
const [re2,
|
|
14850
|
+
const [re2, _, hasMagic, uflag] = typeof p2 === "string" ? _a2.#parseGlob(p2, this.#hasMagic, noEmpty) : p2.toRegExpSource(allowDot);
|
|
14780
14851
|
this.#hasMagic = this.#hasMagic || hasMagic;
|
|
14781
14852
|
this.#uflag = this.#uflag || uflag;
|
|
14782
14853
|
return re2;
|
|
@@ -14882,7 +14953,7 @@ var init_ast = __esm({
|
|
|
14882
14953
|
if (typeof p2 === "string") {
|
|
14883
14954
|
throw new Error("string type in extglob ast??");
|
|
14884
14955
|
}
|
|
14885
|
-
const [re2,
|
|
14956
|
+
const [re2, _, _hasMagic, uflag] = p2.toRegExpSource(dot);
|
|
14886
14957
|
this.#uflag = this.#uflag || uflag;
|
|
14887
14958
|
return re2;
|
|
14888
14959
|
}).filter((p2) => !(this.isStart() && this.isEnd()) || !!p2).join("|");
|
|
@@ -14955,7 +15026,7 @@ var init_escape = __esm({
|
|
|
14955
15026
|
});
|
|
14956
15027
|
|
|
14957
15028
|
// node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js
|
|
14958
|
-
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform,
|
|
15029
|
+
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path7, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
|
|
14959
15030
|
var init_esm4 = __esm({
|
|
14960
15031
|
"node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js"() {
|
|
14961
15032
|
init_esm3();
|
|
@@ -15024,11 +15095,11 @@ var init_esm4 = __esm({
|
|
|
15024
15095
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
15025
15096
|
};
|
|
15026
15097
|
defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
15027
|
-
|
|
15098
|
+
path7 = {
|
|
15028
15099
|
win32: { sep: "\\" },
|
|
15029
15100
|
posix: { sep: "/" }
|
|
15030
15101
|
};
|
|
15031
|
-
sep = defaultPlatform === "win32" ?
|
|
15102
|
+
sep = defaultPlatform === "win32" ? path7.win32.sep : path7.posix.sep;
|
|
15032
15103
|
minimatch.sep = sep;
|
|
15033
15104
|
GLOBSTAR = Symbol("globstar **");
|
|
15034
15105
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -15155,7 +15226,7 @@ var init_esm4 = __esm({
|
|
|
15155
15226
|
}
|
|
15156
15227
|
return false;
|
|
15157
15228
|
}
|
|
15158
|
-
debug(...
|
|
15229
|
+
debug(..._) {
|
|
15159
15230
|
}
|
|
15160
15231
|
make() {
|
|
15161
15232
|
const pattern = this.pattern;
|
|
@@ -15177,7 +15248,7 @@ var init_esm4 = __esm({
|
|
|
15177
15248
|
const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
|
|
15178
15249
|
this.globParts = this.preprocess(rawGlobParts);
|
|
15179
15250
|
this.debug(this.pattern, this.globParts);
|
|
15180
|
-
let set2 = this.globParts.map((s,
|
|
15251
|
+
let set2 = this.globParts.map((s, _, __) => {
|
|
15181
15252
|
if (this.isWindows && this.windowsNoMagicRoot) {
|
|
15182
15253
|
const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
|
|
15183
15254
|
const isDrive = /^[a-z]:/i.test(s[0]);
|
|
@@ -17074,7 +17145,7 @@ var init_database_detector = __esm({
|
|
|
17074
17145
|
});
|
|
17075
17146
|
|
|
17076
17147
|
// packages/analyzer/dist/module-extractor.js
|
|
17077
|
-
import
|
|
17148
|
+
import path8 from "path";
|
|
17078
17149
|
function extractModulesAndMethods(analyses, layerDetails, fileDependencies) {
|
|
17079
17150
|
const modules = [];
|
|
17080
17151
|
const methods = [];
|
|
@@ -17221,7 +17292,7 @@ function deriveModuleName(analysis) {
|
|
|
17221
17292
|
return localExports[0].name;
|
|
17222
17293
|
}
|
|
17223
17294
|
const baseName = fileBaseName(analysis.filePath);
|
|
17224
|
-
const strippedName = stripExtension(
|
|
17295
|
+
const strippedName = stripExtension(path8.basename(analysis.filePath));
|
|
17225
17296
|
if (INDEX_NAMES.has(strippedName)) {
|
|
17226
17297
|
return baseName;
|
|
17227
17298
|
}
|
|
@@ -17238,10 +17309,10 @@ function deriveModuleName(analysis) {
|
|
|
17238
17309
|
return baseName;
|
|
17239
17310
|
}
|
|
17240
17311
|
function deriveNextjsRouteName(filePath) {
|
|
17241
|
-
const base =
|
|
17312
|
+
const base = path8.basename(filePath).replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
17242
17313
|
if (base !== "route" && base !== "page")
|
|
17243
17314
|
return null;
|
|
17244
|
-
const parts = filePath.split(
|
|
17315
|
+
const parts = filePath.split(path8.sep);
|
|
17245
17316
|
const appIdx = parts.lastIndexOf("app");
|
|
17246
17317
|
if (appIdx === -1)
|
|
17247
17318
|
return null;
|
|
@@ -17259,9 +17330,9 @@ function stripExtension(filename) {
|
|
|
17259
17330
|
return filename;
|
|
17260
17331
|
}
|
|
17261
17332
|
function fileBaseName(filePath) {
|
|
17262
|
-
const name = stripExtension(
|
|
17333
|
+
const name = stripExtension(path8.basename(filePath));
|
|
17263
17334
|
if (INDEX_NAMES.has(name)) {
|
|
17264
|
-
return
|
|
17335
|
+
return path8.basename(path8.dirname(filePath));
|
|
17265
17336
|
}
|
|
17266
17337
|
return name;
|
|
17267
17338
|
}
|
|
@@ -44024,10 +44095,10 @@ var init_secret_rules = __esm({
|
|
|
44024
44095
|
});
|
|
44025
44096
|
|
|
44026
44097
|
// packages/analyzer/dist/rules/security/secret-scanner.js
|
|
44027
|
-
import
|
|
44098
|
+
import path9 from "node:path";
|
|
44028
44099
|
function isSensitiveFile(filePath) {
|
|
44029
|
-
const basename2 =
|
|
44030
|
-
const ext2 =
|
|
44100
|
+
const basename2 = path9.basename(filePath);
|
|
44101
|
+
const ext2 = path9.extname(filePath);
|
|
44031
44102
|
if (basename2.startsWith(".env")) {
|
|
44032
44103
|
const envVariant = basename2;
|
|
44033
44104
|
if (SENSITIVE_FILE_EXTENSIONS.has(envVariant)) {
|
|
@@ -86710,7 +86781,7 @@ var init_js_naming_convention = __esm({
|
|
|
86710
86781
|
return null;
|
|
86711
86782
|
}
|
|
86712
86783
|
if (funcName.includes("_") && !funcName.startsWith("_")) {
|
|
86713
|
-
return makeViolation(this.ruleKey, node, filePath, "low", "Function uses snake_case naming", `Function '${funcName}' uses snake_case. JavaScript convention is camelCase.`, sourceCode, `Rename to camelCase: ${funcName.replace(/_([a-z])/g, (
|
|
86784
|
+
return makeViolation(this.ruleKey, node, filePath, "low", "Function uses snake_case naming", `Function '${funcName}' uses snake_case. JavaScript convention is camelCase.`, sourceCode, `Rename to camelCase: ${funcName.replace(/_([a-z])/g, (_, c2) => c2.toUpperCase())}.`);
|
|
86714
86785
|
}
|
|
86715
86786
|
return null;
|
|
86716
86787
|
}
|
|
@@ -93628,7 +93699,7 @@ var init_dist6 = __esm({
|
|
|
93628
93699
|
});
|
|
93629
93700
|
|
|
93630
93701
|
// apps/server/dist/services/analyzer.service.js
|
|
93631
|
-
import
|
|
93702
|
+
import path10 from "node:path";
|
|
93632
93703
|
function runDeterministicModuleChecks(result, enabledDeterministic) {
|
|
93633
93704
|
if (!result.modules || !result.methods)
|
|
93634
93705
|
return [];
|
|
@@ -93671,7 +93742,7 @@ async function runAnalysis(repoPath, _branch, onProgress, options) {
|
|
|
93671
93742
|
const statusResult = await git.status();
|
|
93672
93743
|
hasChanges = !statusResult.isClean();
|
|
93673
93744
|
const gitRoot = (await git.revparse(["--show-toplevel"])).trim();
|
|
93674
|
-
isSubdirectory =
|
|
93745
|
+
isSubdirectory = path10.resolve(repoPath) !== path10.resolve(gitRoot);
|
|
93675
93746
|
}
|
|
93676
93747
|
if (hasChanges && !options?.skipStash && !isSubdirectory && git) {
|
|
93677
93748
|
onProgress({ step: "stash", percent: 2, detail: "Stashing pending changes to analyze committed state..." });
|
|
@@ -94005,149 +94076,210 @@ var init_analysis_persistence_service = __esm({
|
|
|
94005
94076
|
}
|
|
94006
94077
|
});
|
|
94007
94078
|
|
|
94008
|
-
// apps/server/dist/
|
|
94009
|
-
import
|
|
94010
|
-
|
|
94011
|
-
|
|
94012
|
-
|
|
94013
|
-
|
|
94014
|
-
|
|
94015
|
-
|
|
94016
|
-
|
|
94017
|
-
|
|
94018
|
-
|
|
94019
|
-
|
|
94020
|
-
|
|
94021
|
-
const file = lockPath(repoPath);
|
|
94022
|
-
fs6.mkdirSync(path9.dirname(file), { recursive: true });
|
|
94023
|
-
try {
|
|
94024
|
-
const fd = fs6.openSync(file, "wx");
|
|
94025
|
-
fs6.writeSync(fd, `${process.pid}
|
|
94026
|
-
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
94027
|
-
`);
|
|
94028
|
-
fs6.closeSync(fd);
|
|
94029
|
-
} catch (err) {
|
|
94030
|
-
if (err.code === "EEXIST") {
|
|
94031
|
-
let owner = null;
|
|
94032
|
-
try {
|
|
94033
|
-
owner = parseInt(fs6.readFileSync(file, "utf-8").split("\n")[0], 10) || null;
|
|
94034
|
-
} catch {
|
|
94079
|
+
// apps/server/dist/services/flow.service.js
|
|
94080
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
94081
|
+
function detectFlows(result) {
|
|
94082
|
+
const dbTypeMap = /* @__PURE__ */ new Map();
|
|
94083
|
+
for (const db of result.databaseResult.databases) {
|
|
94084
|
+
dbTypeMap.set(db.name, db.type);
|
|
94085
|
+
}
|
|
94086
|
+
const functionsByFile = /* @__PURE__ */ new Map();
|
|
94087
|
+
if (result.fileAnalyses) {
|
|
94088
|
+
for (const fa of result.fileAnalyses) {
|
|
94089
|
+
const entries = [];
|
|
94090
|
+
for (const fn of fa.functions) {
|
|
94091
|
+
entries.push({ name: fn.name, startLine: fn.location.startLine, endLine: fn.location.endLine });
|
|
94035
94092
|
}
|
|
94036
|
-
|
|
94093
|
+
for (const cls of fa.classes) {
|
|
94094
|
+
for (const method of cls.methods) {
|
|
94095
|
+
entries.push({ name: method.name, startLine: method.location.startLine, endLine: method.location.endLine });
|
|
94096
|
+
}
|
|
94097
|
+
}
|
|
94098
|
+
if (entries.length > 0)
|
|
94099
|
+
functionsByFile.set(fa.filePath, entries);
|
|
94037
94100
|
}
|
|
94038
|
-
throw err;
|
|
94039
94101
|
}
|
|
94040
|
-
|
|
94041
|
-
|
|
94042
|
-
|
|
94043
|
-
|
|
94044
|
-
} catch (err) {
|
|
94045
|
-
if (err.code !== "ENOENT")
|
|
94046
|
-
throw err;
|
|
94102
|
+
const crossServiceCalls = [];
|
|
94103
|
+
const fileToModule = /* @__PURE__ */ new Map();
|
|
94104
|
+
for (const mod of result.modules) {
|
|
94105
|
+
fileToModule.set(mod.filePath, `${mod.serviceName}::${mod.name}`);
|
|
94047
94106
|
}
|
|
94048
|
-
|
|
94049
|
-
|
|
94050
|
-
|
|
94051
|
-
|
|
94052
|
-
|
|
94053
|
-
|
|
94054
|
-
|
|
94055
|
-
|
|
94056
|
-
|
|
94057
|
-
|
|
94058
|
-
|
|
94107
|
+
const fileToLanguage = /* @__PURE__ */ new Map();
|
|
94108
|
+
if (result.fileAnalyses) {
|
|
94109
|
+
for (const fa of result.fileAnalyses) {
|
|
94110
|
+
fileToLanguage.set(fa.filePath, fa.language);
|
|
94111
|
+
}
|
|
94112
|
+
}
|
|
94113
|
+
for (const dep of result.dependencies) {
|
|
94114
|
+
if (!dep.httpCalls || dep.httpCalls.length === 0)
|
|
94115
|
+
continue;
|
|
94116
|
+
for (const call of dep.httpCalls) {
|
|
94117
|
+
const moduleKey = fileToModule.get(call.location.filePath);
|
|
94118
|
+
if (!moduleKey)
|
|
94119
|
+
continue;
|
|
94120
|
+
const [sourceService, sourceModule] = moduleKey.split("::");
|
|
94121
|
+
let sourceMethod;
|
|
94122
|
+
const fileFunctions = functionsByFile.get(call.location.filePath);
|
|
94123
|
+
if (fileFunctions) {
|
|
94124
|
+
for (const fn of fileFunctions) {
|
|
94125
|
+
if (call.location.startLine >= fn.startLine && call.location.startLine <= fn.endLine) {
|
|
94126
|
+
sourceMethod = fn.name;
|
|
94127
|
+
break;
|
|
94128
|
+
}
|
|
94129
|
+
}
|
|
94059
94130
|
}
|
|
94060
|
-
|
|
94131
|
+
const language = fileToLanguage.get(call.location.filePath);
|
|
94132
|
+
const normalizedUrl = language ? normalizeUrl(call.url, language) : call.url;
|
|
94133
|
+
crossServiceCalls.push({
|
|
94134
|
+
sourceService,
|
|
94135
|
+
sourceModule,
|
|
94136
|
+
sourceMethod,
|
|
94137
|
+
httpMethod: call.method,
|
|
94138
|
+
url: normalizedUrl,
|
|
94139
|
+
targetService: dep.target
|
|
94140
|
+
});
|
|
94141
|
+
}
|
|
94061
94142
|
}
|
|
94062
|
-
|
|
94063
|
-
|
|
94064
|
-
|
|
94065
|
-
|
|
94066
|
-
|
|
94067
|
-
|
|
94068
|
-
|
|
94069
|
-
|
|
94070
|
-
|
|
94071
|
-
|
|
94072
|
-
|
|
94073
|
-
|
|
94074
|
-
|
|
94075
|
-
}
|
|
94076
|
-
|
|
94077
|
-
|
|
94078
|
-
|
|
94079
|
-
|
|
94080
|
-
|
|
94081
|
-
|
|
94082
|
-
|
|
94083
|
-
|
|
94084
|
-
|
|
94085
|
-
|
|
94086
|
-
|
|
94087
|
-
|
|
94088
|
-
|
|
94143
|
+
const routeHandlers = buildRouteHandlerLookup(result);
|
|
94144
|
+
const graph = new AnalysisGraph({
|
|
94145
|
+
methods: result.methods,
|
|
94146
|
+
methodDependencies: result.methodLevelDependencies,
|
|
94147
|
+
modules: result.modules,
|
|
94148
|
+
services: result.services.map((s) => ({ name: s.name, type: s.type })),
|
|
94149
|
+
crossServiceCalls: crossServiceCalls.length > 0 ? crossServiceCalls : void 0,
|
|
94150
|
+
databaseConnections: result.databaseResult.connections.map((c2) => ({
|
|
94151
|
+
serviceName: c2.serviceName,
|
|
94152
|
+
databaseName: c2.databaseName,
|
|
94153
|
+
databaseType: dbTypeMap.get(c2.databaseName) || "unknown"
|
|
94154
|
+
})),
|
|
94155
|
+
routeHandlers: routeHandlers.size > 0 ? routeHandlers : void 0
|
|
94156
|
+
});
|
|
94157
|
+
const traced = traceFlows(graph);
|
|
94158
|
+
const out = traced.map((flow) => ({
|
|
94159
|
+
id: randomUUID2(),
|
|
94160
|
+
name: flow.name,
|
|
94161
|
+
description: null,
|
|
94162
|
+
entryService: flow.entryService,
|
|
94163
|
+
entryMethod: flow.entryMethod,
|
|
94164
|
+
category: flow.category,
|
|
94165
|
+
trigger: flow.trigger,
|
|
94166
|
+
stepCount: flow.steps.length,
|
|
94167
|
+
steps: flow.steps.map((step) => ({
|
|
94168
|
+
stepOrder: step.stepOrder,
|
|
94169
|
+
sourceService: step.sourceService,
|
|
94170
|
+
sourceModule: step.sourceModule,
|
|
94171
|
+
sourceMethod: step.sourceMethod,
|
|
94172
|
+
targetService: step.targetService,
|
|
94173
|
+
targetModule: step.targetModule,
|
|
94174
|
+
targetMethod: step.targetMethod,
|
|
94175
|
+
stepType: step.stepType,
|
|
94176
|
+
dataDescription: null,
|
|
94177
|
+
isAsync: step.isAsync,
|
|
94178
|
+
isConditional: step.isConditional
|
|
94179
|
+
}))
|
|
94180
|
+
}));
|
|
94181
|
+
log.info(`[Flows] Detected ${out.length} flows`);
|
|
94182
|
+
return out;
|
|
94089
94183
|
}
|
|
94090
|
-
function
|
|
94091
|
-
const
|
|
94092
|
-
|
|
94093
|
-
|
|
94094
|
-
|
|
94095
|
-
|
|
94096
|
-
|
|
94097
|
-
|
|
94098
|
-
|
|
94184
|
+
function buildRouteHandlerLookup(result) {
|
|
94185
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
94186
|
+
if (!result.fileAnalyses)
|
|
94187
|
+
return handlers;
|
|
94188
|
+
const fileToService = /* @__PURE__ */ new Map();
|
|
94189
|
+
const fileToModuleName = /* @__PURE__ */ new Map();
|
|
94190
|
+
for (const mod of result.modules) {
|
|
94191
|
+
fileToService.set(mod.filePath, mod.serviceName);
|
|
94192
|
+
fileToModuleName.set(mod.filePath, mod.name);
|
|
94193
|
+
}
|
|
94194
|
+
for (const ld of result.layerDetails) {
|
|
94195
|
+
for (const fp of ld.filePaths) {
|
|
94196
|
+
if (!fileToService.has(fp))
|
|
94197
|
+
fileToService.set(fp, ld.serviceName);
|
|
94099
94198
|
}
|
|
94100
|
-
throw err;
|
|
94101
94199
|
}
|
|
94102
|
-
const
|
|
94103
|
-
|
|
94104
|
-
|
|
94105
|
-
|
|
94106
|
-
|
|
94107
|
-
|
|
94108
|
-
|
|
94109
|
-
|
|
94110
|
-
|
|
94111
|
-
|
|
94112
|
-
|
|
94113
|
-
|
|
94114
|
-
|
|
94115
|
-
|
|
94116
|
-
|
|
94117
|
-
|
|
94118
|
-
|
|
94119
|
-
|
|
94120
|
-
|
|
94121
|
-
|
|
94122
|
-
|
|
94123
|
-
|
|
94124
|
-
|
|
94125
|
-
|
|
94126
|
-
|
|
94127
|
-
|
|
94128
|
-
|
|
94129
|
-
|
|
94130
|
-
|
|
94200
|
+
const fileMountPrefix = /* @__PURE__ */ new Map();
|
|
94201
|
+
for (const fa of result.fileAnalyses) {
|
|
94202
|
+
if (!fa.routerMounts || fa.routerMounts.length === 0)
|
|
94203
|
+
continue;
|
|
94204
|
+
for (const mount of fa.routerMounts) {
|
|
94205
|
+
for (const imp of fa.imports) {
|
|
94206
|
+
const spec = imp.specifiers.find((s) => s.name === mount.routerName || s.alias === mount.routerName);
|
|
94207
|
+
if (spec) {
|
|
94208
|
+
for (const dep of result.moduleDependencies) {
|
|
94209
|
+
if (dep.source === fa.filePath && dep.importedNames.includes(spec.name)) {
|
|
94210
|
+
fileMountPrefix.set(dep.target, mount.path);
|
|
94211
|
+
}
|
|
94212
|
+
}
|
|
94213
|
+
}
|
|
94214
|
+
}
|
|
94215
|
+
const hasLocal = fa.functions.some((f) => f.name === mount.routerName) || fa.exports.some((e) => e.name === mount.routerName);
|
|
94216
|
+
if (hasLocal)
|
|
94217
|
+
fileMountPrefix.set(fa.filePath, mount.path);
|
|
94218
|
+
}
|
|
94219
|
+
}
|
|
94220
|
+
for (const fa of result.fileAnalyses) {
|
|
94221
|
+
if (!fa.routeRegistrations || fa.routeRegistrations.length === 0)
|
|
94222
|
+
continue;
|
|
94223
|
+
const serviceName = fileToService.get(fa.filePath);
|
|
94224
|
+
if (!serviceName)
|
|
94225
|
+
continue;
|
|
94226
|
+
const mountPrefix = fileMountPrefix.get(fa.filePath) || "";
|
|
94227
|
+
for (const route of fa.routeRegistrations) {
|
|
94228
|
+
const fullPath = composePath(mountPrefix, route.path);
|
|
94229
|
+
const moduleName = resolveHandlerModule(route.handlerName, fa, result.fileAnalyses, fileToModuleName);
|
|
94230
|
+
const key = `${serviceName}::${route.httpMethod}::${fullPath}`;
|
|
94231
|
+
handlers.set(key, { handlerName: route.handlerName, moduleName });
|
|
94232
|
+
}
|
|
94233
|
+
}
|
|
94234
|
+
return handlers;
|
|
94131
94235
|
}
|
|
94132
|
-
function
|
|
94133
|
-
|
|
94134
|
-
|
|
94135
|
-
|
|
94136
|
-
|
|
94137
|
-
|
|
94236
|
+
function resolveHandlerModule(handlerName, routeFile, allFiles, fileToModuleName) {
|
|
94237
|
+
for (const cls of routeFile.classes) {
|
|
94238
|
+
for (const method of cls.methods) {
|
|
94239
|
+
if (method.name === handlerName)
|
|
94240
|
+
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
94241
|
+
}
|
|
94138
94242
|
}
|
|
94243
|
+
for (const fn of routeFile.functions) {
|
|
94244
|
+
if (fn.name === handlerName)
|
|
94245
|
+
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
94246
|
+
}
|
|
94247
|
+
for (const imp of routeFile.imports) {
|
|
94248
|
+
const spec = imp.specifiers.find((s) => s.name === handlerName || s.alias === handlerName);
|
|
94249
|
+
if (spec) {
|
|
94250
|
+
for (const targetFile of allFiles) {
|
|
94251
|
+
const hasExport = targetFile.exports.some((e) => e.name === (spec.alias || spec.name) || e.name === spec.name);
|
|
94252
|
+
const hasFunction = targetFile.functions.some((f) => f.name === spec.name);
|
|
94253
|
+
const hasClassMethod = targetFile.classes.some((c2) => c2.methods.some((m) => m.name === spec.name));
|
|
94254
|
+
if (hasExport || hasFunction || hasClassMethod) {
|
|
94255
|
+
return fileToModuleName.get(targetFile.filePath) || handlerName;
|
|
94256
|
+
}
|
|
94257
|
+
}
|
|
94258
|
+
}
|
|
94259
|
+
for (const s of imp.specifiers) {
|
|
94260
|
+
for (const targetFile of allFiles) {
|
|
94261
|
+
for (const cls of targetFile.classes) {
|
|
94262
|
+
if (cls.name === s.name && cls.methods.some((m) => m.name === handlerName)) {
|
|
94263
|
+
return fileToModuleName.get(targetFile.filePath) || cls.name;
|
|
94264
|
+
}
|
|
94265
|
+
}
|
|
94266
|
+
}
|
|
94267
|
+
}
|
|
94268
|
+
}
|
|
94269
|
+
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
94139
94270
|
}
|
|
94140
|
-
|
|
94141
|
-
|
|
94142
|
-
"
|
|
94271
|
+
function composePath(prefix, routePath) {
|
|
94272
|
+
const p2 = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
94273
|
+
const r = routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
94274
|
+
const full = `${p2}${r}`;
|
|
94275
|
+
return full.length > 1 && full.endsWith("/") ? full.slice(0, -1) : full || "/";
|
|
94276
|
+
}
|
|
94277
|
+
var init_flow_service = __esm({
|
|
94278
|
+
"apps/server/dist/services/flow.service.js"() {
|
|
94143
94279
|
"use strict";
|
|
94144
|
-
|
|
94145
|
-
|
|
94146
|
-
|
|
94147
|
-
LATEST_FILE = "LATEST.json";
|
|
94148
|
-
HISTORY_FILE = "history.json";
|
|
94149
|
-
DIFF_FILE = "diff.json";
|
|
94150
|
-
latestCache = /* @__PURE__ */ new Map();
|
|
94280
|
+
init_logger();
|
|
94281
|
+
init_dist6();
|
|
94282
|
+
init_analysis_store();
|
|
94151
94283
|
}
|
|
94152
94284
|
});
|
|
94153
94285
|
|
|
@@ -107143,7 +107275,7 @@ var require_sender = __commonJS({
|
|
|
107143
107275
|
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
|
107144
107276
|
this._bufferedBytes += options[kByteLength];
|
|
107145
107277
|
this._state = DEFLATING;
|
|
107146
|
-
perMessageDeflate.compress(data, options.fin, (
|
|
107278
|
+
perMessageDeflate.compress(data, options.fin, (_, buf) => {
|
|
107147
107279
|
if (this._socket.destroyed) {
|
|
107148
107280
|
const err = new Error(
|
|
107149
107281
|
"The socket was closed while data was being compressed"
|
|
@@ -115967,7 +116099,7 @@ var util, objectUtil, ZodParsedType, getParsedType;
|
|
|
115967
116099
|
var init_util3 = __esm({
|
|
115968
116100
|
"node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js"() {
|
|
115969
116101
|
(function(util2) {
|
|
115970
|
-
util2.assertEqual = (
|
|
116102
|
+
util2.assertEqual = (_) => {
|
|
115971
116103
|
};
|
|
115972
116104
|
function assertIs(_arg) {
|
|
115973
116105
|
}
|
|
@@ -116017,7 +116149,7 @@ var init_util3 = __esm({
|
|
|
116017
116149
|
return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
|
|
116018
116150
|
}
|
|
116019
116151
|
util2.joinValues = joinValues;
|
|
116020
|
-
util2.jsonStringifyReplacer = (
|
|
116152
|
+
util2.jsonStringifyReplacer = (_, value) => {
|
|
116021
116153
|
if (typeof value === "bigint") {
|
|
116022
116154
|
return value.toString();
|
|
116023
116155
|
}
|
|
@@ -120630,9 +120762,12 @@ var init_schemas = __esm({
|
|
|
120630
120762
|
path: external_exports.string().min(1)
|
|
120631
120763
|
});
|
|
120632
120764
|
AnalyzeRepoSchema = external_exports.object({
|
|
120633
|
-
|
|
120634
|
-
|
|
120635
|
-
|
|
120765
|
+
/** Which mode to run — full analyze (HEAD committed state) or diff (working tree
|
|
120766
|
+
* vs LATEST). Required; no silent default. */
|
|
120767
|
+
mode: external_exports.enum(["full", "diff"]),
|
|
120768
|
+
/** Skip git ops (branch detection, commit hash read, pre-parse stash). Useful
|
|
120769
|
+
* for non-git dirs or test environments. No per-repo-config equivalent —
|
|
120770
|
+
* only way to opt out for a single run. */
|
|
120636
120771
|
skipGit: external_exports.boolean().optional().default(false)
|
|
120637
120772
|
});
|
|
120638
120773
|
GenerateViolationsSchema = external_exports.object({
|
|
@@ -120642,80 +120777,6 @@ var init_schemas = __esm({
|
|
|
120642
120777
|
});
|
|
120643
120778
|
|
|
120644
120779
|
// packages/shared/dist/index.js
|
|
120645
|
-
var dist_exports3 = {};
|
|
120646
|
-
__export(dist_exports3, {
|
|
120647
|
-
AnalysisRuleSchema: () => AnalysisRuleSchema,
|
|
120648
|
-
AnalyzeRepoSchema: () => AnalyzeRepoSchema,
|
|
120649
|
-
ArchitectureSchema: () => ArchitectureSchema,
|
|
120650
|
-
ArchitectureSummarySchema: () => ArchitectureSummarySchema,
|
|
120651
|
-
ArchitectureSummaryServiceSchema: () => ArchitectureSummaryServiceSchema,
|
|
120652
|
-
BreakdownResponseSchema: () => BreakdownResponseSchema,
|
|
120653
|
-
CODE_DOMAINS: () => CODE_DOMAINS,
|
|
120654
|
-
CallExpressionSchema: () => CallExpressionSchema,
|
|
120655
|
-
ClassDefinitionSchema: () => ClassDefinitionSchema,
|
|
120656
|
-
ClassPropertySchema: () => ClassPropertySchema,
|
|
120657
|
-
ColumnInfoSchema: () => ColumnInfoSchema,
|
|
120658
|
-
ContextRequirementSchema: () => ContextRequirementSchema,
|
|
120659
|
-
ContextTierSchema: () => ContextTierSchema,
|
|
120660
|
-
CreateRepoSchema: () => CreateRepoSchema,
|
|
120661
|
-
DEFAULT_DOMAINS: () => DEFAULT_DOMAINS,
|
|
120662
|
-
DOMAIN_ORDER: () => DOMAIN_ORDER,
|
|
120663
|
-
DatabaseConnectionInfoSchema: () => DatabaseConnectionInfoSchema,
|
|
120664
|
-
DatabaseDetectionResultSchema: () => DatabaseDetectionResultSchema,
|
|
120665
|
-
DatabaseInfoSchema: () => DatabaseInfoSchema,
|
|
120666
|
-
DatabaseTypeSchema: () => DatabaseTypeSchema,
|
|
120667
|
-
EntityFieldSchema: () => EntityFieldSchema,
|
|
120668
|
-
EntityRelationshipSchema: () => EntityRelationshipSchema,
|
|
120669
|
-
EntitySchema: () => EntitySchema,
|
|
120670
|
-
ExportStatementSchema: () => ExportStatementSchema,
|
|
120671
|
-
FileAnalysisSchema: () => FileAnalysisSchema,
|
|
120672
|
-
FileFilterSchema: () => FileFilterSchema,
|
|
120673
|
-
FlowSchema: () => FlowSchema,
|
|
120674
|
-
FlowStepSchema: () => FlowStepSchema,
|
|
120675
|
-
FlowStepTypeSchema: () => FlowStepTypeSchema,
|
|
120676
|
-
FlowTriggerSchema: () => FlowTriggerSchema,
|
|
120677
|
-
FunctionDefinitionSchema: () => FunctionDefinitionSchema,
|
|
120678
|
-
FunctionFilterSchema: () => FunctionFilterSchema,
|
|
120679
|
-
GenerateViolationsSchema: () => GenerateViolationsSchema,
|
|
120680
|
-
HttpCallSchema: () => HttpCallSchema,
|
|
120681
|
-
ImportSpecifierSchema: () => ImportSpecifierSchema,
|
|
120682
|
-
ImportStatementSchema: () => ImportStatementSchema,
|
|
120683
|
-
IndexInfoSchema: () => IndexInfoSchema,
|
|
120684
|
-
LayerDependencyInfoSchema: () => LayerDependencyInfoSchema,
|
|
120685
|
-
LayerDetailSchema: () => LayerDetailSchema,
|
|
120686
|
-
LayerDetectionResultSchema: () => LayerDetectionResultSchema,
|
|
120687
|
-
LayerSchema: () => LayerSchema,
|
|
120688
|
-
MethodInfoSchema: () => MethodInfoSchema,
|
|
120689
|
-
MethodLevelDependencySchema: () => MethodLevelDependencySchema,
|
|
120690
|
-
ModuleDependencySchema: () => ModuleDependencySchema,
|
|
120691
|
-
ModuleInfoSchema: () => ModuleInfoSchema,
|
|
120692
|
-
ModuleKindSchema: () => ModuleKindSchema,
|
|
120693
|
-
ModuleLevelDependencySchema: () => ModuleLevelDependencySchema,
|
|
120694
|
-
ParameterSchema: () => ParameterSchema,
|
|
120695
|
-
RelationInfoSchema: () => RelationInfoSchema,
|
|
120696
|
-
ResolutionResponseSchema: () => ResolutionResponseSchema,
|
|
120697
|
-
RouteRegistrationSchema: () => RouteRegistrationSchema,
|
|
120698
|
-
RouterMountSchema: () => RouterMountSchema,
|
|
120699
|
-
RuleCategorySchema: () => RuleCategorySchema,
|
|
120700
|
-
RuleDomainSchema: () => RuleDomainSchema,
|
|
120701
|
-
RuleSeveritySchema: () => RuleSeveritySchema,
|
|
120702
|
-
RuleTypeSchema: () => RuleTypeSchema,
|
|
120703
|
-
ServiceDependencyDetailSchema: () => ServiceDependencyDetailSchema,
|
|
120704
|
-
ServiceDependencyInfoSchema: () => ServiceDependencyInfoSchema,
|
|
120705
|
-
ServiceInfoSchema: () => ServiceInfoSchema,
|
|
120706
|
-
ServiceTypeSchema: () => ServiceTypeSchema,
|
|
120707
|
-
SourceLocationSchema: () => SourceLocationSchema,
|
|
120708
|
-
SupportedLanguageSchema: () => SupportedLanguageSchema,
|
|
120709
|
-
TableInfoSchema: () => TableInfoSchema,
|
|
120710
|
-
TopOffenderSchema: () => TopOffenderSchema,
|
|
120711
|
-
TopOffendersResponseSchema: () => TopOffendersResponseSchema,
|
|
120712
|
-
TrendDataPointSchema: () => TrendDataPointSchema,
|
|
120713
|
-
TrendResponseSchema: () => TrendResponseSchema,
|
|
120714
|
-
ViolationSchema: () => ViolationSchema,
|
|
120715
|
-
ViolationSeveritySchema: () => ViolationSeveritySchema,
|
|
120716
|
-
ViolationStatusSchema: () => ViolationStatusSchema,
|
|
120717
|
-
ViolationTypeSchema: () => ViolationTypeSchema
|
|
120718
|
-
});
|
|
120719
120780
|
var init_dist7 = __esm({
|
|
120720
120781
|
"packages/shared/dist/index.js"() {
|
|
120721
120782
|
"use strict";
|
|
@@ -121400,7 +121461,7 @@ function parseStringDef(def, refs) {
|
|
|
121400
121461
|
case "trim":
|
|
121401
121462
|
break;
|
|
121402
121463
|
default:
|
|
121403
|
-
/* @__PURE__ */ ((
|
|
121464
|
+
/* @__PURE__ */ ((_) => {
|
|
121404
121465
|
})(check);
|
|
121405
121466
|
}
|
|
121406
121467
|
}
|
|
@@ -122248,7 +122309,7 @@ var init_selectParser = __esm({
|
|
|
122248
122309
|
case ZodFirstPartyTypeKind.ZodSymbol:
|
|
122249
122310
|
return void 0;
|
|
122250
122311
|
default:
|
|
122251
|
-
return /* @__PURE__ */ ((
|
|
122312
|
+
return /* @__PURE__ */ ((_) => void 0)(typeName);
|
|
122252
122313
|
}
|
|
122253
122314
|
};
|
|
122254
122315
|
}
|
|
@@ -125952,422 +126013,6 @@ var init_violation_pipeline_service = __esm({
|
|
|
125952
126013
|
}
|
|
125953
126014
|
});
|
|
125954
126015
|
|
|
125955
|
-
// apps/server/dist/commands/diff-in-process.js
|
|
125956
|
-
var diff_in_process_exports = {};
|
|
125957
|
-
__export(diff_in_process_exports, {
|
|
125958
|
-
diffInProcess: () => diffInProcess
|
|
125959
|
-
});
|
|
125960
|
-
import path14 from "node:path";
|
|
125961
|
-
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
125962
|
-
async function diffInProcess(project, options = {}) {
|
|
125963
|
-
const latest = readLatest(project.path);
|
|
125964
|
-
if (!latest) {
|
|
125965
|
-
throw new Error("Run a full analysis first before checking a diff.");
|
|
125966
|
-
}
|
|
125967
|
-
const projectConfig = readProjectConfig(project.path);
|
|
125968
|
-
const enabledCategories = options.enabledCategoriesOverride ?? projectConfig.enabledCategories ?? void 0;
|
|
125969
|
-
const enableLlmRules = options.enableLlmRulesOverride ?? projectConfig.enableLlmRules ?? false;
|
|
125970
|
-
const git = await getGit(project.path);
|
|
125971
|
-
const statusResult = await git.status();
|
|
125972
|
-
const changedFiles = [];
|
|
125973
|
-
for (const f of statusResult.not_added)
|
|
125974
|
-
changedFiles.push({ path: f, status: "new" });
|
|
125975
|
-
for (const f of statusResult.created)
|
|
125976
|
-
changedFiles.push({ path: f, status: "new" });
|
|
125977
|
-
for (const f of statusResult.modified)
|
|
125978
|
-
changedFiles.push({ path: f, status: "modified" });
|
|
125979
|
-
for (const f of statusResult.staged) {
|
|
125980
|
-
if (!changedFiles.some((cf) => cf.path === f)) {
|
|
125981
|
-
changedFiles.push({ path: f, status: "modified" });
|
|
125982
|
-
}
|
|
125983
|
-
}
|
|
125984
|
-
for (const f of statusResult.deleted)
|
|
125985
|
-
changedFiles.push({ path: f, status: "deleted" });
|
|
125986
|
-
const commitHash = (await git.revparse(["HEAD"])).trim() || null;
|
|
125987
|
-
options.tracker?.start("parse", "Analyzing working tree...");
|
|
125988
|
-
const result = await runAnalysis(project.path, latest.analysis.branch ?? void 0, (progress) => {
|
|
125989
|
-
options.tracker?.detail("parse", progress.detail ?? "Analyzing...");
|
|
125990
|
-
options.onProgress?.({ detail: progress.detail });
|
|
125991
|
-
}, { signal: options.signal, skipStash: true });
|
|
125992
|
-
if (options.signal?.aborted)
|
|
125993
|
-
throw new DOMException("Diff cancelled", "AbortError");
|
|
125994
|
-
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
125995
|
-
const analysisId = randomUUID7();
|
|
125996
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
125997
|
-
options.tracker?.done("parse", `${result.services.length} services, ${result.fileAnalyses?.length ?? 0} files`);
|
|
125998
|
-
const pipelineResult = await runViolationPipeline({
|
|
125999
|
-
repoPath: project.path,
|
|
126000
|
-
analysisId,
|
|
126001
|
-
now,
|
|
126002
|
-
result,
|
|
126003
|
-
serviceIdMap,
|
|
126004
|
-
moduleIdMap,
|
|
126005
|
-
methodIdMap,
|
|
126006
|
-
dbIdMap,
|
|
126007
|
-
previousActiveViolations: latest.violations,
|
|
126008
|
-
enabledCategories,
|
|
126009
|
-
enableLlmRules,
|
|
126010
|
-
tracker: options.tracker,
|
|
126011
|
-
signal: options.signal
|
|
126012
|
-
});
|
|
126013
|
-
const diff = buildDiffSnapshot({
|
|
126014
|
-
latest,
|
|
126015
|
-
graph,
|
|
126016
|
-
analysisId,
|
|
126017
|
-
now,
|
|
126018
|
-
branch: latest.analysis.branch,
|
|
126019
|
-
commitHash,
|
|
126020
|
-
changedFiles,
|
|
126021
|
-
pipelineResult,
|
|
126022
|
-
repoPath: project.path
|
|
126023
|
-
});
|
|
126024
|
-
writeDiff(project.path, diff);
|
|
126025
|
-
log.info(`[Diff] Done \u2014 ${diff.summary.newCount} new, ${diff.summary.unchangedCount} unchanged, ${diff.summary.resolvedCount} resolved across ${diff.changedFiles.length} changed files`);
|
|
126026
|
-
return { diff, isStale: false };
|
|
126027
|
-
}
|
|
126028
|
-
function buildDiffSnapshot(params) {
|
|
126029
|
-
const { latest, graph, analysisId, branch, commitHash, changedFiles, pipelineResult, repoPath } = params;
|
|
126030
|
-
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126031
|
-
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126032
|
-
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
126033
|
-
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
126034
|
-
const denormalize = (v) => ({
|
|
126035
|
-
...v,
|
|
126036
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
126037
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
126038
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
126039
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
126040
|
-
});
|
|
126041
|
-
const newViolations = pipelineResult.added.map(denormalize);
|
|
126042
|
-
const latestById = new Map(latest.violations.map((v) => [v.id, v]));
|
|
126043
|
-
const resolvedViolations = pipelineResult.resolvedRefs.map((r) => latestById.get(r.id)).filter((v) => !!v);
|
|
126044
|
-
const changedAbs = new Set(changedFiles.map((c2) => path14.resolve(repoPath, c2.path)));
|
|
126045
|
-
const matchesChanged = (p2) => !!p2 && (changedAbs.has(p2) || changedAbs.has(path14.resolve(repoPath, p2)));
|
|
126046
|
-
const affectedModules = graph.modules.filter((m) => matchesChanged(m.filePath));
|
|
126047
|
-
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
126048
|
-
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126049
|
-
const layerKeyById = new Map(graph.layers.map((l) => [l.id, `${l.serviceName}::${l.layer}`]));
|
|
126050
|
-
const affectedServices = /* @__PURE__ */ new Set();
|
|
126051
|
-
const affectedLayers = /* @__PURE__ */ new Set();
|
|
126052
|
-
const affectedModuleKeys = /* @__PURE__ */ new Set();
|
|
126053
|
-
for (const mod of affectedModules) {
|
|
126054
|
-
const svcName = serviceNameById.get(mod.serviceId);
|
|
126055
|
-
if (svcName) {
|
|
126056
|
-
affectedServices.add(svcName);
|
|
126057
|
-
affectedModuleKeys.add(`${svcName}::${mod.name}`);
|
|
126058
|
-
}
|
|
126059
|
-
const layerKey = layerKeyById.get(mod.layerId);
|
|
126060
|
-
if (layerKey)
|
|
126061
|
-
affectedLayers.add(layerKey);
|
|
126062
|
-
}
|
|
126063
|
-
const moduleNameById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126064
|
-
const affectedMethodKeys = [];
|
|
126065
|
-
for (const method of graph.methods) {
|
|
126066
|
-
if (!affectedModuleIdSet.has(method.moduleId))
|
|
126067
|
-
continue;
|
|
126068
|
-
const modName = moduleNameById.get(method.moduleId);
|
|
126069
|
-
const mod = graph.modules.find((m) => m.id === method.moduleId);
|
|
126070
|
-
const svcName = mod ? serviceNameById.get(mod.serviceId) : void 0;
|
|
126071
|
-
if (svcName && modName)
|
|
126072
|
-
affectedMethodKeys.push(`${svcName}::${modName}::${method.name}`);
|
|
126073
|
-
}
|
|
126074
|
-
return {
|
|
126075
|
-
id: analysisId,
|
|
126076
|
-
baseAnalysisId: latest.analysis.id,
|
|
126077
|
-
createdAt: params.now,
|
|
126078
|
-
branch,
|
|
126079
|
-
commitHash,
|
|
126080
|
-
graph,
|
|
126081
|
-
changedFiles,
|
|
126082
|
-
newViolations,
|
|
126083
|
-
resolvedViolations,
|
|
126084
|
-
affectedNodeIds: {
|
|
126085
|
-
services: [...affectedServices],
|
|
126086
|
-
layers: [...affectedLayers],
|
|
126087
|
-
modules: [...affectedModuleKeys],
|
|
126088
|
-
methods: affectedMethodKeys
|
|
126089
|
-
},
|
|
126090
|
-
summary: {
|
|
126091
|
-
newCount: newViolations.length,
|
|
126092
|
-
unchangedCount: pipelineResult.unchanged.length,
|
|
126093
|
-
resolvedCount: resolvedViolations.length
|
|
126094
|
-
}
|
|
126095
|
-
};
|
|
126096
|
-
}
|
|
126097
|
-
var init_diff_in_process = __esm({
|
|
126098
|
-
"apps/server/dist/commands/diff-in-process.js"() {
|
|
126099
|
-
"use strict";
|
|
126100
|
-
init_git();
|
|
126101
|
-
init_logger();
|
|
126102
|
-
init_project_config();
|
|
126103
|
-
init_analyzer_service();
|
|
126104
|
-
init_analysis_persistence_service();
|
|
126105
|
-
init_violation_pipeline_service();
|
|
126106
|
-
init_analysis_store();
|
|
126107
|
-
}
|
|
126108
|
-
});
|
|
126109
|
-
|
|
126110
|
-
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
126111
|
-
var import_index = __toESM(require_commander(), 1);
|
|
126112
|
-
var {
|
|
126113
|
-
program,
|
|
126114
|
-
createCommand,
|
|
126115
|
-
createArgument,
|
|
126116
|
-
createOption,
|
|
126117
|
-
CommanderError,
|
|
126118
|
-
InvalidArgumentError,
|
|
126119
|
-
InvalidOptionArgumentError,
|
|
126120
|
-
// deprecated old name
|
|
126121
|
-
Command,
|
|
126122
|
-
Argument,
|
|
126123
|
-
Option,
|
|
126124
|
-
Help
|
|
126125
|
-
} = import_index.default;
|
|
126126
|
-
|
|
126127
|
-
// tools/cli/src/index.ts
|
|
126128
|
-
init_dist4();
|
|
126129
|
-
|
|
126130
|
-
// tools/cli/src/commands/add.ts
|
|
126131
|
-
init_dist4();
|
|
126132
|
-
init_paths();
|
|
126133
|
-
init_registry();
|
|
126134
|
-
init_helpers();
|
|
126135
|
-
async function runAdd() {
|
|
126136
|
-
const repoPath = resolveRepoDir(process.cwd()) ?? process.cwd();
|
|
126137
|
-
mt("Adding repository to TrueCourse");
|
|
126138
|
-
O2.step(repoPath);
|
|
126139
|
-
ensureRepoTruecourseDir(repoPath);
|
|
126140
|
-
const existing = getProjectByPath(repoPath);
|
|
126141
|
-
const entry = registerProject(repoPath);
|
|
126142
|
-
if (existing) {
|
|
126143
|
-
O2.info(`Repository "${entry.name}" is already registered.`);
|
|
126144
|
-
} else {
|
|
126145
|
-
O2.success(`Repository "${entry.name}" added.`);
|
|
126146
|
-
}
|
|
126147
|
-
await promptInstallSkills(repoPath);
|
|
126148
|
-
gt("Run `truecourse analyze` to generate analysis data.");
|
|
126149
|
-
}
|
|
126150
|
-
|
|
126151
|
-
// tools/cli/src/commands/analyze.ts
|
|
126152
|
-
init_dist4();
|
|
126153
|
-
import { execSync } from "node:child_process";
|
|
126154
|
-
import path15 from "node:path";
|
|
126155
|
-
|
|
126156
|
-
// apps/server/dist/commands/analyze-in-process.js
|
|
126157
|
-
init_logger();
|
|
126158
|
-
init_git();
|
|
126159
|
-
init_project_config();
|
|
126160
|
-
init_registry();
|
|
126161
|
-
init_analyzer_service();
|
|
126162
|
-
init_analysis_persistence_service();
|
|
126163
|
-
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
126164
|
-
|
|
126165
|
-
// apps/server/dist/services/flow.service.js
|
|
126166
|
-
init_logger();
|
|
126167
|
-
init_dist6();
|
|
126168
|
-
init_analysis_store();
|
|
126169
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
126170
|
-
function detectFlows(result) {
|
|
126171
|
-
const dbTypeMap = /* @__PURE__ */ new Map();
|
|
126172
|
-
for (const db of result.databaseResult.databases) {
|
|
126173
|
-
dbTypeMap.set(db.name, db.type);
|
|
126174
|
-
}
|
|
126175
|
-
const functionsByFile = /* @__PURE__ */ new Map();
|
|
126176
|
-
if (result.fileAnalyses) {
|
|
126177
|
-
for (const fa of result.fileAnalyses) {
|
|
126178
|
-
const entries = [];
|
|
126179
|
-
for (const fn of fa.functions) {
|
|
126180
|
-
entries.push({ name: fn.name, startLine: fn.location.startLine, endLine: fn.location.endLine });
|
|
126181
|
-
}
|
|
126182
|
-
for (const cls of fa.classes) {
|
|
126183
|
-
for (const method of cls.methods) {
|
|
126184
|
-
entries.push({ name: method.name, startLine: method.location.startLine, endLine: method.location.endLine });
|
|
126185
|
-
}
|
|
126186
|
-
}
|
|
126187
|
-
if (entries.length > 0)
|
|
126188
|
-
functionsByFile.set(fa.filePath, entries);
|
|
126189
|
-
}
|
|
126190
|
-
}
|
|
126191
|
-
const crossServiceCalls = [];
|
|
126192
|
-
const fileToModule = /* @__PURE__ */ new Map();
|
|
126193
|
-
for (const mod of result.modules) {
|
|
126194
|
-
fileToModule.set(mod.filePath, `${mod.serviceName}::${mod.name}`);
|
|
126195
|
-
}
|
|
126196
|
-
const fileToLanguage = /* @__PURE__ */ new Map();
|
|
126197
|
-
if (result.fileAnalyses) {
|
|
126198
|
-
for (const fa of result.fileAnalyses) {
|
|
126199
|
-
fileToLanguage.set(fa.filePath, fa.language);
|
|
126200
|
-
}
|
|
126201
|
-
}
|
|
126202
|
-
for (const dep of result.dependencies) {
|
|
126203
|
-
if (!dep.httpCalls || dep.httpCalls.length === 0)
|
|
126204
|
-
continue;
|
|
126205
|
-
for (const call of dep.httpCalls) {
|
|
126206
|
-
const moduleKey = fileToModule.get(call.location.filePath);
|
|
126207
|
-
if (!moduleKey)
|
|
126208
|
-
continue;
|
|
126209
|
-
const [sourceService, sourceModule] = moduleKey.split("::");
|
|
126210
|
-
let sourceMethod;
|
|
126211
|
-
const fileFunctions = functionsByFile.get(call.location.filePath);
|
|
126212
|
-
if (fileFunctions) {
|
|
126213
|
-
for (const fn of fileFunctions) {
|
|
126214
|
-
if (call.location.startLine >= fn.startLine && call.location.startLine <= fn.endLine) {
|
|
126215
|
-
sourceMethod = fn.name;
|
|
126216
|
-
break;
|
|
126217
|
-
}
|
|
126218
|
-
}
|
|
126219
|
-
}
|
|
126220
|
-
const language = fileToLanguage.get(call.location.filePath);
|
|
126221
|
-
const normalizedUrl = language ? normalizeUrl(call.url, language) : call.url;
|
|
126222
|
-
crossServiceCalls.push({
|
|
126223
|
-
sourceService,
|
|
126224
|
-
sourceModule,
|
|
126225
|
-
sourceMethod,
|
|
126226
|
-
httpMethod: call.method,
|
|
126227
|
-
url: normalizedUrl,
|
|
126228
|
-
targetService: dep.target
|
|
126229
|
-
});
|
|
126230
|
-
}
|
|
126231
|
-
}
|
|
126232
|
-
const routeHandlers = buildRouteHandlerLookup(result);
|
|
126233
|
-
const graph = new AnalysisGraph({
|
|
126234
|
-
methods: result.methods,
|
|
126235
|
-
methodDependencies: result.methodLevelDependencies,
|
|
126236
|
-
modules: result.modules,
|
|
126237
|
-
services: result.services.map((s) => ({ name: s.name, type: s.type })),
|
|
126238
|
-
crossServiceCalls: crossServiceCalls.length > 0 ? crossServiceCalls : void 0,
|
|
126239
|
-
databaseConnections: result.databaseResult.connections.map((c2) => ({
|
|
126240
|
-
serviceName: c2.serviceName,
|
|
126241
|
-
databaseName: c2.databaseName,
|
|
126242
|
-
databaseType: dbTypeMap.get(c2.databaseName) || "unknown"
|
|
126243
|
-
})),
|
|
126244
|
-
routeHandlers: routeHandlers.size > 0 ? routeHandlers : void 0
|
|
126245
|
-
});
|
|
126246
|
-
const traced = traceFlows(graph);
|
|
126247
|
-
const out = traced.map((flow) => ({
|
|
126248
|
-
id: randomUUID2(),
|
|
126249
|
-
name: flow.name,
|
|
126250
|
-
description: null,
|
|
126251
|
-
entryService: flow.entryService,
|
|
126252
|
-
entryMethod: flow.entryMethod,
|
|
126253
|
-
category: flow.category,
|
|
126254
|
-
trigger: flow.trigger,
|
|
126255
|
-
stepCount: flow.steps.length,
|
|
126256
|
-
steps: flow.steps.map((step) => ({
|
|
126257
|
-
stepOrder: step.stepOrder,
|
|
126258
|
-
sourceService: step.sourceService,
|
|
126259
|
-
sourceModule: step.sourceModule,
|
|
126260
|
-
sourceMethod: step.sourceMethod,
|
|
126261
|
-
targetService: step.targetService,
|
|
126262
|
-
targetModule: step.targetModule,
|
|
126263
|
-
targetMethod: step.targetMethod,
|
|
126264
|
-
stepType: step.stepType,
|
|
126265
|
-
dataDescription: null,
|
|
126266
|
-
isAsync: step.isAsync,
|
|
126267
|
-
isConditional: step.isConditional
|
|
126268
|
-
}))
|
|
126269
|
-
}));
|
|
126270
|
-
log.info(`[Flows] Detected ${out.length} flows`);
|
|
126271
|
-
return out;
|
|
126272
|
-
}
|
|
126273
|
-
function buildRouteHandlerLookup(result) {
|
|
126274
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
126275
|
-
if (!result.fileAnalyses)
|
|
126276
|
-
return handlers;
|
|
126277
|
-
const fileToService = /* @__PURE__ */ new Map();
|
|
126278
|
-
const fileToModuleName = /* @__PURE__ */ new Map();
|
|
126279
|
-
for (const mod of result.modules) {
|
|
126280
|
-
fileToService.set(mod.filePath, mod.serviceName);
|
|
126281
|
-
fileToModuleName.set(mod.filePath, mod.name);
|
|
126282
|
-
}
|
|
126283
|
-
for (const ld of result.layerDetails) {
|
|
126284
|
-
for (const fp of ld.filePaths) {
|
|
126285
|
-
if (!fileToService.has(fp))
|
|
126286
|
-
fileToService.set(fp, ld.serviceName);
|
|
126287
|
-
}
|
|
126288
|
-
}
|
|
126289
|
-
const fileMountPrefix = /* @__PURE__ */ new Map();
|
|
126290
|
-
for (const fa of result.fileAnalyses) {
|
|
126291
|
-
if (!fa.routerMounts || fa.routerMounts.length === 0)
|
|
126292
|
-
continue;
|
|
126293
|
-
for (const mount of fa.routerMounts) {
|
|
126294
|
-
for (const imp of fa.imports) {
|
|
126295
|
-
const spec = imp.specifiers.find((s) => s.name === mount.routerName || s.alias === mount.routerName);
|
|
126296
|
-
if (spec) {
|
|
126297
|
-
for (const dep of result.moduleDependencies) {
|
|
126298
|
-
if (dep.source === fa.filePath && dep.importedNames.includes(spec.name)) {
|
|
126299
|
-
fileMountPrefix.set(dep.target, mount.path);
|
|
126300
|
-
}
|
|
126301
|
-
}
|
|
126302
|
-
}
|
|
126303
|
-
}
|
|
126304
|
-
const hasLocal = fa.functions.some((f) => f.name === mount.routerName) || fa.exports.some((e) => e.name === mount.routerName);
|
|
126305
|
-
if (hasLocal)
|
|
126306
|
-
fileMountPrefix.set(fa.filePath, mount.path);
|
|
126307
|
-
}
|
|
126308
|
-
}
|
|
126309
|
-
for (const fa of result.fileAnalyses) {
|
|
126310
|
-
if (!fa.routeRegistrations || fa.routeRegistrations.length === 0)
|
|
126311
|
-
continue;
|
|
126312
|
-
const serviceName = fileToService.get(fa.filePath);
|
|
126313
|
-
if (!serviceName)
|
|
126314
|
-
continue;
|
|
126315
|
-
const mountPrefix = fileMountPrefix.get(fa.filePath) || "";
|
|
126316
|
-
for (const route of fa.routeRegistrations) {
|
|
126317
|
-
const fullPath = composePath(mountPrefix, route.path);
|
|
126318
|
-
const moduleName = resolveHandlerModule(route.handlerName, fa, result.fileAnalyses, fileToModuleName);
|
|
126319
|
-
const key = `${serviceName}::${route.httpMethod}::${fullPath}`;
|
|
126320
|
-
handlers.set(key, { handlerName: route.handlerName, moduleName });
|
|
126321
|
-
}
|
|
126322
|
-
}
|
|
126323
|
-
return handlers;
|
|
126324
|
-
}
|
|
126325
|
-
function resolveHandlerModule(handlerName, routeFile, allFiles, fileToModuleName) {
|
|
126326
|
-
for (const cls of routeFile.classes) {
|
|
126327
|
-
for (const method of cls.methods) {
|
|
126328
|
-
if (method.name === handlerName)
|
|
126329
|
-
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
126330
|
-
}
|
|
126331
|
-
}
|
|
126332
|
-
for (const fn of routeFile.functions) {
|
|
126333
|
-
if (fn.name === handlerName)
|
|
126334
|
-
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
126335
|
-
}
|
|
126336
|
-
for (const imp of routeFile.imports) {
|
|
126337
|
-
const spec = imp.specifiers.find((s) => s.name === handlerName || s.alias === handlerName);
|
|
126338
|
-
if (spec) {
|
|
126339
|
-
for (const targetFile of allFiles) {
|
|
126340
|
-
const hasExport = targetFile.exports.some((e) => e.name === (spec.alias || spec.name) || e.name === spec.name);
|
|
126341
|
-
const hasFunction = targetFile.functions.some((f) => f.name === spec.name);
|
|
126342
|
-
const hasClassMethod = targetFile.classes.some((c2) => c2.methods.some((m) => m.name === spec.name));
|
|
126343
|
-
if (hasExport || hasFunction || hasClassMethod) {
|
|
126344
|
-
return fileToModuleName.get(targetFile.filePath) || handlerName;
|
|
126345
|
-
}
|
|
126346
|
-
}
|
|
126347
|
-
}
|
|
126348
|
-
for (const s of imp.specifiers) {
|
|
126349
|
-
for (const targetFile of allFiles) {
|
|
126350
|
-
for (const cls of targetFile.classes) {
|
|
126351
|
-
if (cls.name === s.name && cls.methods.some((m) => m.name === handlerName)) {
|
|
126352
|
-
return fileToModuleName.get(targetFile.filePath) || cls.name;
|
|
126353
|
-
}
|
|
126354
|
-
}
|
|
126355
|
-
}
|
|
126356
|
-
}
|
|
126357
|
-
}
|
|
126358
|
-
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
126359
|
-
}
|
|
126360
|
-
function composePath(prefix, routePath) {
|
|
126361
|
-
const p2 = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
126362
|
-
const r = routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
126363
|
-
const full = `${p2}${r}`;
|
|
126364
|
-
return full.length > 1 && full.endsWith("/") ? full.slice(0, -1) : full || "/";
|
|
126365
|
-
}
|
|
126366
|
-
|
|
126367
|
-
// apps/server/dist/commands/analyze-in-process.js
|
|
126368
|
-
init_violation_pipeline_service();
|
|
126369
|
-
init_provider();
|
|
126370
|
-
|
|
126371
126016
|
// apps/server/dist/services/usage.service.js
|
|
126372
126017
|
function toUsageRecords(records) {
|
|
126373
126018
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -126384,11 +126029,15 @@ function toUsageRecords(records) {
|
|
|
126384
126029
|
createdAt: now
|
|
126385
126030
|
}));
|
|
126386
126031
|
}
|
|
126032
|
+
var init_usage_service = __esm({
|
|
126033
|
+
"apps/server/dist/services/usage.service.js"() {
|
|
126034
|
+
"use strict";
|
|
126035
|
+
}
|
|
126036
|
+
});
|
|
126387
126037
|
|
|
126388
|
-
// apps/server/dist/commands/analyze-
|
|
126389
|
-
|
|
126390
|
-
|
|
126391
|
-
async function analyzeInProcess(project, options = {}) {
|
|
126038
|
+
// apps/server/dist/commands/analyze-core.js
|
|
126039
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
126040
|
+
async function analyzeCore(project, options) {
|
|
126392
126041
|
try {
|
|
126393
126042
|
acquireAnalyzeLock(project.path);
|
|
126394
126043
|
} catch (err) {
|
|
@@ -126397,12 +126046,27 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126397
126046
|
throw err;
|
|
126398
126047
|
}
|
|
126399
126048
|
try {
|
|
126400
|
-
const
|
|
126401
|
-
const
|
|
126049
|
+
const { mode, signal } = options;
|
|
126050
|
+
const isDiff = mode === "diff";
|
|
126051
|
+
const skipGit = !isDiff && !!options.skipGit;
|
|
126402
126052
|
const projectConfig = readProjectConfig(project.path);
|
|
126053
|
+
const latestBaseline = readLatest(project.path);
|
|
126054
|
+
if (isDiff && !latestBaseline) {
|
|
126055
|
+
throw new Error("Run a full analysis first before checking a diff.");
|
|
126056
|
+
}
|
|
126403
126057
|
let branch = options.branch ?? null;
|
|
126404
126058
|
let commitHash = options.commitHash ?? null;
|
|
126405
|
-
if (
|
|
126059
|
+
if (isDiff) {
|
|
126060
|
+
branch = latestBaseline.analysis.branch ?? branch;
|
|
126061
|
+
if (commitHash === null) {
|
|
126062
|
+
try {
|
|
126063
|
+
const git = await getGit(project.path);
|
|
126064
|
+
commitHash = (await git.revparse(["HEAD"])).trim() || null;
|
|
126065
|
+
} catch {
|
|
126066
|
+
commitHash = null;
|
|
126067
|
+
}
|
|
126068
|
+
}
|
|
126069
|
+
} else if (!skipGit && (branch === null || commitHash === null)) {
|
|
126406
126070
|
const git = await getGit(project.path);
|
|
126407
126071
|
if (branch === null)
|
|
126408
126072
|
branch = (await git.branch()).current || null;
|
|
@@ -126411,39 +126075,62 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126411
126075
|
}
|
|
126412
126076
|
const analysisId = randomUUID6();
|
|
126413
126077
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
126414
|
-
const
|
|
126078
|
+
const start = Date.now();
|
|
126415
126079
|
const effectiveCategories = options.enabledCategoriesOverride?.length ? options.enabledCategoriesOverride : projectConfig.enabledCategories ?? void 0;
|
|
126416
126080
|
const effectiveLlmRules = projectConfig.enableLlmRules ?? options.enableLlmRulesOverride ?? true;
|
|
126417
|
-
options.tracker?.start("parse", "Starting analysis...");
|
|
126081
|
+
options.tracker?.start("parse", isDiff ? "Analyzing working tree..." : "Starting analysis...");
|
|
126418
126082
|
const result = await runAnalysis(project.path, branch ?? void 0, (progress) => {
|
|
126419
126083
|
options.tracker?.detail("parse", progress.detail ?? "Analyzing...");
|
|
126420
126084
|
options.onProgress?.({ detail: progress.detail });
|
|
126421
|
-
}, { signal });
|
|
126422
|
-
if (signal?.aborted)
|
|
126423
|
-
throw new DOMException("Analysis cancelled", "AbortError");
|
|
126424
|
-
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
126425
|
-
const previousLatest = readLatest(project.path);
|
|
126426
|
-
const previousAnalysisId = previousLatest?.analysis.id ?? null;
|
|
126427
|
-
const previousActiveViolations = previousLatest ? previousLatest.violations.filter((v) => branch == null || previousLatest.analysis.branch == null || previousLatest.analysis.branch === branch) : [];
|
|
126428
|
-
try {
|
|
126429
|
-
graph.flows = detectFlows(result);
|
|
126430
|
-
} catch (flowError) {
|
|
126431
|
-
log.error(`[Flows] Detection failed: ${flowError instanceof Error ? flowError.message : String(flowError)}`);
|
|
126432
|
-
graph.flows = [];
|
|
126085
|
+
}, { signal, skipStash: isDiff });
|
|
126086
|
+
if (signal?.aborted) {
|
|
126087
|
+
throw new DOMException(isDiff ? "Diff cancelled" : "Analysis cancelled", "AbortError");
|
|
126433
126088
|
}
|
|
126434
|
-
|
|
126089
|
+
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
126090
|
+
let changedFiles = [];
|
|
126435
126091
|
let changedFileSet;
|
|
126436
|
-
if (
|
|
126092
|
+
if (isDiff) {
|
|
126437
126093
|
try {
|
|
126438
126094
|
const git = await getGit(project.path);
|
|
126439
|
-
const
|
|
126095
|
+
const statusResult = await git.status();
|
|
126096
|
+
for (const f of statusResult.not_added)
|
|
126097
|
+
changedFiles.push({ path: f, status: "new" });
|
|
126098
|
+
for (const f of statusResult.created)
|
|
126099
|
+
changedFiles.push({ path: f, status: "new" });
|
|
126100
|
+
for (const f of statusResult.modified)
|
|
126101
|
+
changedFiles.push({ path: f, status: "modified" });
|
|
126102
|
+
for (const f of statusResult.staged) {
|
|
126103
|
+
if (!changedFiles.some((cf) => cf.path === f)) {
|
|
126104
|
+
changedFiles.push({ path: f, status: "modified" });
|
|
126105
|
+
}
|
|
126106
|
+
}
|
|
126107
|
+
for (const f of statusResult.deleted)
|
|
126108
|
+
changedFiles.push({ path: f, status: "deleted" });
|
|
126109
|
+
} catch (err) {
|
|
126110
|
+
log.warn(`[Diff] git status failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
126111
|
+
}
|
|
126112
|
+
} else if (latestBaseline?.analysis.commitHash && !skipGit) {
|
|
126113
|
+
try {
|
|
126114
|
+
const git = await getGit(project.path);
|
|
126115
|
+
const diffOutput = await git.diff([latestBaseline.analysis.commitHash, "HEAD", "--name-only"]);
|
|
126440
126116
|
const files = diffOutput.trim().split("\n").filter(Boolean);
|
|
126441
126117
|
if (files.length > 0)
|
|
126442
126118
|
changedFileSet = new Set(files);
|
|
126443
126119
|
} catch {
|
|
126444
126120
|
}
|
|
126445
126121
|
}
|
|
126122
|
+
if (!isDiff) {
|
|
126123
|
+
try {
|
|
126124
|
+
graph.flows = detectFlows(result);
|
|
126125
|
+
} catch (flowError) {
|
|
126126
|
+
log.error(`[Flows] Detection failed: ${flowError instanceof Error ? flowError.message : String(flowError)}`);
|
|
126127
|
+
graph.flows = [];
|
|
126128
|
+
}
|
|
126129
|
+
touchProject(project.slug);
|
|
126130
|
+
}
|
|
126446
126131
|
options.tracker?.done("parse", `${result.services.length} services, ${result.fileAnalyses?.length ?? 0} files`);
|
|
126132
|
+
const previousActiveViolations = latestBaseline ? latestBaseline.violations.filter((v) => branch == null || latestBaseline.analysis.branch == null || latestBaseline.analysis.branch === branch) : [];
|
|
126133
|
+
const previousAnalysisId = latestBaseline?.analysis.id ?? null;
|
|
126447
126134
|
const provider = options.provider ?? (effectiveLlmRules ? createLLMProvider() : void 0);
|
|
126448
126135
|
if (provider) {
|
|
126449
126136
|
provider.setAnalysisId(analysisId);
|
|
@@ -126451,32 +126138,28 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126451
126138
|
if (signal)
|
|
126452
126139
|
provider.setAbortSignal(signal);
|
|
126453
126140
|
}
|
|
126454
|
-
|
|
126455
|
-
|
|
126456
|
-
|
|
126457
|
-
|
|
126458
|
-
|
|
126459
|
-
|
|
126460
|
-
|
|
126461
|
-
|
|
126462
|
-
|
|
126463
|
-
|
|
126464
|
-
|
|
126465
|
-
|
|
126466
|
-
|
|
126467
|
-
|
|
126468
|
-
|
|
126469
|
-
|
|
126470
|
-
|
|
126471
|
-
|
|
126472
|
-
|
|
126473
|
-
|
|
126474
|
-
|
|
126475
|
-
|
|
126476
|
-
} : void 0
|
|
126477
|
-
});
|
|
126478
|
-
} finally {
|
|
126479
|
-
}
|
|
126141
|
+
const pipelineResult = await runViolationPipeline({
|
|
126142
|
+
repoPath: project.path,
|
|
126143
|
+
analysisId,
|
|
126144
|
+
now,
|
|
126145
|
+
result,
|
|
126146
|
+
serviceIdMap,
|
|
126147
|
+
moduleIdMap,
|
|
126148
|
+
methodIdMap,
|
|
126149
|
+
dbIdMap,
|
|
126150
|
+
previousActiveViolations,
|
|
126151
|
+
changedFileSet,
|
|
126152
|
+
tracker: options.tracker,
|
|
126153
|
+
enabledCategories: effectiveCategories,
|
|
126154
|
+
enableLlmRules: effectiveLlmRules,
|
|
126155
|
+
provider,
|
|
126156
|
+
signal,
|
|
126157
|
+
onLlmEstimate: options.onLlmEstimate ? async (estimate) => {
|
|
126158
|
+
const proceed = await options.onLlmEstimate(estimate);
|
|
126159
|
+
options.onLlmResolved?.(proceed);
|
|
126160
|
+
return proceed;
|
|
126161
|
+
} : void 0
|
|
126162
|
+
});
|
|
126480
126163
|
if (pipelineResult.serviceDescriptions.length > 0) {
|
|
126481
126164
|
for (const desc of pipelineResult.serviceDescriptions) {
|
|
126482
126165
|
const svc = graph.services.find((s) => s.id === desc.id);
|
|
@@ -126488,56 +126171,105 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126488
126171
|
enforceLocationInvariant(pipelineResult.added);
|
|
126489
126172
|
enforceLocationInvariant(pipelineResult.unchanged);
|
|
126490
126173
|
enforceLocationInvariant(pipelineResult.resolved);
|
|
126491
|
-
|
|
126492
|
-
|
|
126493
|
-
|
|
126174
|
+
log.info(`[${isDiff ? "Diff" : "Analysis"}] core complete in ${Date.now() - start}ms \u2014 ${pipelineResult.added.length} added, ${pipelineResult.unchanged.length} unchanged, ${pipelineResult.resolvedRefs.length} resolved`);
|
|
126175
|
+
return {
|
|
126176
|
+
mode,
|
|
126177
|
+
analysisId,
|
|
126178
|
+
now,
|
|
126494
126179
|
branch,
|
|
126495
126180
|
commitHash,
|
|
126496
126181
|
architecture: result.architecture,
|
|
126497
|
-
status: "completed",
|
|
126498
126182
|
metadata: result.metadata ?? null,
|
|
126499
126183
|
graph,
|
|
126500
|
-
|
|
126501
|
-
|
|
126502
|
-
|
|
126503
|
-
|
|
126504
|
-
|
|
126505
|
-
|
|
126506
|
-
};
|
|
126507
|
-
const latest = buildLatestSnapshot(snapshot, filename, pipelineResult.unchanged, pipelineResult.added);
|
|
126508
|
-
const { bySeverity, total } = summarizeActiveViolations(latest.violations);
|
|
126509
|
-
writeAnalysis(project.path, snapshot);
|
|
126510
|
-
writeLatest(project.path, latest);
|
|
126511
|
-
const historyEntry = buildHistoryEntry(snapshot, filename, pipelineResult);
|
|
126512
|
-
appendHistory(project.path, historyEntry);
|
|
126513
|
-
deleteDiff(project.path);
|
|
126514
|
-
setLastAnalyzed(project.slug, now);
|
|
126515
|
-
return {
|
|
126516
|
-
analysisId,
|
|
126517
|
-
filename,
|
|
126518
|
-
serviceCount: result.services.length,
|
|
126519
|
-
fileCount: result.fileAnalyses?.length ?? 0,
|
|
126520
|
-
architecture: result.architecture,
|
|
126521
|
-
durationMs: Date.now() - start,
|
|
126522
|
-
violationsSummary: { total, bySeverity }
|
|
126184
|
+
changedFiles,
|
|
126185
|
+
pipelineResult,
|
|
126186
|
+
usage,
|
|
126187
|
+
latestBaseline,
|
|
126188
|
+
previousAnalysisId,
|
|
126189
|
+
analysisResult: result
|
|
126523
126190
|
};
|
|
126524
126191
|
} finally {
|
|
126525
126192
|
releaseAnalyzeLock(project.path);
|
|
126526
126193
|
}
|
|
126527
126194
|
}
|
|
126195
|
+
function enforceLocationInvariant(violations) {
|
|
126196
|
+
for (const v of violations) {
|
|
126197
|
+
const hasFile = v.filePath != null;
|
|
126198
|
+
const hasRange = v.lineStart != null && v.lineEnd != null;
|
|
126199
|
+
if (hasFile === hasRange)
|
|
126200
|
+
continue;
|
|
126201
|
+
log.warn(`[Violations] ${v.ruleKey}: partial location (filePath=${v.filePath}, lineStart=${v.lineStart}, lineEnd=${v.lineEnd}) \u2014 dropping to uphold the location invariant`);
|
|
126202
|
+
v.filePath = null;
|
|
126203
|
+
v.lineStart = null;
|
|
126204
|
+
v.lineEnd = null;
|
|
126205
|
+
}
|
|
126206
|
+
}
|
|
126207
|
+
var init_analyze_core = __esm({
|
|
126208
|
+
"apps/server/dist/commands/analyze-core.js"() {
|
|
126209
|
+
"use strict";
|
|
126210
|
+
init_logger();
|
|
126211
|
+
init_git();
|
|
126212
|
+
init_project_config();
|
|
126213
|
+
init_registry();
|
|
126214
|
+
init_analyzer_service();
|
|
126215
|
+
init_analysis_persistence_service();
|
|
126216
|
+
init_flow_service();
|
|
126217
|
+
init_violation_pipeline_service();
|
|
126218
|
+
init_provider();
|
|
126219
|
+
init_usage_service();
|
|
126220
|
+
init_analysis_store();
|
|
126221
|
+
init_atomic_write();
|
|
126222
|
+
}
|
|
126223
|
+
});
|
|
126224
|
+
|
|
126225
|
+
// apps/server/dist/commands/analyze-persist.js
|
|
126226
|
+
import path13 from "node:path";
|
|
126227
|
+
function persistFullAnalysis(project, core2, startedAt) {
|
|
126228
|
+
const filename = buildAnalysisFilename(core2.analysisId, core2.now);
|
|
126229
|
+
const snapshot = {
|
|
126230
|
+
id: core2.analysisId,
|
|
126231
|
+
createdAt: core2.now,
|
|
126232
|
+
branch: core2.branch,
|
|
126233
|
+
commitHash: core2.commitHash,
|
|
126234
|
+
architecture: core2.architecture,
|
|
126235
|
+
status: "completed",
|
|
126236
|
+
metadata: core2.metadata,
|
|
126237
|
+
graph: core2.graph,
|
|
126238
|
+
violations: {
|
|
126239
|
+
added: core2.pipelineResult.added,
|
|
126240
|
+
resolved: core2.pipelineResult.resolvedRefs,
|
|
126241
|
+
previousAnalysisId: core2.previousAnalysisId
|
|
126242
|
+
},
|
|
126243
|
+
usage: core2.usage
|
|
126244
|
+
};
|
|
126245
|
+
const latest = buildLatestSnapshot(snapshot, filename, core2.pipelineResult.unchanged, core2.pipelineResult.added);
|
|
126246
|
+
const { bySeverity, total } = summarizeActiveViolations(latest.violations);
|
|
126247
|
+
writeAnalysis(project.path, snapshot);
|
|
126248
|
+
writeLatest(project.path, latest);
|
|
126249
|
+
appendHistory(project.path, buildHistoryEntry(snapshot, filename, core2.pipelineResult));
|
|
126250
|
+
deleteDiff(project.path);
|
|
126251
|
+
setLastAnalyzed(project.slug, core2.now);
|
|
126252
|
+
return {
|
|
126253
|
+
analysisId: core2.analysisId,
|
|
126254
|
+
filename,
|
|
126255
|
+
serviceCount: core2.graph.services.length,
|
|
126256
|
+
fileCount: core2.analysisResult.fileAnalyses?.length ?? 0,
|
|
126257
|
+
architecture: core2.architecture,
|
|
126258
|
+
durationMs: Date.now() - startedAt,
|
|
126259
|
+
violationsSummary: { total, bySeverity }
|
|
126260
|
+
};
|
|
126261
|
+
}
|
|
126262
|
+
function persistDiffAnalysis(project, core2) {
|
|
126263
|
+
if (!core2.latestBaseline) {
|
|
126264
|
+
throw new Error("Diff persist requires a latestBaseline \u2014 analyzeCore should have enforced this.");
|
|
126265
|
+
}
|
|
126266
|
+
const diff = buildDiffSnapshot(project.path, core2, core2.latestBaseline);
|
|
126267
|
+
writeDiff(project.path, diff);
|
|
126268
|
+
log.info(`[Diff] Done \u2014 ${diff.summary.newCount} new, ${diff.summary.unchangedCount} unchanged, ${diff.summary.resolvedCount} resolved across ${diff.changedFiles.length} changed files`);
|
|
126269
|
+
return { diff, isStale: false };
|
|
126270
|
+
}
|
|
126528
126271
|
function buildLatestSnapshot(snapshot, filename, unchanged, added) {
|
|
126529
|
-
const
|
|
126530
|
-
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126531
|
-
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126532
|
-
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
126533
|
-
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
126534
|
-
const denormalize = (v) => ({
|
|
126535
|
-
...v,
|
|
126536
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
126537
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
126538
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
126539
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
126540
|
-
});
|
|
126272
|
+
const denormalize = makeDenormalizer(snapshot.graph);
|
|
126541
126273
|
return {
|
|
126542
126274
|
head: filename,
|
|
126543
126275
|
analysis: {
|
|
@@ -126549,7 +126281,7 @@ function buildLatestSnapshot(snapshot, filename, unchanged, added) {
|
|
|
126549
126281
|
metadata: snapshot.metadata,
|
|
126550
126282
|
status: "completed"
|
|
126551
126283
|
},
|
|
126552
|
-
graph,
|
|
126284
|
+
graph: snapshot.graph,
|
|
126553
126285
|
violations: [...added.map(denormalize), ...unchanged.map(denormalize)]
|
|
126554
126286
|
};
|
|
126555
126287
|
}
|
|
@@ -126613,17 +126345,159 @@ function buildHistoryEntry(snapshot, filename, pipeline) {
|
|
|
126613
126345
|
}
|
|
126614
126346
|
};
|
|
126615
126347
|
}
|
|
126616
|
-
function
|
|
126617
|
-
|
|
126618
|
-
|
|
126619
|
-
|
|
126620
|
-
|
|
126348
|
+
function buildDiffSnapshot(repoPath, core2, baseline) {
|
|
126349
|
+
const { graph, changedFiles, pipelineResult } = core2;
|
|
126350
|
+
const denormalize = makeDenormalizer(graph);
|
|
126351
|
+
const newViolations = pipelineResult.added.map(denormalize);
|
|
126352
|
+
const latestById = new Map(baseline.violations.map((v) => [v.id, v]));
|
|
126353
|
+
const resolvedViolations = pipelineResult.resolvedRefs.map((r) => latestById.get(r.id)).filter((v) => !!v);
|
|
126354
|
+
const changedAbs = new Set(changedFiles.map((c2) => path13.resolve(repoPath, c2.path)));
|
|
126355
|
+
const matchesChanged = (p2) => !!p2 && (changedAbs.has(p2) || changedAbs.has(path13.resolve(repoPath, p2)));
|
|
126356
|
+
const affectedModules = graph.modules.filter((m) => matchesChanged(m.filePath));
|
|
126357
|
+
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
126358
|
+
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126359
|
+
const layerKeyById = new Map(graph.layers.map((l) => [l.id, `${l.serviceName}::${l.layer}`]));
|
|
126360
|
+
const affectedServices = /* @__PURE__ */ new Set();
|
|
126361
|
+
const affectedLayers = /* @__PURE__ */ new Set();
|
|
126362
|
+
const affectedModuleKeys = /* @__PURE__ */ new Set();
|
|
126363
|
+
for (const mod of affectedModules) {
|
|
126364
|
+
const svcName = serviceNameById.get(mod.serviceId);
|
|
126365
|
+
if (svcName) {
|
|
126366
|
+
affectedServices.add(svcName);
|
|
126367
|
+
affectedModuleKeys.add(`${svcName}::${mod.name}`);
|
|
126368
|
+
}
|
|
126369
|
+
const layerKey = layerKeyById.get(mod.layerId);
|
|
126370
|
+
if (layerKey)
|
|
126371
|
+
affectedLayers.add(layerKey);
|
|
126372
|
+
}
|
|
126373
|
+
const moduleNameById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126374
|
+
const affectedMethodKeys = [];
|
|
126375
|
+
for (const method of graph.methods) {
|
|
126376
|
+
if (!affectedModuleIdSet.has(method.moduleId))
|
|
126621
126377
|
continue;
|
|
126622
|
-
|
|
126623
|
-
|
|
126624
|
-
|
|
126625
|
-
|
|
126378
|
+
const modName = moduleNameById.get(method.moduleId);
|
|
126379
|
+
const mod = graph.modules.find((m) => m.id === method.moduleId);
|
|
126380
|
+
const svcName = mod ? serviceNameById.get(mod.serviceId) : void 0;
|
|
126381
|
+
if (svcName && modName)
|
|
126382
|
+
affectedMethodKeys.push(`${svcName}::${modName}::${method.name}`);
|
|
126626
126383
|
}
|
|
126384
|
+
return {
|
|
126385
|
+
id: core2.analysisId,
|
|
126386
|
+
baseAnalysisId: baseline.analysis.id,
|
|
126387
|
+
createdAt: core2.now,
|
|
126388
|
+
branch: core2.branch,
|
|
126389
|
+
commitHash: core2.commitHash,
|
|
126390
|
+
graph,
|
|
126391
|
+
changedFiles,
|
|
126392
|
+
newViolations,
|
|
126393
|
+
resolvedViolations,
|
|
126394
|
+
affectedNodeIds: {
|
|
126395
|
+
services: [...affectedServices],
|
|
126396
|
+
layers: [...affectedLayers],
|
|
126397
|
+
modules: [...affectedModuleKeys],
|
|
126398
|
+
methods: affectedMethodKeys
|
|
126399
|
+
},
|
|
126400
|
+
summary: {
|
|
126401
|
+
newCount: newViolations.length,
|
|
126402
|
+
unchangedCount: pipelineResult.unchanged.length,
|
|
126403
|
+
resolvedCount: resolvedViolations.length
|
|
126404
|
+
},
|
|
126405
|
+
usage: core2.usage
|
|
126406
|
+
};
|
|
126407
|
+
}
|
|
126408
|
+
function makeDenormalizer(graph) {
|
|
126409
|
+
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126410
|
+
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126411
|
+
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
126412
|
+
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
126413
|
+
return (v) => ({
|
|
126414
|
+
...v,
|
|
126415
|
+
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
126416
|
+
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
126417
|
+
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
126418
|
+
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
126419
|
+
});
|
|
126420
|
+
}
|
|
126421
|
+
var init_analyze_persist = __esm({
|
|
126422
|
+
"apps/server/dist/commands/analyze-persist.js"() {
|
|
126423
|
+
"use strict";
|
|
126424
|
+
init_logger();
|
|
126425
|
+
init_registry();
|
|
126426
|
+
init_analysis_store();
|
|
126427
|
+
}
|
|
126428
|
+
});
|
|
126429
|
+
|
|
126430
|
+
// apps/server/dist/commands/diff-in-process.js
|
|
126431
|
+
var diff_in_process_exports = {};
|
|
126432
|
+
__export(diff_in_process_exports, {
|
|
126433
|
+
diffInProcess: () => diffInProcess
|
|
126434
|
+
});
|
|
126435
|
+
async function diffInProcess(project, options = {}) {
|
|
126436
|
+
const core2 = await analyzeCore(project, { ...options, mode: "diff" });
|
|
126437
|
+
return persistDiffAnalysis(project, core2);
|
|
126438
|
+
}
|
|
126439
|
+
var init_diff_in_process = __esm({
|
|
126440
|
+
"apps/server/dist/commands/diff-in-process.js"() {
|
|
126441
|
+
"use strict";
|
|
126442
|
+
init_analyze_core();
|
|
126443
|
+
init_analyze_persist();
|
|
126444
|
+
}
|
|
126445
|
+
});
|
|
126446
|
+
|
|
126447
|
+
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
126448
|
+
var import_index = __toESM(require_commander(), 1);
|
|
126449
|
+
var {
|
|
126450
|
+
program,
|
|
126451
|
+
createCommand,
|
|
126452
|
+
createArgument,
|
|
126453
|
+
createOption,
|
|
126454
|
+
CommanderError,
|
|
126455
|
+
InvalidArgumentError,
|
|
126456
|
+
InvalidOptionArgumentError,
|
|
126457
|
+
// deprecated old name
|
|
126458
|
+
Command,
|
|
126459
|
+
Argument,
|
|
126460
|
+
Option,
|
|
126461
|
+
Help
|
|
126462
|
+
} = import_index.default;
|
|
126463
|
+
|
|
126464
|
+
// tools/cli/src/index.ts
|
|
126465
|
+
init_dist4();
|
|
126466
|
+
|
|
126467
|
+
// tools/cli/src/commands/add.ts
|
|
126468
|
+
init_dist4();
|
|
126469
|
+
init_paths();
|
|
126470
|
+
init_registry();
|
|
126471
|
+
init_helpers();
|
|
126472
|
+
async function runAdd() {
|
|
126473
|
+
const repoPath = resolveRepoDir(process.cwd()) ?? process.cwd();
|
|
126474
|
+
mt("Adding repository to TrueCourse");
|
|
126475
|
+
O2.step(repoPath);
|
|
126476
|
+
ensureRepoTruecourseDir(repoPath);
|
|
126477
|
+
const existing = getProjectByPath(repoPath);
|
|
126478
|
+
const entry = registerProject(repoPath);
|
|
126479
|
+
if (existing) {
|
|
126480
|
+
O2.info(`Repository "${entry.name}" is already registered.`);
|
|
126481
|
+
} else {
|
|
126482
|
+
O2.success(`Repository "${entry.name}" added.`);
|
|
126483
|
+
}
|
|
126484
|
+
await promptInstallSkills(repoPath);
|
|
126485
|
+
gt("Run `truecourse analyze` to generate analysis data.");
|
|
126486
|
+
}
|
|
126487
|
+
|
|
126488
|
+
// tools/cli/src/commands/analyze.ts
|
|
126489
|
+
init_dist4();
|
|
126490
|
+
import { execSync } from "node:child_process";
|
|
126491
|
+
import path15 from "node:path";
|
|
126492
|
+
|
|
126493
|
+
// apps/server/dist/commands/analyze-in-process.js
|
|
126494
|
+
init_analysis_store();
|
|
126495
|
+
init_analyze_core();
|
|
126496
|
+
init_analyze_persist();
|
|
126497
|
+
async function analyzeInProcess(project, options = {}) {
|
|
126498
|
+
const startedAt = Date.now();
|
|
126499
|
+
const core2 = await analyzeCore(project, { ...options, mode: "full" });
|
|
126500
|
+
return persistFullAnalysis(project, core2, startedAt);
|
|
126627
126501
|
}
|
|
126628
126502
|
|
|
126629
126503
|
// tools/cli/src/commands/analyze.ts
|
|
@@ -126634,10 +126508,24 @@ init_project_config();
|
|
|
126634
126508
|
init_logger();
|
|
126635
126509
|
init_helpers();
|
|
126636
126510
|
|
|
126511
|
+
// tools/cli/src/commands/llm-prompt.ts
|
|
126512
|
+
init_dist4();
|
|
126513
|
+
async function promptLlmEstimate(estimate) {
|
|
126514
|
+
const totalRules = estimate.uniqueRuleCount ?? estimate.tiers.reduce((s, t2) => s + t2.ruleCount, 0);
|
|
126515
|
+
const totalFiles = estimate.uniqueFileCount ?? estimate.tiers.reduce((s, t2) => s + t2.fileCount, 0);
|
|
126516
|
+
const tokens = estimate.totalEstimatedTokens;
|
|
126517
|
+
const tokenStr = tokens >= 1e6 ? `~${(tokens / 1e6).toFixed(1)}M tokens` : `~${Math.round(tokens / 1e3)}k tokens`;
|
|
126518
|
+
O2.step(`LLM will analyze ${totalFiles} files with ${totalRules} rules (${tokenStr})`);
|
|
126519
|
+
const proceed = await ot2({ message: "Run LLM-powered rules?", initialValue: true });
|
|
126520
|
+
if (q(proceed)) return false;
|
|
126521
|
+
if (!proceed) O2.info("Skipping LLM rules.");
|
|
126522
|
+
return !!proceed;
|
|
126523
|
+
}
|
|
126524
|
+
|
|
126637
126525
|
// tools/cli/src/telemetry.ts
|
|
126638
126526
|
init_dist4();
|
|
126639
126527
|
import fs9 from "node:fs";
|
|
126640
|
-
import
|
|
126528
|
+
import path14 from "node:path";
|
|
126641
126529
|
import os4 from "node:os";
|
|
126642
126530
|
import crypto from "node:crypto";
|
|
126643
126531
|
var DEFAULT_CONFIG2 = {
|
|
@@ -126646,7 +126534,7 @@ var DEFAULT_CONFIG2 = {
|
|
|
126646
126534
|
noticeShown: false
|
|
126647
126535
|
};
|
|
126648
126536
|
function getTelemetryConfigPath() {
|
|
126649
|
-
return
|
|
126537
|
+
return path14.join(os4.homedir(), ".truecourse", "telemetry.json");
|
|
126650
126538
|
}
|
|
126651
126539
|
function readTelemetryConfig() {
|
|
126652
126540
|
const configPath = getTelemetryConfigPath();
|
|
@@ -126671,7 +126559,7 @@ function readTelemetryConfig() {
|
|
|
126671
126559
|
}
|
|
126672
126560
|
function writeTelemetryConfig(partial) {
|
|
126673
126561
|
const configPath = getTelemetryConfigPath();
|
|
126674
|
-
const dir =
|
|
126562
|
+
const dir = path14.dirname(configPath);
|
|
126675
126563
|
fs9.mkdirSync(dir, { recursive: true });
|
|
126676
126564
|
let current;
|
|
126677
126565
|
try {
|
|
@@ -126806,16 +126694,9 @@ async function runAnalyze(_options = {}) {
|
|
|
126806
126694
|
enableLlmRulesOverride: enableLlmRules,
|
|
126807
126695
|
onLlmEstimate: async (estimate) => {
|
|
126808
126696
|
stopSpinner();
|
|
126809
|
-
const
|
|
126810
|
-
const totalFiles = estimate.uniqueFileCount ?? estimate.tiers.reduce((s, t2) => s + t2.fileCount, 0);
|
|
126811
|
-
const tokens = estimate.totalEstimatedTokens;
|
|
126812
|
-
const tokenStr = tokens >= 1e6 ? `~${(tokens / 1e6).toFixed(1)}M tokens` : `~${Math.round(tokens / 1e3)}k tokens`;
|
|
126813
|
-
O2.step(`LLM will analyze ${totalFiles} files with ${totalRules} rules (${tokenStr})`);
|
|
126814
|
-
const proceed = await ot2({ message: "Run LLM-powered rules?", initialValue: true });
|
|
126697
|
+
const proceed = await promptLlmEstimate(estimate);
|
|
126815
126698
|
renderPhase = "post-llm";
|
|
126816
|
-
|
|
126817
|
-
if (!proceed) O2.info("Skipping LLM rules.");
|
|
126818
|
-
return !!proceed;
|
|
126699
|
+
return proceed;
|
|
126819
126700
|
}
|
|
126820
126701
|
});
|
|
126821
126702
|
stopSpinner();
|
|
@@ -126846,19 +126727,32 @@ async function runAnalyzeDiff(_options = {}) {
|
|
|
126846
126727
|
configureLogger({
|
|
126847
126728
|
filePath: path15.join(project.path, ".truecourse/logs/analyze.log")
|
|
126848
126729
|
});
|
|
126849
|
-
const
|
|
126850
|
-
|
|
126730
|
+
const config2 = readProjectConfig(project.path);
|
|
126731
|
+
const enabledCategories = config2.enabledCategories ?? void 0;
|
|
126732
|
+
const enableLlmRules = config2.enableLlmRules ?? true;
|
|
126733
|
+
renderPhase = enableLlmRules ? "pre-llm" : "all";
|
|
126734
|
+
const stepDefs = buildAnalysisSteps(enabledCategories, enableLlmRules);
|
|
126735
|
+
const tracker = new StepTracker((payload) => {
|
|
126736
|
+
if (payload.steps) renderSteps(payload.steps);
|
|
126737
|
+
}, stepDefs);
|
|
126851
126738
|
const abortController = new AbortController();
|
|
126852
126739
|
const onSigint = () => abortController.abort();
|
|
126853
126740
|
process.on("SIGINT", onSigint);
|
|
126854
126741
|
try {
|
|
126855
126742
|
const { diff } = await diffInProcess2(project, {
|
|
126743
|
+
tracker,
|
|
126856
126744
|
signal: abortController.signal,
|
|
126857
|
-
|
|
126858
|
-
|
|
126745
|
+
enabledCategoriesOverride: enabledCategories,
|
|
126746
|
+
enableLlmRulesOverride: enableLlmRules,
|
|
126747
|
+
onLlmEstimate: async (estimate) => {
|
|
126748
|
+
stopSpinner();
|
|
126749
|
+
const proceed = await promptLlmEstimate(estimate);
|
|
126750
|
+
renderPhase = "post-llm";
|
|
126751
|
+
return proceed;
|
|
126859
126752
|
}
|
|
126860
126753
|
});
|
|
126861
|
-
|
|
126754
|
+
stopSpinner();
|
|
126755
|
+
O2.success("Diff check complete");
|
|
126862
126756
|
renderDiffResultsSummary2({
|
|
126863
126757
|
changedFiles: diff.changedFiles,
|
|
126864
126758
|
newViolations: diff.newViolations,
|
|
@@ -126868,7 +126762,7 @@ async function runAnalyzeDiff(_options = {}) {
|
|
|
126868
126762
|
});
|
|
126869
126763
|
gt("Diff complete \u2014 view results with: truecourse dashboard");
|
|
126870
126764
|
} catch (err) {
|
|
126871
|
-
|
|
126765
|
+
stopSpinner();
|
|
126872
126766
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
126873
126767
|
gt("Diff cancelled");
|
|
126874
126768
|
process.exit(130);
|
|
@@ -127313,7 +127207,7 @@ function targetUrlFor(baseUrl) {
|
|
|
127313
127207
|
const repoDir = resolveRepoDir(process.cwd());
|
|
127314
127208
|
if (!repoDir) return baseUrl;
|
|
127315
127209
|
const entry = getProjectByPath(repoDir) ?? registerProject(repoDir);
|
|
127316
|
-
return `${baseUrl}/
|
|
127210
|
+
return `${baseUrl}/repos/${entry.slug}`;
|
|
127317
127211
|
}
|
|
127318
127212
|
async function promptRunMode() {
|
|
127319
127213
|
const choice = await _t({
|
|
@@ -127388,7 +127282,7 @@ async function runServiceMode(serverEntry) {
|
|
|
127388
127282
|
O2.success(`Dashboard open at ${target}`);
|
|
127389
127283
|
O2.info("Stop the dashboard with: truecourse dashboard stop");
|
|
127390
127284
|
}
|
|
127391
|
-
async function runDashboard() {
|
|
127285
|
+
async function runDashboard(options = {}) {
|
|
127392
127286
|
mt("Opening TrueCourse dashboard");
|
|
127393
127287
|
const serverEntry = resolveServerEntry();
|
|
127394
127288
|
if (!serverEntry) {
|
|
@@ -127398,8 +127292,9 @@ async function runDashboard() {
|
|
|
127398
127292
|
process.exit(1);
|
|
127399
127293
|
}
|
|
127400
127294
|
const configured = fs14.existsSync(getConfigPath());
|
|
127401
|
-
const
|
|
127402
|
-
|
|
127295
|
+
const shouldPrompt = !configured || options.reconfigure;
|
|
127296
|
+
const runMode = shouldPrompt ? await promptRunMode() : readConfig().runMode;
|
|
127297
|
+
if (shouldPrompt) writeConfig({ runMode });
|
|
127403
127298
|
if (runMode === "service") {
|
|
127404
127299
|
try {
|
|
127405
127300
|
await runServiceMode(serverEntry);
|
|
@@ -127497,55 +127392,163 @@ async function runDashboardUninstall() {
|
|
|
127497
127392
|
|
|
127498
127393
|
// tools/cli/src/commands/list.ts
|
|
127499
127394
|
init_dist4();
|
|
127395
|
+
|
|
127396
|
+
// apps/server/dist/services/violation-query.service.js
|
|
127397
|
+
init_analysis_store();
|
|
127398
|
+
var SEVERITY_ORDER = {
|
|
127399
|
+
critical: 0,
|
|
127400
|
+
high: 1,
|
|
127401
|
+
medium: 2,
|
|
127402
|
+
low: 3,
|
|
127403
|
+
info: 4
|
|
127404
|
+
};
|
|
127405
|
+
function listViolations(repoPath, options = {}) {
|
|
127406
|
+
const latest = readLatest(repoPath);
|
|
127407
|
+
if (!latest)
|
|
127408
|
+
return { violations: [], total: 0 };
|
|
127409
|
+
if (options.analysisId && latest.analysis.id !== options.analysisId) {
|
|
127410
|
+
return { violations: [], total: 0 };
|
|
127411
|
+
}
|
|
127412
|
+
const statusMode = options.status ?? "active";
|
|
127413
|
+
let filtered;
|
|
127414
|
+
if (statusMode === "resolved") {
|
|
127415
|
+
filtered = latest.violations.filter((v) => v.status === "resolved");
|
|
127416
|
+
} else if (statusMode === "all") {
|
|
127417
|
+
filtered = latest.violations;
|
|
127418
|
+
} else {
|
|
127419
|
+
const active = ["new", "unchanged"];
|
|
127420
|
+
filtered = latest.violations.filter((v) => active.includes(v.status));
|
|
127421
|
+
}
|
|
127422
|
+
if (options.filePath) {
|
|
127423
|
+
const absPath = options.filePath.startsWith("/") ? options.filePath : `${repoPath}/${options.filePath}`;
|
|
127424
|
+
filtered = filtered.filter((v) => v.type === "code" && (v.filePath === absPath || v.filePath === options.filePath));
|
|
127425
|
+
}
|
|
127426
|
+
filtered.sort((a, b) => {
|
|
127427
|
+
const sa = SEVERITY_ORDER[a.severity] ?? 5;
|
|
127428
|
+
const sb = SEVERITY_ORDER[b.severity] ?? 5;
|
|
127429
|
+
if (sa !== sb)
|
|
127430
|
+
return sa - sb;
|
|
127431
|
+
return b.createdAt.localeCompare(a.createdAt);
|
|
127432
|
+
});
|
|
127433
|
+
const total = filtered.length;
|
|
127434
|
+
const limit = options.limit ?? 0;
|
|
127435
|
+
const offset = options.offset ?? 0;
|
|
127436
|
+
const paged = limit > 0 ? filtered.slice(offset, offset + limit) : filtered;
|
|
127437
|
+
return { violations: paged, total };
|
|
127438
|
+
}
|
|
127439
|
+
function getDiffResult(repoPath) {
|
|
127440
|
+
const diff = readDiff(repoPath);
|
|
127441
|
+
if (!diff)
|
|
127442
|
+
return null;
|
|
127443
|
+
const latest = readLatest(repoPath);
|
|
127444
|
+
const isStale = latest ? latest.analysis.id !== diff.baseAnalysisId : false;
|
|
127445
|
+
return { diff, isStale };
|
|
127446
|
+
}
|
|
127447
|
+
|
|
127448
|
+
// tools/cli/src/commands/list.ts
|
|
127500
127449
|
init_helpers();
|
|
127501
127450
|
async function runList({ limit = 20, offset = 0 } = {}) {
|
|
127502
127451
|
mt("Violations");
|
|
127503
|
-
await requireDashboard();
|
|
127504
127452
|
const repo = requireRegisteredRepo();
|
|
127505
|
-
const
|
|
127506
|
-
|
|
127507
|
-
|
|
127508
|
-
|
|
127509
|
-
|
|
127510
|
-
|
|
127511
|
-
|
|
127512
|
-
const url = `${serverUrl}/api/repos/${repo.id}/violations${params.toString() ? `?${params}` : ""}`;
|
|
127513
|
-
const res = await fetch(url);
|
|
127514
|
-
if (!res.ok) {
|
|
127515
|
-
O2.error(`Failed to fetch violations: ${res.status}`);
|
|
127516
|
-
process.exit(1);
|
|
127517
|
-
}
|
|
127518
|
-
const body = await res.json();
|
|
127519
|
-
if (Array.isArray(body)) {
|
|
127520
|
-
renderViolations(body, { total: body.length });
|
|
127521
|
-
} else {
|
|
127522
|
-
const { violations, total } = body;
|
|
127523
|
-
renderViolations(violations, { total, offset });
|
|
127453
|
+
const { violations, total } = listViolations(repo.path, {
|
|
127454
|
+
limit: isFinite(limit) ? limit : 0,
|
|
127455
|
+
offset
|
|
127456
|
+
});
|
|
127457
|
+
if (total === 0 && violations.length === 0) {
|
|
127458
|
+
O2.info("No violations. Run `truecourse analyze` if you haven't yet.");
|
|
127459
|
+
return;
|
|
127524
127460
|
}
|
|
127461
|
+
renderViolations(violations, { total, offset });
|
|
127525
127462
|
}
|
|
127526
127463
|
async function runListDiff() {
|
|
127527
127464
|
mt("Diff check results");
|
|
127528
|
-
await requireDashboard();
|
|
127529
127465
|
const repo = requireRegisteredRepo();
|
|
127530
|
-
const
|
|
127531
|
-
const res = await fetch(`${serverUrl}/api/repos/${repo.id}/diff-check`);
|
|
127532
|
-
if (!res.ok) {
|
|
127533
|
-
O2.error(`Failed to fetch diff check results: ${res.status}`);
|
|
127534
|
-
process.exit(1);
|
|
127535
|
-
}
|
|
127536
|
-
const result = await res.json();
|
|
127466
|
+
const result = getDiffResult(repo.path);
|
|
127537
127467
|
if (!result) {
|
|
127538
127468
|
O2.info("No diff check found. Run `truecourse analyze --diff` first.");
|
|
127539
127469
|
return;
|
|
127540
127470
|
}
|
|
127541
|
-
|
|
127471
|
+
const { diff, isStale } = result;
|
|
127472
|
+
if (isStale) {
|
|
127542
127473
|
O2.warn("Results may be stale \u2014 baseline analysis has changed.");
|
|
127543
127474
|
}
|
|
127544
|
-
|
|
127475
|
+
const rendered = {
|
|
127476
|
+
isStale,
|
|
127477
|
+
changedFiles: diff.changedFiles,
|
|
127478
|
+
newViolations: diff.newViolations,
|
|
127479
|
+
resolvedViolations: diff.resolvedViolations,
|
|
127480
|
+
summary: { newCount: diff.summary.newCount, resolvedCount: diff.summary.resolvedCount }
|
|
127481
|
+
};
|
|
127482
|
+
renderDiffResults(rendered);
|
|
127545
127483
|
}
|
|
127546
127484
|
|
|
127547
|
-
// tools/cli/src/
|
|
127485
|
+
// tools/cli/src/commands/rules.ts
|
|
127486
|
+
init_dist4();
|
|
127487
|
+
init_dist7();
|
|
127488
|
+
init_project_config();
|
|
127548
127489
|
init_helpers();
|
|
127490
|
+
var ALL_CATEGORIES = [...DOMAIN_ORDER];
|
|
127491
|
+
async function runRulesCategories(options) {
|
|
127492
|
+
const repo = requireRegisteredRepo();
|
|
127493
|
+
if (options.reset) {
|
|
127494
|
+
updateProjectConfig(repo.path, { enabledCategories: null });
|
|
127495
|
+
O2.success("Reset to global default categories.");
|
|
127496
|
+
return;
|
|
127497
|
+
}
|
|
127498
|
+
if (options.enable || options.disable) {
|
|
127499
|
+
const cat = options.enable ?? options.disable;
|
|
127500
|
+
if (!ALL_CATEGORIES.includes(cat)) {
|
|
127501
|
+
O2.error(`Invalid category: ${cat}. Valid: ${ALL_CATEGORIES.join(", ")}`);
|
|
127502
|
+
process.exit(1);
|
|
127503
|
+
}
|
|
127504
|
+
const config3 = readProjectConfig(repo.path);
|
|
127505
|
+
const hasOverride = config3.enabledCategories != null;
|
|
127506
|
+
const current = new Set(hasOverride ? config3.enabledCategories : ALL_CATEGORIES);
|
|
127507
|
+
if (options.enable) current.add(cat);
|
|
127508
|
+
else current.delete(cat);
|
|
127509
|
+
updateProjectConfig(repo.path, { enabledCategories: [...current] });
|
|
127510
|
+
O2.success(`${options.enable ? "Enabled" : "Disabled"} ${cat} rules for ${repo.name}.`);
|
|
127511
|
+
return;
|
|
127512
|
+
}
|
|
127513
|
+
const config2 = readProjectConfig(repo.path);
|
|
127514
|
+
const isOverride = config2.enabledCategories != null;
|
|
127515
|
+
const enabled = new Set(isOverride ? config2.enabledCategories : ALL_CATEGORIES);
|
|
127516
|
+
const status = (cat) => enabled.has(cat) ? "\x1B[32menabled\x1B[0m" : "\x1B[31mdisabled\x1B[0m";
|
|
127517
|
+
O2.info(
|
|
127518
|
+
`Rule categories for ${repo.name}${isOverride ? " (per-repo override)" : " (global default)"}:`
|
|
127519
|
+
);
|
|
127520
|
+
for (const cat of ALL_CATEGORIES) {
|
|
127521
|
+
console.log(` ${cat.padEnd(14)} ${status(cat)}`);
|
|
127522
|
+
}
|
|
127523
|
+
console.log("");
|
|
127524
|
+
if (!isOverride) {
|
|
127525
|
+
O2.info("Override with: truecourse rules categories --enable/--disable <name>");
|
|
127526
|
+
}
|
|
127527
|
+
}
|
|
127528
|
+
async function runRulesLlm(options) {
|
|
127529
|
+
const repo = requireRegisteredRepo();
|
|
127530
|
+
if (options.reset) {
|
|
127531
|
+
updateProjectConfig(repo.path, { enableLlmRules: null });
|
|
127532
|
+
O2.success("Reset LLM rules to global default.");
|
|
127533
|
+
return;
|
|
127534
|
+
}
|
|
127535
|
+
if (options.enable || options.disable) {
|
|
127536
|
+
const enabled = !!options.enable;
|
|
127537
|
+
updateProjectConfig(repo.path, { enableLlmRules: enabled });
|
|
127538
|
+
O2.success(`LLM rules ${enabled ? "enabled" : "disabled"} for ${repo.name}.`);
|
|
127539
|
+
return;
|
|
127540
|
+
}
|
|
127541
|
+
const config2 = readProjectConfig(repo.path);
|
|
127542
|
+
const isOverride = config2.enableLlmRules != null;
|
|
127543
|
+
const effective = isOverride ? config2.enableLlmRules : true;
|
|
127544
|
+
const status = effective ? "\x1B[32menabled\x1B[0m" : "\x1B[31mdisabled\x1B[0m";
|
|
127545
|
+
O2.info(
|
|
127546
|
+
`LLM rules for ${repo.name}${isOverride ? " (per-repo override)" : " (global default)"}: ${status}`
|
|
127547
|
+
);
|
|
127548
|
+
if (!isOverride) {
|
|
127549
|
+
O2.info("Override with: truecourse rules llm --enable/--disable");
|
|
127550
|
+
}
|
|
127551
|
+
}
|
|
127549
127552
|
|
|
127550
127553
|
// tools/cli/src/commands/hooks.ts
|
|
127551
127554
|
import { execSync as execSync5 } from "node:child_process";
|
|
@@ -130437,9 +130440,9 @@ async function runHooksRun() {
|
|
|
130437
130440
|
|
|
130438
130441
|
// tools/cli/src/index.ts
|
|
130439
130442
|
var program2 = new Command();
|
|
130440
|
-
program2.name("truecourse").version("0.4.
|
|
130441
|
-
var dashboardCmd = program2.command("dashboard").description("Start the TrueCourse dashboard and open it in your browser").action(async () => {
|
|
130442
|
-
await runDashboard();
|
|
130443
|
+
program2.name("truecourse").version("0.4.4").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
130444
|
+
var dashboardCmd = program2.command("dashboard").description("Start the TrueCourse dashboard and open it in your browser").option("--reconfigure", "Re-prompt for console vs background service mode").action(async (options) => {
|
|
130445
|
+
await runDashboard({ reconfigure: options.reconfigure });
|
|
130443
130446
|
});
|
|
130444
130447
|
dashboardCmd.command("stop").description("Stop the dashboard").action(async () => {
|
|
130445
130448
|
await runDashboardStop();
|
|
@@ -130475,94 +130478,10 @@ program2.command("list").description("List violations from the latest analysis")
|
|
|
130475
130478
|
});
|
|
130476
130479
|
var rulesCmd = program2.command("rules").description("Manage analysis rules");
|
|
130477
130480
|
rulesCmd.command("categories").description("View or override rule categories for this repository").option("--enable <category>", "Enable a category").option("--disable <category>", "Disable a category").option("--reset", "Reset to global default").action(async (options) => {
|
|
130478
|
-
await
|
|
130479
|
-
const repo = requireRegisteredRepo();
|
|
130480
|
-
const serverUrl = getServerUrl();
|
|
130481
|
-
const { DOMAIN_ORDER: DOMAIN_ORDER2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports3));
|
|
130482
|
-
const allCategories = [...DOMAIN_ORDER2];
|
|
130483
|
-
if (options.reset) {
|
|
130484
|
-
await fetch(`${serverUrl}/api/repos/${repo.id}/categories`, {
|
|
130485
|
-
method: "PUT",
|
|
130486
|
-
headers: { "Content-Type": "application/json" },
|
|
130487
|
-
body: JSON.stringify({ enabledCategories: null })
|
|
130488
|
-
});
|
|
130489
|
-
O2.success("Reset to global default categories.");
|
|
130490
|
-
return;
|
|
130491
|
-
}
|
|
130492
|
-
if (options.enable || options.disable) {
|
|
130493
|
-
const cat = options.enable || options.disable;
|
|
130494
|
-
if (!allCategories.includes(cat)) {
|
|
130495
|
-
O2.error(`Invalid category: ${cat}. Valid: ${allCategories.join(", ")}`);
|
|
130496
|
-
process.exit(1);
|
|
130497
|
-
}
|
|
130498
|
-
const repoRes2 = await fetch(`${serverUrl}/api/repos/${repo.id}`);
|
|
130499
|
-
const repoData2 = await repoRes2.json();
|
|
130500
|
-
const hasOverride = repoData2.enabledCategories !== null && repoData2.enabledCategories !== void 0;
|
|
130501
|
-
const current = new Set(
|
|
130502
|
-
hasOverride ? repoData2.enabledCategories : allCategories
|
|
130503
|
-
);
|
|
130504
|
-
if (options.enable) current.add(cat);
|
|
130505
|
-
else current.delete(cat);
|
|
130506
|
-
await fetch(`${serverUrl}/api/repos/${repo.id}/categories`, {
|
|
130507
|
-
method: "PUT",
|
|
130508
|
-
headers: { "Content-Type": "application/json" },
|
|
130509
|
-
body: JSON.stringify({ enabledCategories: [...current] })
|
|
130510
|
-
});
|
|
130511
|
-
O2.success(`${options.enable ? "Enabled" : "Disabled"} ${cat} rules for ${repo.name}.`);
|
|
130512
|
-
return;
|
|
130513
|
-
}
|
|
130514
|
-
const repoRes = await fetch(`${serverUrl}/api/repos/${repo.id}`);
|
|
130515
|
-
const repoData = await repoRes.json();
|
|
130516
|
-
const isOverride = repoData.enabledCategories !== null && repoData.enabledCategories !== void 0;
|
|
130517
|
-
const enabled = new Set(
|
|
130518
|
-
isOverride ? repoData.enabledCategories : allCategories
|
|
130519
|
-
);
|
|
130520
|
-
const status = (cat) => enabled.has(cat) ? "\x1B[32menabled\x1B[0m" : "\x1B[31mdisabled\x1B[0m";
|
|
130521
|
-
O2.info(
|
|
130522
|
-
`Rule categories for ${repo.name}${isOverride ? " (per-repo override)" : " (global default)"}:`
|
|
130523
|
-
);
|
|
130524
|
-
for (const cat of allCategories) {
|
|
130525
|
-
console.log(` ${cat.padEnd(14)} ${status(cat)}`);
|
|
130526
|
-
}
|
|
130527
|
-
console.log("");
|
|
130528
|
-
if (!isOverride) {
|
|
130529
|
-
O2.info("Override with: truecourse rules categories --enable/--disable <name>");
|
|
130530
|
-
}
|
|
130481
|
+
await runRulesCategories(options);
|
|
130531
130482
|
});
|
|
130532
130483
|
rulesCmd.command("llm").description("Enable or disable LLM-powered rules for this repository").option("--enable", "Enable LLM rules").option("--disable", "Disable LLM rules").option("--reset", "Reset to global default").action(async (options) => {
|
|
130533
|
-
await
|
|
130534
|
-
const repo = requireRegisteredRepo();
|
|
130535
|
-
const serverUrl = getServerUrl();
|
|
130536
|
-
if (options.reset) {
|
|
130537
|
-
await fetch(`${serverUrl}/api/repos/${repo.id}/llm`, {
|
|
130538
|
-
method: "PUT",
|
|
130539
|
-
headers: { "Content-Type": "application/json" },
|
|
130540
|
-
body: JSON.stringify({ enableLlmRules: null })
|
|
130541
|
-
});
|
|
130542
|
-
O2.success("Reset LLM rules to global default.");
|
|
130543
|
-
return;
|
|
130544
|
-
}
|
|
130545
|
-
if (options.enable || options.disable) {
|
|
130546
|
-
const enabled = !!options.enable;
|
|
130547
|
-
await fetch(`${serverUrl}/api/repos/${repo.id}/llm`, {
|
|
130548
|
-
method: "PUT",
|
|
130549
|
-
headers: { "Content-Type": "application/json" },
|
|
130550
|
-
body: JSON.stringify({ enableLlmRules: enabled })
|
|
130551
|
-
});
|
|
130552
|
-
O2.success(`LLM rules ${enabled ? "enabled" : "disabled"} for ${repo.name}.`);
|
|
130553
|
-
return;
|
|
130554
|
-
}
|
|
130555
|
-
const repoRes = await fetch(`${serverUrl}/api/repos/${repo.id}`);
|
|
130556
|
-
const repoData = await repoRes.json();
|
|
130557
|
-
const isOverride = repoData.enableLlmRules !== null && repoData.enableLlmRules !== void 0;
|
|
130558
|
-
const effective = isOverride ? repoData.enableLlmRules : true;
|
|
130559
|
-
const status = effective ? "\x1B[32menabled\x1B[0m" : "\x1B[31mdisabled\x1B[0m";
|
|
130560
|
-
O2.info(
|
|
130561
|
-
`LLM rules for ${repo.name}${isOverride ? " (per-repo override)" : " (global default)"}: ${status}`
|
|
130562
|
-
);
|
|
130563
|
-
if (!isOverride) {
|
|
130564
|
-
O2.info("Override with: truecourse rules llm --enable/--disable");
|
|
130565
|
-
}
|
|
130484
|
+
await runRulesLlm(options);
|
|
130566
130485
|
});
|
|
130567
130486
|
var telemetryCmd = program2.command("telemetry").description("Manage anonymous usage telemetry");
|
|
130568
130487
|
telemetryCmd.command("enable").description("Enable anonymous usage telemetry").action(() => {
|