@mneme-ai/xray 2.150.0 → 2.152.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/busfactor.d.ts.map +1 -1
- package/dist/battery/busfactor.js +24 -8
- package/dist/battery/busfactor.js.map +1 -1
- 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/server.d.ts.map +1 -1
- package/dist/server.js +27 -6
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +30 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/public/card.js +33 -8
- package/public/index.html +44 -1
- package/public/report.html +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"busfactor.d.ts","sourceRoot":"","sources":["../../src/battery/busfactor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"busfactor.d.ts","sourceRoot":"","sources":["../../src/battery/busfactor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAuFjE"}
|
|
@@ -12,18 +12,38 @@ export function analyzeBusFactor(repoPath) {
|
|
|
12
12
|
return emptyBlock("Not a git repository — authorship/bus-factor signals unavailable.");
|
|
13
13
|
// One line per (commit, file): "<authorEmail>\t<file>". --no-renames keeps paths stable.
|
|
14
14
|
const raw = git(repoPath, [
|
|
15
|
-
"log", "--no-merges", "--pretty=format:C%H%x09%ae", "--name-only", "-n", "
|
|
15
|
+
"log", "--no-merges", "--no-renames", "--pretty=format:C%H%x09%ae", "--name-only", "-n", "3000",
|
|
16
16
|
]);
|
|
17
17
|
if (!raw.trim()) {
|
|
18
18
|
return emptyBlock("No commit history available.");
|
|
19
19
|
}
|
|
20
|
+
// A commit touching a huge number of files is a merge/vendoring/generated
|
|
21
|
+
// sweep — it does NOT signal real per-file ownership and, on monorepos like
|
|
22
|
+
// mattermost (22k commits), folding it makes the map explode (~120s → ~3s).
|
|
23
|
+
// We still count it toward author totals; we just skip it for file-ownership.
|
|
24
|
+
const MAX_FILES_PER_COMMIT = 100;
|
|
20
25
|
const fileAuthors = new Map(); // file -> author -> commits
|
|
21
26
|
const authorCommits = new Map(); // author -> total commits
|
|
22
27
|
const allAuthors = new Set();
|
|
23
28
|
let curAuthor = "";
|
|
29
|
+
let curFiles = [];
|
|
24
30
|
let totalCommits = 0;
|
|
31
|
+
const flush = () => {
|
|
32
|
+
if (curAuthor && curFiles.length > 0 && curFiles.length <= MAX_FILES_PER_COMMIT) {
|
|
33
|
+
for (const file of curFiles) {
|
|
34
|
+
let m = fileAuthors.get(file);
|
|
35
|
+
if (!m) {
|
|
36
|
+
m = new Map();
|
|
37
|
+
fileAuthors.set(file, m);
|
|
38
|
+
}
|
|
39
|
+
m.set(curAuthor, (m.get(curAuthor) ?? 0) + 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
curFiles = [];
|
|
43
|
+
};
|
|
25
44
|
for (const line of raw.split("\n")) {
|
|
26
45
|
if (line.startsWith("C")) {
|
|
46
|
+
flush();
|
|
27
47
|
const tab = line.indexOf("\t");
|
|
28
48
|
curAuthor = tab >= 0 ? line.slice(tab + 1).trim() : "";
|
|
29
49
|
if (curAuthor) {
|
|
@@ -34,15 +54,11 @@ export function analyzeBusFactor(repoPath) {
|
|
|
34
54
|
continue;
|
|
35
55
|
}
|
|
36
56
|
const file = line.trim();
|
|
37
|
-
if (!file || SKIP_DIR.test(file)
|
|
57
|
+
if (!file || SKIP_DIR.test(file))
|
|
38
58
|
continue;
|
|
39
|
-
|
|
40
|
-
if (!m) {
|
|
41
|
-
m = new Map();
|
|
42
|
-
fileAuthors.set(file, m);
|
|
43
|
-
}
|
|
44
|
-
m.set(curAuthor, (m.get(curAuthor) ?? 0) + 1);
|
|
59
|
+
curFiles.push(file);
|
|
45
60
|
}
|
|
61
|
+
flush();
|
|
46
62
|
if (totalCommits === 0)
|
|
47
63
|
return emptyBlock("No authored commits found.");
|
|
48
64
|
let singleOwner = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"busfactor.js","sourceRoot":"","sources":["../../src/battery/busfactor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,QAAQ,GAAG,oEAAoE,CAAC;AACtF,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,2EAA2E;AAElG,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAAE,OAAO,UAAU,CAAC,mEAAmE,CAAC,CAAC;IACjH,yFAAyF;IACzF,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE;QACxB,KAAK,EAAE,aAAa,EAAE,4BAA4B,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"busfactor.js","sourceRoot":"","sources":["../../src/battery/busfactor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,QAAQ,GAAG,oEAAoE,CAAC;AACtF,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,2EAA2E;AAElG,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAAE,OAAO,UAAU,CAAC,mEAAmE,CAAC,CAAC;IACjH,yFAAyF;IACzF,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE;QACxB,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,4BAA4B,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM;KAChG,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QAChB,OAAO,UAAU,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,0EAA0E;IAC1E,4EAA4E;IAC5E,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,oBAAoB,GAAG,GAAG,CAAC;IACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA+B,CAAC,CAAC,4BAA4B;IACxF,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,0BAA0B;IAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,QAAQ,GAAa,EAAE,CAAC;IAC5B,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,IAAI,oBAAoB,EAAE,CAAC;YAChF,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;oBAAC,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;oBAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAAC,CAAC;gBACpD,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;QACD,QAAQ,GAAG,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,KAAK,EAAE,CAAC;YACR,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC/B,SAAS,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,IAAI,SAAS,EAAE,CAAC;gBACd,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC1B,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACtE,YAAY,EAAE,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,KAAK,EAAE,CAAC;IAER,IAAI,YAAY,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC,4BAA4B,CAAC,CAAC;IAExE,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,OAAO,GAAmC,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC;QACpC,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;YAAC,GAAG,IAAI,CAAC,CAAC;YAAC,IAAI,CAAC,GAAG,GAAG;gBAAE,GAAG,GAAG,CAAC,CAAC;QAAC,CAAC;QAC/D,IAAI,GAAG,GAAG,CAAC;YAAE,SAAS,CAAC,8BAA8B;QACrD,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;QACxB,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;YACvB,WAAW,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IAE9C,MAAM,eAAe,GAAG,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;IACxH,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtE,sEAAsE;IACtE,MAAM,MAAM,GAAG,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjE,IAAI,GAAG,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QAAC,GAAG,IAAI,CAAC,CAAC;QAAC,SAAS,EAAE,CAAC;QAAC,IAAI,GAAG,IAAI,YAAY,GAAG,GAAG;YAAE,MAAM;IAAC,CAAC;IAExF,OAAO;QACL,OAAO,EAAE,UAAU,CAAC,IAAI;QACxB,kBAAkB,EAAE,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,eAAe,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACrG,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAClC,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE;QACrD,SAAS;QACT,IAAI,EACF,SAAS,IAAI,CAAC;YACZ,CAAC,CAAC,+EAA+E;YACjF,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,aAAa,WAAW,6CAA6C;KAC9F,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,mBAAmB,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;AAC7G,CAAC"}
|
|
@@ -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":"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"}
|
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":"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"}
|
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"}
|