@mneme-ai/xray 2.150.0 → 2.151.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/battery/coupling.d.ts +3 -0
- package/dist/battery/coupling.d.ts.map +1 -0
- package/dist/battery/coupling.js +61 -0
- package/dist/battery/coupling.js.map +1 -0
- package/dist/battery/deps.d.ts +2 -0
- package/dist/battery/deps.d.ts.map +1 -1
- package/dist/battery/deps.js +34 -7
- package/dist/battery/deps.js.map +1 -1
- package/dist/battery/hotspots.d.ts.map +1 -1
- package/dist/battery/hotspots.js +54 -15
- package/dist/battery/hotspots.js.map +1 -1
- package/dist/bin.js +27 -0
- package/dist/bin.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +89 -0
- package/dist/bridge.js.map +1 -0
- package/dist/clone.d.ts.map +1 -1
- package/dist/clone.js +2 -1
- package/dist/clone.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +17 -4
- package/dist/engine.js.map +1 -1
- package/dist/gauntlet.d.ts.map +1 -1
- package/dist/gauntlet.js +3 -2
- package/dist/gauntlet.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +30 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/public/card.js +33 -8
- package/public/index.html +44 -1
- package/public/report.html +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coupling.d.ts","sourceRoot":"","sources":["../../src/battery/coupling.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,aAAa,CAwC9F"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CHANGE-COUPLING (temporal coupling) — files that keep changing together.
|
|
3
|
+
*
|
|
4
|
+
* Research: logical/evolutionary coupling (Gall et al.; D'Ambros & Lanza;
|
|
5
|
+
* Zimmermann et al. "mining version histories to guide changes"). Two files
|
|
6
|
+
* that change together far more often than chance share a HIDDEN dependency the
|
|
7
|
+
* type system can't see — the classic "I fixed A but forgot B" bug source.
|
|
8
|
+
*
|
|
9
|
+
* Deterministic: from `git log --name-only` (commit/tree metadata; no blob
|
|
10
|
+
* fetch). confidence(A,B) = coChanges / min(changes(A), changes(B)).
|
|
11
|
+
* "hidden" = the two files live in different directories (non-obvious coupling).
|
|
12
|
+
*/
|
|
13
|
+
import { git, isGitRepo } from "../util.js";
|
|
14
|
+
const SKIP_DIR = /(^|\/)(node_modules|\.git|dist|build|vendor|\.next|coverage|__pycache__|\.venv|target)(\/|$)/;
|
|
15
|
+
const CODE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|c|h|cpp|cc|rb|php|cs|kt|swift|scala|vue|svelte)$/i;
|
|
16
|
+
const dir = (p) => { const i = p.lastIndexOf("/"); return i < 0 ? "" : p.slice(0, i); };
|
|
17
|
+
export function analyzeCoupling(repoPath, now, windowDays = 365) {
|
|
18
|
+
if (!isGitRepo(repoPath))
|
|
19
|
+
return { windowDays, pairs: [], note: "Not a git repository — coupling history unavailable." };
|
|
20
|
+
const since = new Date(now - windowDays * 86_400_000).toISOString();
|
|
21
|
+
// \x01 between commits → recover each commit's changed-file set
|
|
22
|
+
const raw = git(repoPath, ["log", "--since", since, "--no-merges", "--name-only", "--pretty=format:%x01"]);
|
|
23
|
+
if (!raw.trim())
|
|
24
|
+
return { windowDays, pairs: [], note: "No commit activity in the window." };
|
|
25
|
+
const fileCount = new Map();
|
|
26
|
+
const pairCount = new Map();
|
|
27
|
+
for (const block of raw.split("\x01")) {
|
|
28
|
+
const files = [...new Set(block.split("\n").map((s) => s.trim()).filter((f) => f && !SKIP_DIR.test(f) && CODE_EXT.test(f)))];
|
|
29
|
+
for (const f of files)
|
|
30
|
+
fileCount.set(f, (fileCount.get(f) ?? 0) + 1);
|
|
31
|
+
if (files.length < 2 || files.length > 40)
|
|
32
|
+
continue; // skip lone + sweeping commits for pairing
|
|
33
|
+
for (let i = 0; i < files.length; i++) {
|
|
34
|
+
for (let j = i + 1; j < files.length; j++) {
|
|
35
|
+
const k = files[i] < files[j] ? files[i] + "\x00" + files[j] : files[j] + "\x00" + files[i];
|
|
36
|
+
pairCount.set(k, (pairCount.get(k) ?? 0) + 1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const pairs = [];
|
|
41
|
+
for (const [k, co] of pairCount) {
|
|
42
|
+
if (co < 5)
|
|
43
|
+
continue; // need support
|
|
44
|
+
const [a, b] = k.split("\x00");
|
|
45
|
+
const conf = co / Math.min(fileCount.get(a) ?? co, fileCount.get(b) ?? co);
|
|
46
|
+
if (conf < 0.5)
|
|
47
|
+
continue; // need strength
|
|
48
|
+
pairs.push({ a, b, coChanges: co, confidence: Math.round(conf * 100) / 100, hidden: dir(a) !== dir(b) });
|
|
49
|
+
}
|
|
50
|
+
// hidden (cross-dir) coupling first — that's the surprising, valuable kind
|
|
51
|
+
pairs.sort((x, y) => Number(y.hidden) - Number(x.hidden) || y.confidence - x.confidence || y.coChanges - x.coChanges);
|
|
52
|
+
const top = pairs[0];
|
|
53
|
+
return {
|
|
54
|
+
windowDays,
|
|
55
|
+
pairs: pairs.slice(0, 15),
|
|
56
|
+
note: top
|
|
57
|
+
? `${top.a} ⇄ ${top.b} change together ${Math.round(top.confidence * 100)}% of the time${top.hidden ? " across different directories (hidden dependency)" : ""}.`
|
|
58
|
+
: "No strong change-coupling detected.",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=coupling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coupling.js","sourceRoot":"","sources":["../../src/battery/coupling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,QAAQ,GAAG,8FAA8F,CAAC;AAChH,MAAM,QAAQ,GAAG,0FAA0F,CAAC;AAC5G,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhG,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,GAAW,EAAE,UAAU,GAAG,GAAG;IAC7E,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,sDAAsD,EAAE,CAAC;IACzH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,gEAAgE;IAChE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAC3G,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC;IAE7F,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7H,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACrE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;YAAE,SAAS,CAAC,2CAA2C;QAChG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC5F,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;QAChC,IAAI,EAAE,GAAG,CAAC;YAAE,SAAS,CAAC,eAAe;QACrC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3E,IAAI,IAAI,GAAG,GAAG;YAAE,SAAS,CAAC,gBAAgB;QAC1C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3G,CAAC;IACD,2EAA2E;IAC3E,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAEtH,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACrB,OAAO;QACL,UAAU;QACV,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACzB,IAAI,EAAE,GAAG;YACP,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,oBAAoB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,gBAAgB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,mDAAmD,CAAC,CAAC,CAAC,EAAE,GAAG;YACjK,CAAC,CAAC,qCAAqC;KAC1C,CAAC;AACJ,CAAC"}
|
package/dist/battery/deps.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ type NpmMeta = Parameters<typeof depMortality.predictMortality>[0];
|
|
|
10
10
|
export type MetaFetcher = (pkg: string, now: number) => Promise<NpmMeta | null>;
|
|
11
11
|
/** Default: fetch the public npm registry document and derive mortality inputs. */
|
|
12
12
|
export declare const defaultFetcher: MetaFetcher;
|
|
13
|
+
/** Classify an SPDX-ish license string into a commercial-risk band. */
|
|
14
|
+
export declare function licenseClass(lic: string): "permissive" | "weak-copyleft" | "strong-copyleft" | "unknown";
|
|
13
15
|
export declare function analyzeDeps(repoPath: string, now: number, fetcher?: MetaFetcher): Promise<DepsBlock>;
|
|
14
16
|
export {};
|
|
15
17
|
//# sourceMappingURL=deps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/battery/deps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAIhF,mFAAmF;AACnF,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/battery/deps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAIhF,mFAAmF;AACnF,eAAO,MAAM,cAAc,EAAE,WA+C5B,CAAC;AAEF,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,GAAG,iBAAiB,GAAG,SAAS,CAOxG;AAcD,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAA4B,GAAG,OAAO,CAAC,SAAS,CAAC,CA+C1H"}
|
package/dist/battery/deps.js
CHANGED
|
@@ -39,6 +39,7 @@ export const defaultFetcher = async (pkg, now) => {
|
|
|
39
39
|
monthsSinceFeatureRelease = (now - bestAt) / MONTH_MS;
|
|
40
40
|
}
|
|
41
41
|
const deprecated = !!(latest && doc.versions?.[latest]?.deprecated);
|
|
42
|
+
const license = (latest && doc.versions?.[latest]?.license) || doc.license || "";
|
|
42
43
|
return {
|
|
43
44
|
name: pkg,
|
|
44
45
|
latestPublishedAt: latestAt,
|
|
@@ -46,12 +47,26 @@ export const defaultFetcher = async (pkg, now) => {
|
|
|
46
47
|
monthsSinceFeatureRelease,
|
|
47
48
|
deprecated,
|
|
48
49
|
maintainerCount: Array.isArray(doc.maintainers) ? doc.maintainers.length : undefined,
|
|
50
|
+
license,
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
catch {
|
|
52
54
|
return null;
|
|
53
55
|
}
|
|
54
56
|
};
|
|
57
|
+
/** Classify an SPDX-ish license string into a commercial-risk band. */
|
|
58
|
+
export function licenseClass(lic) {
|
|
59
|
+
const s = (lic || "").toUpperCase();
|
|
60
|
+
if (!s || s === "UNLICENSED" || s === "SEE LICENSE" || s.includes("CUSTOM"))
|
|
61
|
+
return "unknown";
|
|
62
|
+
if (/\bAGPL|GPL-?[23]|GPLV[23]\b/.test(s) && !s.includes("LGPL"))
|
|
63
|
+
return "strong-copyleft";
|
|
64
|
+
if (/\bLGPL|MPL|EPL|CDDL|MS-RL\b/.test(s))
|
|
65
|
+
return "weak-copyleft";
|
|
66
|
+
if (/\bMIT|ISC|BSD|APACHE|UNLICENSE|0BSD|CC0|WTFPL|ZLIB|PYTHON|BLUEOAK\b/.test(s))
|
|
67
|
+
return "permissive";
|
|
68
|
+
return "unknown";
|
|
69
|
+
}
|
|
55
70
|
function depNames(repoPath) {
|
|
56
71
|
const raw = readText(join(repoPath, "package.json"));
|
|
57
72
|
if (!raw)
|
|
@@ -68,12 +83,13 @@ function depNames(repoPath) {
|
|
|
68
83
|
export async function analyzeDeps(repoPath, now, fetcher = defaultFetcher) {
|
|
69
84
|
const names = depNames(repoPath);
|
|
70
85
|
const byBand = { thriving: 0, healthy: 0, watch: 0, moribund: 0, dead: 0 };
|
|
86
|
+
const licenses = { permissive: 0, "weak-copyleft": 0, "strong-copyleft": 0, unknown: 0 };
|
|
71
87
|
const atRisk = [];
|
|
88
|
+
const licenseFlags = [];
|
|
72
89
|
if (names.length === 0) {
|
|
73
|
-
return { total: 0, byBand, atRisk, partial: false, note: "No package.json dependencies found (non-npm repo or no deps)." };
|
|
90
|
+
return { total: 0, byBand, atRisk, licenses, licenseFlags, partial: false, note: "No package.json dependencies found (non-npm repo or no deps)." };
|
|
74
91
|
}
|
|
75
92
|
let partial = false;
|
|
76
|
-
// bounded concurrency to be a polite registry citizen
|
|
77
93
|
const LIMIT = 8;
|
|
78
94
|
for (let i = 0; i < names.length; i += LIMIT) {
|
|
79
95
|
const chunk = names.slice(i, i + LIMIT);
|
|
@@ -88,20 +104,31 @@ export async function analyzeDeps(repoPath, now, fetcher = defaultFetcher) {
|
|
|
88
104
|
if (r.band === "watch" || r.band === "moribund" || r.band === "dead") {
|
|
89
105
|
atRisk.push({ name: r.package, band: r.band, probability18mo: Math.round(r.probability18mo * 100) / 100, successor: meta.knownSubstitute ?? null });
|
|
90
106
|
}
|
|
107
|
+
const lic = meta.license ?? "";
|
|
108
|
+
const cls = licenseClass(lic);
|
|
109
|
+
licenses[cls]++;
|
|
110
|
+
if (cls === "strong-copyleft" || cls === "weak-copyleft") {
|
|
111
|
+
licenseFlags.push({ name: meta.name, license: lic || "?", class: cls });
|
|
112
|
+
}
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
atRisk.sort((a, b) => b.probability18mo - a.probability18mo);
|
|
116
|
+
licenseFlags.sort((a, b) => (a.class === "strong-copyleft" ? -1 : 1) - (b.class === "strong-copyleft" ? -1 : 1));
|
|
94
117
|
const danger = byBand.moribund + byBand.dead;
|
|
118
|
+
const copyleft = licenses["strong-copyleft"] + licenses["weak-copyleft"];
|
|
119
|
+
const parts = [];
|
|
120
|
+
if (danger > 0)
|
|
121
|
+
parts.push(`${danger} dying (moribund/dead) — plan replacements`);
|
|
122
|
+
if (copyleft > 0)
|
|
123
|
+
parts.push(`${copyleft} copyleft-licensed (commercial-use review)`);
|
|
95
124
|
return {
|
|
96
125
|
total: names.length,
|
|
97
126
|
byBand,
|
|
98
127
|
atRisk: atRisk.slice(0, 20),
|
|
128
|
+
licenses,
|
|
129
|
+
licenseFlags: licenseFlags.slice(0, 15),
|
|
99
130
|
partial,
|
|
100
|
-
note:
|
|
101
|
-
? `${danger} depend<x>${danger === 1 ? "y is" : "ies are"}</x> dying (moribund/dead). Plan replacements.`.replace(/<x>|<\/x>/g, "")
|
|
102
|
-
: partial
|
|
103
|
-
? "Some packages could not be reached on the npm registry (counted as unknown)."
|
|
104
|
-
: "No dying dependencies detected.",
|
|
131
|
+
note: parts.length ? parts.join("; ") + "." : partial ? "Some packages could not be reached on the npm registry." : "No dying or copyleft-risk dependencies.",
|
|
105
132
|
};
|
|
106
133
|
}
|
|
107
134
|
//# sourceMappingURL=deps.js.map
|
package/dist/battery/deps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/battery/deps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;AAE7C,mFAAmF;AACnF,MAAM,CAAC,MAAM,cAAc,GAAgB,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC5D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,8BAA8B,kBAAkB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE;YACnG,OAAO,EAAE,EAAE,MAAM,EAAE,uDAAuD,EAAE;SAC7E,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/battery/deps.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotspots.d.ts","sourceRoot":"","sources":["../../src/battery/hotspots.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hotspots.d.ts","sourceRoot":"","sources":["../../src/battery/hotspots.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,aAAa,CA4D9F"}
|
package/dist/battery/hotspots.js
CHANGED
|
@@ -5,16 +5,15 @@
|
|
|
5
5
|
* that are BOTH changed often AND large/complex. This is well established —
|
|
6
6
|
* code churn predicts defects (Nagappan & Ball, ICSE'05), and "hotspots" =
|
|
7
7
|
* change-frequency × complexity surface the highest-ROI refactoring targets
|
|
8
|
-
* (Tornhill, *Your Code as a Crime Scene*; D'Ambros & Lanza
|
|
9
|
-
*
|
|
8
|
+
* (Tornhill, *Your Code as a Crime Scene*; D'Ambros & Lanza). We compute it
|
|
9
|
+
* 100% deterministically:
|
|
10
10
|
*
|
|
11
11
|
* change-frequency ← `git log --name-only` over a window (no blob fetch —
|
|
12
12
|
* works on a blobless clone; uses commit/tree metadata)
|
|
13
13
|
* complexity proxy ← current lines-of-code of the file (HEAD blob)
|
|
14
14
|
* hotspot score ← changeCount × loc, ranked
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* or dependency checker answers, and one a CTO actually pays for.
|
|
15
|
+
* expert ← the author with the most commits to the file (who to ask)
|
|
16
|
+
* trend ← commits per time bucket (oldest→newest), for a sparkline
|
|
18
17
|
*/
|
|
19
18
|
import { git, readText, isGitRepo } from "../util.js";
|
|
20
19
|
import { join } from "node:path";
|
|
@@ -22,39 +21,79 @@ const SKIP_DIR = /(^|\/)(node_modules|\.git|dist|build|vendor|\.next|coverage|__
|
|
|
22
21
|
const CODE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|c|h|cpp|cc|rb|php|cs|kt|swift|scala|vue|svelte)$/i;
|
|
23
22
|
export function analyzeHotspots(repoPath, now, windowDays = 365) {
|
|
24
23
|
if (!isGitRepo(repoPath)) {
|
|
25
|
-
return { windowDays, filesConsidered: 0, hotspots: [], note: "Not a git repository — hotspot history unavailable." };
|
|
24
|
+
return { windowDays, filesConsidered: 0, hotspots: [], trend: [], note: "Not a git repository — hotspot history unavailable." };
|
|
26
25
|
}
|
|
27
26
|
const since = new Date(now - windowDays * 86_400_000).toISOString();
|
|
28
|
-
//
|
|
29
|
-
// per
|
|
30
|
-
const raw = git(repoPath, ["log", "--since", since, "--no-merges", "--name-only", "--pretty=format:"]);
|
|
27
|
+
// per commit: "C<TAB><email>" then changed file paths. One pass → change
|
|
28
|
+
// frequency per file + the dominant author per file (the "expert").
|
|
29
|
+
const raw = git(repoPath, ["log", "--since", since, "--no-merges", "--name-only", "--pretty=format:C%x09%ae"]);
|
|
31
30
|
if (!raw.trim())
|
|
32
|
-
return { windowDays, filesConsidered: 0, hotspots: [], note: "No commit activity in the window." };
|
|
31
|
+
return { windowDays, filesConsidered: 0, hotspots: [], trend: [], note: "No commit activity in the window." };
|
|
33
32
|
const changes = new Map();
|
|
33
|
+
const authorsByFile = new Map();
|
|
34
|
+
let curAuthor = "";
|
|
34
35
|
for (const line of raw.split("\n")) {
|
|
36
|
+
if (line.startsWith("C\t")) {
|
|
37
|
+
curAuthor = line.slice(2).trim();
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
35
40
|
const f = line.trim();
|
|
36
41
|
if (!f || SKIP_DIR.test(f) || !CODE_EXT.test(f))
|
|
37
42
|
continue;
|
|
38
43
|
changes.set(f, (changes.get(f) ?? 0) + 1);
|
|
44
|
+
if (curAuthor) {
|
|
45
|
+
let m = authorsByFile.get(f);
|
|
46
|
+
if (!m) {
|
|
47
|
+
m = new Map();
|
|
48
|
+
authorsByFile.set(f, m);
|
|
49
|
+
}
|
|
50
|
+
m.set(curAuthor, (m.get(curAuthor) ?? 0) + 1);
|
|
51
|
+
}
|
|
39
52
|
}
|
|
40
53
|
if (changes.size === 0)
|
|
41
|
-
return { windowDays, filesConsidered: 0, hotspots: [], note: "No source-file changes in the window." };
|
|
42
|
-
// join change-frequency with current size (complexity proxy). Only read LOC
|
|
43
|
-
// for the most-changed files (bounded work).
|
|
54
|
+
return { windowDays, filesConsidered: 0, hotspots: [], trend: [], note: "No source-file changes in the window." };
|
|
44
55
|
const ranked = [...changes.entries()].sort((a, b) => b[1] - a[1]).slice(0, 80);
|
|
45
56
|
const rows = ranked.map(([file, changeCount]) => {
|
|
46
57
|
const txt = readText(join(repoPath, file));
|
|
47
58
|
const loc = txt ? txt.split("\n").length : 0;
|
|
48
|
-
|
|
59
|
+
const m = authorsByFile.get(file);
|
|
60
|
+
let expert = "", top = 0, authors = 0;
|
|
61
|
+
if (m) {
|
|
62
|
+
authors = m.size;
|
|
63
|
+
for (const [a, c] of m)
|
|
64
|
+
if (c > top) {
|
|
65
|
+
top = c;
|
|
66
|
+
expert = a;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { file, changes: changeCount, loc, score: changeCount * loc, expert, authors };
|
|
49
70
|
}).filter((r) => r.loc > 0);
|
|
50
71
|
rows.sort((a, b) => b.score - a.score);
|
|
72
|
+
// trend: commits per equal bucket over the window (oldest → newest)
|
|
73
|
+
const BUCKETS = 8;
|
|
74
|
+
const trend = new Array(BUCKETS).fill(0);
|
|
75
|
+
const winMs = windowDays * 86_400_000;
|
|
76
|
+
const tsRaw = git(repoPath, ["log", "--since", since, "--no-merges", "--pretty=format:%ct"]);
|
|
77
|
+
for (const l of tsRaw.split("\n")) {
|
|
78
|
+
const sec = parseInt(l.trim(), 10);
|
|
79
|
+
if (!Number.isFinite(sec))
|
|
80
|
+
continue;
|
|
81
|
+
const ageMs = now - sec * 1000;
|
|
82
|
+
let idx = BUCKETS - 1 - Math.floor((ageMs / winMs) * BUCKETS);
|
|
83
|
+
if (idx < 0)
|
|
84
|
+
idx = 0;
|
|
85
|
+
if (idx > BUCKETS - 1)
|
|
86
|
+
idx = BUCKETS - 1;
|
|
87
|
+
trend[idx]++;
|
|
88
|
+
}
|
|
51
89
|
const top = rows[0];
|
|
52
90
|
return {
|
|
53
91
|
windowDays,
|
|
54
92
|
filesConsidered: changes.size,
|
|
55
93
|
hotspots: rows.slice(0, 15),
|
|
94
|
+
trend,
|
|
56
95
|
note: top
|
|
57
|
-
? `Hotspot: ${top.file} — changed ${top.changes}× and ${top.loc} lines. High churn × size = where defects and refactoring ROI concentrate
|
|
96
|
+
? `Hotspot: ${top.file} — changed ${top.changes}× and ${top.loc} lines${top.expert ? ` (ask: ${top.expert})` : ""}. High churn × size = where defects and refactoring ROI concentrate.`
|
|
58
97
|
: "No hotspots surfaced.",
|
|
59
98
|
};
|
|
60
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotspots.js","sourceRoot":"","sources":["../../src/battery/hotspots.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hotspots.js","sourceRoot":"","sources":["../../src/battery/hotspots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,QAAQ,GAAG,8FAA8F,CAAC;AAChH,MAAM,QAAQ,GAAG,0FAA0F,CAAC;AAE5G,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,GAAW,EAAE,UAAU,GAAG,GAAG;IAC7E,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,qDAAqD,EAAE,CAAC;IAClI,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,yEAAyE;IACzE,oEAAoE;IACpE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,0BAA0B,CAAC,CAAC,CAAC;IAC/G,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC;IAE/H,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC7D,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QAC1D,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1C,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAAC,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;gBAAC,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAAC,CAAC;YACjF,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,uCAAuC,EAAE,CAAC;IAE1I,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE;QAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE,CAAC;YAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC;YAAC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;oBAAC,GAAG,GAAG,CAAC,CAAC;oBAAC,MAAM,GAAG,CAAC,CAAC;gBAAC,CAAC;QAAC,CAAC;QAC1F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACxF,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAEvC,oEAAoE;IACpE,MAAM,OAAO,GAAG,CAAC,CAAC;IAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAS,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,UAAU,GAAG,UAAU,CAAC;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAC7F,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QACpC,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;QAC/B,IAAI,GAAG,GAAG,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;QAC9D,IAAI,GAAG,GAAG,CAAC;YAAE,GAAG,GAAG,CAAC,CAAC;QAAC,IAAI,GAAG,GAAG,OAAO,GAAG,CAAC;YAAE,GAAG,GAAG,OAAO,GAAG,CAAC,CAAC;QAC/D,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACf,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO;QACL,UAAU;QACV,eAAe,EAAE,OAAO,CAAC,IAAI;QAC7B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC3B,KAAK;QACL,IAAI,EAAE,GAAG;YACP,CAAC,CAAC,YAAY,GAAG,CAAC,IAAI,cAAc,GAAG,CAAC,OAAO,SAAS,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,sEAAsE;YACvL,CAAC,CAAC,uBAAuB;KAC5B,CAAC;AACJ,CAAC"}
|
package/dist/bin.js
CHANGED
|
@@ -12,13 +12,40 @@ import { buildXRay } from "./engine.js";
|
|
|
12
12
|
import { sealXRay, verifyXRay } from "./sign.js";
|
|
13
13
|
import { xrayLeaksRaw } from "./privacy.js";
|
|
14
14
|
import { publishReport } from "./publish.js";
|
|
15
|
+
import { runBridge } from "./bridge.js";
|
|
15
16
|
import { existsSync } from "node:fs";
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
16
18
|
function flagVal(args, name) {
|
|
17
19
|
const i = args.indexOf(name);
|
|
18
20
|
return i >= 0 && args[i + 1] && !args[i + 1].startsWith("--") ? args[i + 1] : undefined;
|
|
19
21
|
}
|
|
20
22
|
async function main() {
|
|
21
23
|
const args = process.argv.slice(2);
|
|
24
|
+
// `bridge` — run the local agent so the website can scan local folders
|
|
25
|
+
if (args[0] === "bridge") {
|
|
26
|
+
runBridge(parseInt(flagVal(args, "--port") || process.env.XRAY_BRIDGE_PORT || "7799", 10));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// `ci-gate` — fail CI if a changed file is a high hotspot (enterprise gate)
|
|
30
|
+
if (args[0] === "ci-gate") {
|
|
31
|
+
const path = args.find((a, i) => i > 0 && !a.startsWith("--") && a !== flagVal(args, "--changed")) || ".";
|
|
32
|
+
const threshold = parseInt(flagVal(args, "--threshold") || "3", 10); // top-N hotspots are "high"
|
|
33
|
+
const report = await buildXRay({ repoPath: path });
|
|
34
|
+
const top = report.hotspots.hotspots.slice(0, threshold).map((h) => h.file);
|
|
35
|
+
let changed = (flagVal(args, "--changed") || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
36
|
+
if (changed.length === 0) {
|
|
37
|
+
// derive from the last commit if not supplied
|
|
38
|
+
const r = spawnSync("git", ["diff", "--name-only", "HEAD~1", "HEAD"], { cwd: path, encoding: "utf8" });
|
|
39
|
+
changed = (r.stdout || "").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
40
|
+
}
|
|
41
|
+
const hits = changed.filter((f) => top.includes(f));
|
|
42
|
+
if (hits.length) {
|
|
43
|
+
process.stdout.write(`⚠️ CI GATE: changed files touch top-${threshold} hotspots — review carefully:\n${hits.map((h) => " 🔥 " + h).join("\n")}\n`);
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
process.stdout.write(`✓ CI GATE: no changed file is a top-${threshold} hotspot.\n`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
22
49
|
const target = args.find((a) => !a.startsWith("--") && a !== flagVal(args, "--server") && a !== flagVal(args, "--token"));
|
|
23
50
|
const asJson = args.includes("--json");
|
|
24
51
|
const noSign = args.includes("--no-sign");
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AA6CA,wBAAgB,SAAS,CAAC,IAAI,SAAO,GAAG,IAAI,CAoC3C"}
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE LOCAL BRIDGE — how the cloud website reads a LOCAL folder without the
|
|
3
|
+
* code ever leaving the machine.
|
|
4
|
+
*
|
|
5
|
+
* The user runs `npx @mneme-ai/xray bridge` on their own machine. It starts a
|
|
6
|
+
* tiny server bound to 127.0.0.1 only. The website (https://xray.mneme-ai.space)
|
|
7
|
+
* fetches it over localhost — browsers exempt http://localhost from mixed-content
|
|
8
|
+
* blocking, so an https page may call it. The bridge analyses the local path and
|
|
9
|
+
* returns ONLY a signed, raw-free report. The cloud server is never involved;
|
|
10
|
+
* the source never moves. The page renders the report exactly like a cloud scan.
|
|
11
|
+
*
|
|
12
|
+
* GET /bridge/ping → { ok, version } (the page auto-detects it)
|
|
13
|
+
* POST /bridge/xray { path } → build local X-Ray → raw-free gate → sign → return
|
|
14
|
+
*/
|
|
15
|
+
import { createServer } from "node:http";
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { buildXRay } from "./engine.js";
|
|
20
|
+
import { sealXRay } from "./sign.js";
|
|
21
|
+
import { xrayLeaksRaw } from "./privacy.js";
|
|
22
|
+
const ALLOW = [
|
|
23
|
+
/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/,
|
|
24
|
+
/^https:\/\/xray\.mneme-ai\.space$/,
|
|
25
|
+
/^https:\/\/xray\.161\.35\.122\.73\.nip\.io$/,
|
|
26
|
+
];
|
|
27
|
+
function version() {
|
|
28
|
+
try {
|
|
29
|
+
const p = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
30
|
+
return JSON.parse(readFileSync(p, "utf8")).version ?? "0.0.0";
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return "0.0.0";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function cors(res, origin) {
|
|
37
|
+
const ok = origin && ALLOW.some((re) => re.test(origin));
|
|
38
|
+
res.setHeader("access-control-allow-origin", ok ? origin : "https://xray.mneme-ai.space");
|
|
39
|
+
res.setHeader("access-control-allow-methods", "GET,POST,OPTIONS");
|
|
40
|
+
res.setHeader("access-control-allow-headers", "content-type");
|
|
41
|
+
res.setHeader("vary", "origin");
|
|
42
|
+
}
|
|
43
|
+
function json(res, status, body) {
|
|
44
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
45
|
+
res.end(JSON.stringify(body));
|
|
46
|
+
}
|
|
47
|
+
export function runBridge(port = 7799) {
|
|
48
|
+
const server = createServer(async (req, res) => {
|
|
49
|
+
cors(res, req.headers.origin);
|
|
50
|
+
if (req.method === "OPTIONS") {
|
|
51
|
+
res.writeHead(204);
|
|
52
|
+
res.end();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const url = new URL(req.url || "/", "http://127.0.0.1");
|
|
56
|
+
if (req.method === "GET" && url.pathname === "/bridge/ping") {
|
|
57
|
+
return json(res, 200, { ok: true, agent: "mneme-xray-bridge", version: version() });
|
|
58
|
+
}
|
|
59
|
+
if (req.method === "POST" && url.pathname === "/bridge/xray") {
|
|
60
|
+
let body = "";
|
|
61
|
+
req.on("data", (c) => (body += c));
|
|
62
|
+
req.on("end", async () => {
|
|
63
|
+
try {
|
|
64
|
+
const { path } = JSON.parse(body || "{}");
|
|
65
|
+
if (!path || !existsSync(path))
|
|
66
|
+
return json(res, 400, { error: "path does not exist on this machine: " + (path || "(empty)") });
|
|
67
|
+
const report = await buildXRay({ repoPath: path });
|
|
68
|
+
if (xrayLeaksRaw(report).leaks)
|
|
69
|
+
return json(res, 500, { error: "internal: raw-free gate" });
|
|
70
|
+
const signed = sealXRay(existsSync(path) ? path : process.cwd(), report);
|
|
71
|
+
process.stdout.write(` ✓ local X-Ray: ${path} → grade ${report.summary.grade} (source never left this machine)\n`);
|
|
72
|
+
return json(res, 200, signed);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
return json(res, 500, { error: e.message.slice(0, 300) });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
json(res, 404, { error: "not found" });
|
|
81
|
+
});
|
|
82
|
+
server.listen(port, "127.0.0.1", () => {
|
|
83
|
+
process.stdout.write(`\n 🖥 Mneme X-Ray — LOCAL BRIDGE running at http://127.0.0.1:${port}\n` +
|
|
84
|
+
` Open https://xray.mneme-ai.space → it auto-detects this bridge → scan any local folder.\n` +
|
|
85
|
+
` Your source never leaves this machine. Ctrl-C to stop.\n\n`);
|
|
86
|
+
});
|
|
87
|
+
process.on("SIGINT", () => { server.close(() => process.exit(0)); });
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/clone.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"AAaA,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE;AAElE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAgBrD"}
|
package/dist/clone.js
CHANGED
|
@@ -7,7 +7,8 @@ import { spawnSync } from "node:child_process";
|
|
|
7
7
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
|
|
10
|
+
// owner/repo, OR deeper (GitLab nested subgroups: group/subgroup/repo). >=2 segments.
|
|
11
|
+
const ALLOWED = /^https:\/\/(github\.com|gitlab\.com|bitbucket\.org)\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+\/?$/;
|
|
11
12
|
export function isAllowedPublicUrl(url) {
|
|
12
13
|
if (/@|:\/\/[^/]*:[^/]*@/.test(url.replace("https://", "")))
|
|
13
14
|
return false; // reject embedded creds
|
package/dist/clone.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clone.js","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"clone.js","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,sFAAsF;AACtF,MAAM,OAAO,GAAG,gGAAgG,CAAC;AAEjH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,wBAAwB;IACnG,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AAClC,CAAC;AAID;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IACD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,EAAE;QAC3G,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7G,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC"}
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAsB,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAsB,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUlE,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,+CAA+C;IAC/C,UAAU,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAkDvE"}
|
package/dist/engine.js
CHANGED
|
@@ -12,6 +12,7 @@ import { analyzeBusFactor } from "./battery/busfactor.js";
|
|
|
12
12
|
import { analyzeAge } from "./battery/age.js";
|
|
13
13
|
import { analyzeComplexity } from "./battery/complexity.js";
|
|
14
14
|
import { analyzeHotspots } from "./battery/hotspots.js";
|
|
15
|
+
import { analyzeCoupling } from "./battery/coupling.js";
|
|
15
16
|
import { shallowClone } from "./clone.js";
|
|
16
17
|
import { headCommit, repoNameFromUrl, repoNameFromPath } from "./util.js";
|
|
17
18
|
export async function buildXRay(opts) {
|
|
@@ -43,8 +44,9 @@ export async function buildXRay(opts) {
|
|
|
43
44
|
const age = analyzeAge(repoPath, now);
|
|
44
45
|
const complexity = analyzeComplexity(repoPath, maxFiles);
|
|
45
46
|
const hotspots = analyzeHotspots(repoPath, now);
|
|
46
|
-
const
|
|
47
|
-
const
|
|
47
|
+
const coupling = analyzeCoupling(repoPath, now);
|
|
48
|
+
const summary = grade({ deps, secrets, busFactor, age, complexity, hotspots, coupling });
|
|
49
|
+
const blocks = { deps, secrets, busFactor, age, complexity, hotspots, coupling };
|
|
48
50
|
const fingerprint = createHash("sha256")
|
|
49
51
|
.update(JSON.stringify({ subject: { repoName: subject.repoName, commitHash: subject.commitHash }, blocks }))
|
|
50
52
|
.digest("hex");
|
|
@@ -82,14 +84,19 @@ function grade(b) {
|
|
|
82
84
|
? `🔑 No credential patterns in production code${exTail}.`
|
|
83
85
|
: `🔑 ${b.secrets.totalFindings} credential-pattern match(es) in production code — review${exTail}.`);
|
|
84
86
|
}
|
|
85
|
-
// deps
|
|
87
|
+
// deps (mortality + license)
|
|
86
88
|
if (b.deps.total > 0) {
|
|
87
89
|
signalsRun++;
|
|
88
90
|
const dying = b.deps.byBand.moribund + b.deps.byBand.dead;
|
|
91
|
+
const strongCopyleft = b.deps.licenses["strong-copyleft"];
|
|
89
92
|
score -= Math.min(20, dying * 5);
|
|
93
|
+
score -= Math.min(10, strongCopyleft * 5); // GPL/AGPL in a commercial codebase = real risk
|
|
90
94
|
bullets.push(dying === 0
|
|
91
95
|
? `📦 ${b.deps.total} deps, none dying.`
|
|
92
96
|
: `💀 ${dying} of ${b.deps.total} deps are dying${b.deps.atRisk[0]?.successor ? ` (e.g. ${b.deps.atRisk[0].name} → ${b.deps.atRisk[0].successor})` : ""}.`);
|
|
97
|
+
const copyleft = b.deps.licenses["strong-copyleft"] + b.deps.licenses["weak-copyleft"];
|
|
98
|
+
if (copyleft > 0)
|
|
99
|
+
bullets.push(`⚖️ ${copyleft} copyleft-licensed dep(s)${b.deps.licenseFlags[0] ? ` (e.g. ${b.deps.licenseFlags[0].name}: ${b.deps.licenseFlags[0].license})` : ""} — review for commercial use.`);
|
|
93
100
|
}
|
|
94
101
|
// bus factor
|
|
95
102
|
if (b.busFactor.authors > 0) {
|
|
@@ -126,7 +133,13 @@ function grade(b) {
|
|
|
126
133
|
if (b.hotspots.hotspots.length > 0) {
|
|
127
134
|
signalsRun++;
|
|
128
135
|
const h = b.hotspots.hotspots[0];
|
|
129
|
-
bullets.push(`🔥 Refactor first: ${h.file} — changed ${h.changes}× · ${h.loc} lines (
|
|
136
|
+
bullets.push(`🔥 Refactor first: ${h.file} — changed ${h.changes}× · ${h.loc} lines${h.expert ? ` (ask: ${h.expert})` : ""}.`);
|
|
137
|
+
}
|
|
138
|
+
// change-coupling — informational (hidden dependencies)
|
|
139
|
+
if (b.coupling.pairs.length > 0) {
|
|
140
|
+
signalsRun++;
|
|
141
|
+
const c = b.coupling.pairs[0];
|
|
142
|
+
bullets.push(`🔗 ${c.a} ⇄ ${c.b} change together ${Math.round(c.confidence * 100)}%${c.hidden ? " (hidden cross-dir coupling)" : ""}.`);
|
|
130
143
|
}
|
|
131
144
|
score = Math.max(0, Math.min(100, Math.round(score)));
|
|
132
145
|
const g = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 55 ? "D" : "F";
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAO1E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAkB;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;IAEvC,IAAI,QAAgB,CAAC;IACrB,IAAI,OAAO,GAAwB,IAAI,CAAC;IACxC,IAAI,OAA8B,CAAC;IAEnC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC;QAClB,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QACpB,OAAO,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IACjH,CAAC;SAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7F,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QACzB,OAAO,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC5G,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAE1C,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAEhD,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/gauntlet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gauntlet.d.ts","sourceRoot":"","sources":["../src/gauntlet.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,0BAA0B,EAAE,OAAO,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"gauntlet.d.ts","sourceRoot":"","sources":["../src/gauntlet.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,0BAA0B,EAAE,OAAO,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAmBD,wBAAgB,YAAY,IAAI,YAAY,CAyB3C"}
|
package/dist/gauntlet.js
CHANGED
|
@@ -11,12 +11,13 @@ function cleanFixture() {
|
|
|
11
11
|
subject: { kind: "git-url", ref: "https://github.com/acme/widget", repoName: "acme/widget", commitHash: "abc123def456" },
|
|
12
12
|
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
13
13
|
summary: { headline: "Mixed", grade: "C", signalsRun: 5, bullets: ["🔑 No credential patterns found in tracked files."] },
|
|
14
|
-
deps: { total: 12, byBand: { thriving: 8, healthy: 2, watch: 1, moribund: 1, dead: 0 }, atRisk: [{ name: "request", band: "dead", probability18mo: 0.9, successor: "got" }], partial: false, note: "1 dying." },
|
|
14
|
+
deps: { total: 12, byBand: { thriving: 8, healthy: 2, watch: 1, moribund: 1, dead: 0 }, atRisk: [{ name: "request", band: "dead", probability18mo: 0.9, successor: "got" }], licenses: { permissive: 11, "weak-copyleft": 0, "strong-copyleft": 1, unknown: 0 }, licenseFlags: [{ name: "some-gpl-lib", license: "GPL-3.0", class: "strong-copyleft" }], partial: false, note: "1 dying." },
|
|
15
15
|
secrets: { filesScanned: 40, totalFindings: 0, excludedTestHits: 3, byKind: {}, hits: [], worstVerdict: "ALLOW", note: "clean" },
|
|
16
16
|
busFactor: { authors: 5, singleOwnerFilePct: 22.5, fragileFiles: [{ file: "src/core.ts", topAuthorShare: 0.9, commits: 30 }], topContributorShare: 41.2, busFactor: 2, note: "5 authors" },
|
|
17
17
|
age: { bornAt: "2020-01-01", lastCommitAt: "2026-01-01", lifespan: "6 years", lifespanDays: 2192, totalCommits: 1200, totalAuthors: 5, dormant: false, vitality: "active", note: "active" },
|
|
18
18
|
complexity: { filesAnalysed: 40, totalSymbols: 320, hotspots: [{ file: "src/core.ts", symbol: "function handleRequest(req, res)", bodyLines: 180, startLine: 12 }], maxDepth: 4, note: "hotspot" },
|
|
19
|
-
hotspots: { windowDays: 365, filesConsidered: 40, hotspots: [{ file: "src/core.ts", changes: 30, loc: 400, score: 12000 }], note: "Hotspot: src/core.ts — changed 30× and 400 lines." },
|
|
19
|
+
hotspots: { windowDays: 365, filesConsidered: 40, hotspots: [{ file: "src/core.ts", changes: 30, loc: 400, score: 12000, expert: "alice@acme.dev", authors: 3 }], trend: [2, 4, 6, 3, 8, 5, 7, 9], note: "Hotspot: src/core.ts — changed 30× and 400 lines." },
|
|
20
|
+
coupling: { windowDays: 365, pairs: [{ a: "src/core.ts", b: "src/api/routes.ts", coChanges: 12, confidence: 0.8, hidden: true }], note: "src/core.ts ⇄ src/api/routes.ts change together 80%." },
|
|
20
21
|
fingerprint: "deadbeef",
|
|
21
22
|
};
|
|
22
23
|
}
|
package/dist/gauntlet.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gauntlet.js","sourceRoot":"","sources":["../src/gauntlet.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAW5C,SAAS,YAAY;IACnB,OAAO;QACL,CAAC,EAAE,CAAC;QACJ,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,gCAAgC,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE;QACxH,WAAW,EAAE,0BAA0B;QACvC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,mDAAmD,CAAC,EAAE;QACzH,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE;
|
|
1
|
+
{"version":3,"file":"gauntlet.js","sourceRoot":"","sources":["../src/gauntlet.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAW5C,SAAS,YAAY;IACnB,OAAO;QACL,CAAC,EAAE,CAAC;QACJ,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,gCAAgC,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE;QACxH,WAAW,EAAE,0BAA0B;QACvC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,mDAAmD,CAAC,EAAE;QACzH,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE;QAC3X,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE;QAChI,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE;QAC1L,GAAG,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC3L,UAAU,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,kCAAkC,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;QAClM,QAAQ,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,eAAe,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,mDAAmD,EAAE;QAC9P,QAAQ,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,mBAAmB,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,sDAAsD,EAAE;QAChM,WAAW,EAAE,UAAU;KACxB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,wEAAwE;IACxE,8DAA8D;IAC9D,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,MAAM,iBAAiB,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC;IAC9D,MAAM,0BAA0B,GAAG,iBAAiB,CAAC,CAAC,oCAAoC;IAE1F,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAC/B,QAAQ,CAAC,GAAmC,CAAC,IAAI,GAAG,qDAAqD,CAAC;IAC3G,MAAM,qBAAqB,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC;IAEpE,8DAA8D;IAC9D,IAAI,cAAc,GAAG,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAa,CAAC,CAAC;QAC3D,MAAM,CAAC,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QAClC,cAAc,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,cAAc,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,MAAM,GAAG,GAAG,iBAAiB,IAAI,qBAAqB,IAAI,0BAA0B,IAAI,cAAc,CAAC;IACvG,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,cAAc,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACxH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -18,4 +18,7 @@ export { defaultFetcher, type MetaFetcher } from "./battery/deps.js";
|
|
|
18
18
|
export { publishReport, type PublishResult } from "./publish.js";
|
|
19
19
|
export { createXRayServer } from "./server.js";
|
|
20
20
|
export { CosmicMonitor, computeStatus, cosmicBadgeSvg, type CosmicStatus, type Sample } from "./cosmic.js";
|
|
21
|
+
export { runBridge } from "./bridge.js";
|
|
22
|
+
export { analyzeCoupling } from "./battery/coupling.js";
|
|
23
|
+
export { licenseClass } from "./battery/deps.js";
|
|
21
24
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAC3G,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,4 +18,7 @@ export { defaultFetcher } from "./battery/deps.js";
|
|
|
18
18
|
export { publishReport } from "./publish.js";
|
|
19
19
|
export { createXRayServer } from "./server.js";
|
|
20
20
|
export { CosmicMonitor, computeStatus, cosmicBadgeSvg } from "./cosmic.js";
|
|
21
|
+
export { runBridge } from "./bridge.js";
|
|
22
|
+
export { analyzeCoupling } from "./battery/coupling.js";
|
|
23
|
+
export { licenseClass } from "./battery/deps.js";
|
|
21
24
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAqB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAqB,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,cAAc,EAAoB,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAkC,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAqB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAqB,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,cAAc,EAAoB,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAkC,MAAM,aAAa,CAAC;AAC3G,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface XRaySubject {
|
|
|
21
21
|
/** HEAD commit the report was computed at (provenance). */
|
|
22
22
|
commitHash: string;
|
|
23
23
|
}
|
|
24
|
-
/** Dependency mortality
|
|
24
|
+
/** Dependency mortality + license/supply-chain risk. */
|
|
25
25
|
export interface DepsBlock {
|
|
26
26
|
total: number;
|
|
27
27
|
byBand: Record<"thriving" | "healthy" | "watch" | "moribund" | "dead", number>;
|
|
@@ -32,10 +32,31 @@ export interface DepsBlock {
|
|
|
32
32
|
probability18mo: number;
|
|
33
33
|
successor: string | null;
|
|
34
34
|
}>;
|
|
35
|
+
/** License class counts (permissive / copyleft / unknown). */
|
|
36
|
+
licenses: Record<"permissive" | "weak-copyleft" | "strong-copyleft" | "unknown", number>;
|
|
37
|
+
/** Deps whose license is copyleft/unknown — a commercial-use review flag. */
|
|
38
|
+
licenseFlags: Array<{
|
|
39
|
+
name: string;
|
|
40
|
+
license: string;
|
|
41
|
+
class: string;
|
|
42
|
+
}>;
|
|
35
43
|
/** True when npm metadata could not be fetched for some deps (honest degradation). */
|
|
36
44
|
partial: boolean;
|
|
37
45
|
note: string;
|
|
38
46
|
}
|
|
47
|
+
/** Change-coupling (temporal coupling) — files that change together. */
|
|
48
|
+
export interface CouplingBlock {
|
|
49
|
+
windowDays: number;
|
|
50
|
+
/** Strongest co-change pairs. hidden = different directories (a non-obvious dependency). */
|
|
51
|
+
pairs: Array<{
|
|
52
|
+
a: string;
|
|
53
|
+
b: string;
|
|
54
|
+
coChanges: number;
|
|
55
|
+
confidence: number;
|
|
56
|
+
hidden: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
note: string;
|
|
59
|
+
}
|
|
39
60
|
/** Secret / credential leak scan (values are NEVER stored — only kind + count + location). */
|
|
40
61
|
export interface SecretsBlock {
|
|
41
62
|
filesScanned: number;
|
|
@@ -96,7 +117,9 @@ export interface ComplexityBlock {
|
|
|
96
117
|
maxDepth: number;
|
|
97
118
|
note: string;
|
|
98
119
|
}
|
|
99
|
-
/** Behavioral hotspots — change-frequency × size (refactor-ROI targets).
|
|
120
|
+
/** Behavioral hotspots — change-frequency × size (refactor-ROI targets).
|
|
121
|
+
* Each hotspot also names the primary author (who to ask = onboarding signal),
|
|
122
|
+
* and `trend` is per-bucket commit activity (oldest→newest) for a sparkline. */
|
|
100
123
|
export interface HotspotsBlock {
|
|
101
124
|
windowDays: number;
|
|
102
125
|
filesConsidered: number;
|
|
@@ -105,7 +128,11 @@ export interface HotspotsBlock {
|
|
|
105
128
|
changes: number;
|
|
106
129
|
loc: number;
|
|
107
130
|
score: number;
|
|
131
|
+
expert: string;
|
|
132
|
+
authors: number;
|
|
108
133
|
}>;
|
|
134
|
+
/** commit counts per equal time bucket, oldest → newest (sparkline). */
|
|
135
|
+
trend: number[];
|
|
109
136
|
note: string;
|
|
110
137
|
}
|
|
111
138
|
export interface XRaySummary {
|
|
@@ -127,6 +154,7 @@ export interface XRayReport {
|
|
|
127
154
|
age: AgeBlock;
|
|
128
155
|
complexity: ComplexityBlock;
|
|
129
156
|
hotspots: HotspotsBlock;
|
|
157
|
+
coupling: CouplingBlock;
|
|
130
158
|
/** sha256 over the canonicalised metric blocks — a tamper-evident content id. */
|
|
131
159
|
fingerprint: string;
|
|
132
160
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,GAAG,YAAY,CAAC;IAC/B,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mneme-ai/xray",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.151.0",
|
|
4
4
|
"description": "Mneme Repo X-Ray — a signed, raw-free, deterministic X-Ray of any repo. Every number is reproducible from git/AST/metadata and sealed with an offline-verifiable NOTARY receipt. No source code ever leaves the machine; no LLM guesses anything.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/public/card.js
CHANGED
|
@@ -4,6 +4,25 @@
|
|
|
4
4
|
const gC = (x) => "g-" + ("ABCDEF".includes(x) ? x : "C");
|
|
5
5
|
const pct = (n) => (n || 0) + "%";
|
|
6
6
|
|
|
7
|
+
// tiny unicode sparkline from an array of numbers
|
|
8
|
+
function sparkline(arr) {
|
|
9
|
+
if (!arr || !arr.length) return "";
|
|
10
|
+
const bars = "▁▂▃▄▅▆▇█", max = Math.max(1, ...arr);
|
|
11
|
+
return `<span class="spark" title="activity over time (oldest → newest)">${arr.map((n) => bars[Math.min(7, Math.round((n / max) * 7))]).join("")}</span>`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// one-line "what is this / what's it for" under every label
|
|
15
|
+
const INFO = {
|
|
16
|
+
Dependencies: "Are your libraries dying or legally risky?",
|
|
17
|
+
Secrets: "Leaked credentials sitting in the code.",
|
|
18
|
+
"Bus factor": "Key-person risk — what breaks if someone leaves.",
|
|
19
|
+
Vitality: "Is the project alive and maintained?",
|
|
20
|
+
Complexity: "The biggest, most tangled code.",
|
|
21
|
+
Hotspots: "Where bugs hide — refactor these first.",
|
|
22
|
+
Coupling: "Files that secretly change together.",
|
|
23
|
+
};
|
|
24
|
+
const kcell = (label) => `<div class="k">${label}${INFO[label] ? `<span class="kdesc">${INFO[label]}</span>` : ""}</div>`;
|
|
25
|
+
|
|
7
26
|
function xrayCardHTML(signed, opts) {
|
|
8
27
|
opts = opts || {};
|
|
9
28
|
const r = signed.report, s = r.summary;
|
|
@@ -16,8 +35,13 @@
|
|
|
16
35
|
Object.entries(sec.byKind).slice(0, 8).map(([k, n]) => `<span class="chip bad">${esc(k)} ×${n}</span>`).join("");
|
|
17
36
|
const fragile = (bf.fragileFiles || []).slice(0, 4).map((x) => `<span class="chip ${x.topAuthorShare >= 0.9 ? "warn" : ""}">${esc(x.file)} · ${Math.round(x.topAuthorShare * 100)}%</span>`).join("");
|
|
18
37
|
const hot = (cx.hotspots || []).slice(0, 4).map((h) => `<span class="chip">${esc(h.symbol).slice(0, 40)} · ${h.bodyLines}L</span>`).join("");
|
|
19
|
-
const hs = r.hotspots || { hotspots: [] };
|
|
20
|
-
const hsChips = (hs.hotspots || []).slice(0, 5).map((h) => `<span class="chip">${esc(h.file)} · ${h.changes}× · ${h.loc}L</span>`).join("") || `<span class="chip">none</span>`;
|
|
38
|
+
const hs = r.hotspots || { hotspots: [], trend: [] };
|
|
39
|
+
const hsChips = (hs.hotspots || []).slice(0, 5).map((h) => `<span class="chip">${esc(h.file)} · ${h.changes}× · ${h.loc}L${h.expert ? ` · ${esc(h.expert)}` : ""}</span>`).join("") || `<span class="chip">none</span>`;
|
|
40
|
+
const cp = r.coupling || { pairs: [] };
|
|
41
|
+
const cpChips = (cp.pairs || []).slice(0, 5).map((p) => `<span class="chip ${p.hidden ? "warn" : ""}">${esc(p.a)} ⇄ ${esc(p.b)} · ${Math.round(p.confidence * 100)}%</span>`).join("") || `<span class="chip">none</span>`;
|
|
42
|
+
const lic = (dep.licenses) || { permissive: 0, "weak-copyleft": 0, "strong-copyleft": 0, unknown: 0 };
|
|
43
|
+
const licChips = (dep.licenseFlags || []).slice(0, 5).map((l) => `<span class="chip ${l.class === "strong-copyleft" ? "bad" : "warn"}">${esc(l.name)} · ${esc(l.license)}</span>`).join("");
|
|
44
|
+
const spark = sparkline(hs.trend || []);
|
|
21
45
|
|
|
22
46
|
const share = opts.share ? `<div class="share" id="share"></div>` : "";
|
|
23
47
|
|
|
@@ -29,12 +53,13 @@
|
|
|
29
53
|
<div class="head">${esc(s.headline)} · ${s.signalsRun} signals · @ ${esc(String(r.subject.commitHash).slice(0, 10))}</div></div>
|
|
30
54
|
</div>
|
|
31
55
|
<div class="rows">
|
|
32
|
-
<div class="row"
|
|
33
|
-
<div class="row"
|
|
34
|
-
<div class="row"
|
|
35
|
-
<div class="row"
|
|
36
|
-
<div class="row"
|
|
37
|
-
<div class="row"
|
|
56
|
+
<div class="row">${kcell("Dependencies")}<div class="v"><span class="big">${dep.total}</span> total · ${dep.byBand.dead + dep.byBand.moribund} dying · ${(lic["strong-copyleft"] + lic["weak-copyleft"])} copyleft<div class="chips">${depChips}${licChips}</div></div></div>
|
|
57
|
+
<div class="row">${kcell("Secrets")}<div class="v"><span class="big">${sec.totalFindings}</span> in production code · ${sec.filesScanned} files${sec.excludedTestHits ? ` · <span class="muted">+${sec.excludedTestHits} in tests/docs (excluded)</span>` : ""}<div class="chips">${secChips}</div></div></div>
|
|
58
|
+
<div class="row">${kcell("Bus factor")}<div class="v"><span class="big">${bf.busFactor}</span> · top author ${pct(bf.topContributorShare)} · ${pct(bf.singleOwnerFilePct)} files single-owner<div class="chips">${fragile}</div></div></div>
|
|
59
|
+
<div class="row">${kcell("Vitality")}<div class="v"><span class="big">${esc(age.vitality)}</span> · ${esc(age.lifespan)} old · ${age.totalCommits} commits · ${age.totalAuthors} authors</div></div>
|
|
60
|
+
<div class="row">${kcell("Complexity")}<div class="v"><span class="big">${cx.totalSymbols}</span> symbols · ${cx.filesAnalysed} files · max depth ${cx.maxDepth}<div class="chips">${hot}</div></div></div>
|
|
61
|
+
<div class="row">${kcell("Hotspots")}<div class="v"><span class="muted">refactor-ROI · churn × size · last ${hs.windowDays||365}d</span> ${spark}<div class="chips">${hsChips}</div></div></div>
|
|
62
|
+
<div class="row">${kcell("Coupling")}<div class="v"><span class="muted">${(cp.pairs||[]).length} coupled pair(s) · hidden = cross-directory</span><div class="chips">${cpChips}</div></div></div>
|
|
38
63
|
</div>
|
|
39
64
|
<div class="foot">${verified}<span>fingerprint <code>${esc(String(r.fingerprint).slice(0, 28))}…</code></span></div>
|
|
40
65
|
${share}
|
package/public/index.html
CHANGED
|
@@ -109,7 +109,18 @@
|
|
|
109
109
|
.muted{color:var(--sub)}
|
|
110
110
|
/* crisper section separation */
|
|
111
111
|
.card .top{background:linear-gradient(#fff,#fcfcfd)}
|
|
112
|
-
.row .k{font-weight:560}
|
|
112
|
+
.row .k{font-weight:560;display:flex;flex-direction:column;gap:3px}
|
|
113
|
+
.kdesc{font-size:11px;color:var(--sub);text-transform:none;letter-spacing:0;font-weight:400;line-height:1.3}
|
|
114
|
+
.spark{font-family:ui-monospace,Menlo,monospace;letter-spacing:1px;color:var(--a)}
|
|
115
|
+
/* local-folder bridge panel */
|
|
116
|
+
.localbox{max-width:560px;margin:18px auto 0;border:1px solid var(--line);border-radius:14px;padding:16px 18px;background:var(--soft);display:none}
|
|
117
|
+
.localbox.on{display:block}
|
|
118
|
+
.localbox .lh{display:flex;align-items:center;gap:8px;font-size:13.5px;font-weight:560}
|
|
119
|
+
.localdot{width:8px;height:8px;border-radius:50%;background:var(--green)}
|
|
120
|
+
.localrow{display:flex;gap:8px;margin-top:10px}
|
|
121
|
+
.localrow input{flex:1;padding:11px 13px;border:1px solid var(--line);border-radius:10px;font-size:14px;font-family:ui-monospace,Menlo,monospace}
|
|
122
|
+
.localrow button{padding:11px 16px;border:0;border-radius:10px;background:var(--ink);color:#fff;cursor:pointer;font-size:14px}
|
|
123
|
+
.localhint{font-size:12px;color:var(--sub);margin-top:8px}
|
|
113
124
|
/* ---- mobile / cross-browser responsive ---- */
|
|
114
125
|
@media (max-width:680px){
|
|
115
126
|
.wrap{padding:0 16px}
|
|
@@ -156,6 +167,14 @@
|
|
|
156
167
|
<span id="keystate" class="kstate"></span>
|
|
157
168
|
</div>
|
|
158
169
|
</details>
|
|
170
|
+
<div class="localbox" id="localbox">
|
|
171
|
+
<div class="lh"><span class="localdot"></span>Local agent detected — scan a folder on <b>this machine</b> (source never leaves it)</div>
|
|
172
|
+
<div class="localrow">
|
|
173
|
+
<input id="localpath" placeholder="/absolute/path/to/your/repo (git or not)" autocomplete="off" spellcheck="false" />
|
|
174
|
+
<button id="localgo" type="button">Scan local</button>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="localhint">Private code? This reads it locally and shows the signed result here — nothing is uploaded.</div>
|
|
177
|
+
</div>
|
|
159
178
|
<p class="err" id="err" style="display:none"></p>
|
|
160
179
|
</header>
|
|
161
180
|
|
|
@@ -287,6 +306,30 @@ document.getElementById("f").addEventListener("submit", async e=>{
|
|
|
287
306
|
}catch(ex){ err.textContent = String(ex); err.style.display="block"; }
|
|
288
307
|
finally{ go.disabled=false; go.textContent="X-Ray"; }
|
|
289
308
|
});
|
|
309
|
+
// ---- LOCAL BRIDGE: detect the local agent + scan a folder (code never uploaded) ----
|
|
310
|
+
const BRIDGE = "http://127.0.0.1:7799";
|
|
311
|
+
async function detectBridge(){
|
|
312
|
+
try{
|
|
313
|
+
const ctrl=new AbortController(); const t=setTimeout(()=>ctrl.abort(),1200);
|
|
314
|
+
const r=await fetch(BRIDGE+"/bridge/ping",{signal:ctrl.signal}); clearTimeout(t);
|
|
315
|
+
if(r.ok){ document.getElementById("localbox").classList.add("on"); }
|
|
316
|
+
}catch{ /* no local agent — silent */ }
|
|
317
|
+
}
|
|
318
|
+
document.getElementById("localgo").addEventListener("click", async ()=>{
|
|
319
|
+
const p=document.getElementById("localpath").value.trim();
|
|
320
|
+
const err=document.getElementById("err"), btn=document.getElementById("localgo");
|
|
321
|
+
err.style.display="none"; document.getElementById("out").innerHTML="";
|
|
322
|
+
if(!p) return;
|
|
323
|
+
btn.disabled=true; btn.innerHTML='<span class="spin"></span>Scanning…';
|
|
324
|
+
try{
|
|
325
|
+
const res=await fetch(BRIDGE+"/bridge/xray",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:p})});
|
|
326
|
+
const data=await res.json();
|
|
327
|
+
if(!res.ok){ err.textContent=data.error||"local scan failed"; err.style.display="block"; }
|
|
328
|
+
else { render(data); window.scrollTo({top:0,behavior:"smooth"}); }
|
|
329
|
+
}catch(ex){ err.textContent="Could not reach the local agent. Run: npx @mneme-ai/xray bridge"; err.style.display="block"; }
|
|
330
|
+
finally{ btn.disabled=false; btn.textContent="Scan local"; }
|
|
331
|
+
});
|
|
332
|
+
detectBridge();
|
|
290
333
|
refreshKeyState();
|
|
291
334
|
loadList();
|
|
292
335
|
</script>
|
package/public/report.html
CHANGED
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
.top .repo{font-size:21px;font-weight:620;word-break:break-all}.top .head{color:var(--sub);font-size:14.5px;margin-top:2px}
|
|
22
22
|
.rows{padding:8px 28px 14px}
|
|
23
23
|
.row{display:flex;gap:14px;padding:15px 0;border-bottom:1px solid var(--soft);align-items:baseline}.row:last-child{border-bottom:0}
|
|
24
|
-
.row .k{font-size:13px;letter-spacing:.04em;text-transform:uppercase;color:var(--sub);width:
|
|
24
|
+
.row .k{font-size:13px;letter-spacing:.04em;text-transform:uppercase;color:var(--sub);width:140px;flex:none;display:flex;flex-direction:column;gap:3px}
|
|
25
|
+
.kdesc{font-size:11px;color:var(--sub);text-transform:none;letter-spacing:0;line-height:1.3}
|
|
26
|
+
.spark{font-family:ui-monospace,Menlo,monospace;letter-spacing:1px;color:#4f46e5}
|
|
27
|
+
.muted{color:var(--sub)}
|
|
28
|
+
@media (max-width:640px){ .row{flex-direction:column;gap:4px} .row .k{width:auto} }
|
|
25
29
|
.row .v{font-size:15.5px}.row .v .big{font-weight:640}
|
|
26
30
|
.chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
|
|
27
31
|
.chip{font-size:12.5px;padding:3px 9px;border-radius:999px;background:var(--soft);border:1px solid var(--line);color:#374151}
|