@mneme-ai/xray 2.150.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.
Files changed (79) hide show
  1. package/README.md +71 -0
  2. package/dist/battery/age.d.ts +3 -0
  3. package/dist/battery/age.d.ts.map +1 -0
  4. package/dist/battery/age.js +65 -0
  5. package/dist/battery/age.js.map +1 -0
  6. package/dist/battery/busfactor.d.ts +3 -0
  7. package/dist/battery/busfactor.d.ts.map +1 -0
  8. package/dist/battery/busfactor.js +92 -0
  9. package/dist/battery/busfactor.js.map +1 -0
  10. package/dist/battery/complexity.d.ts +3 -0
  11. package/dist/battery/complexity.d.ts.map +1 -0
  12. package/dist/battery/complexity.js +50 -0
  13. package/dist/battery/complexity.js.map +1 -0
  14. package/dist/battery/deps.d.ts +15 -0
  15. package/dist/battery/deps.d.ts.map +1 -0
  16. package/dist/battery/deps.js +107 -0
  17. package/dist/battery/deps.js.map +1 -0
  18. package/dist/battery/hotspots.d.ts +3 -0
  19. package/dist/battery/hotspots.d.ts.map +1 -0
  20. package/dist/battery/hotspots.js +61 -0
  21. package/dist/battery/hotspots.js.map +1 -0
  22. package/dist/battery/secrets.d.ts +3 -0
  23. package/dist/battery/secrets.d.ts.map +1 -0
  24. package/dist/battery/secrets.js +64 -0
  25. package/dist/battery/secrets.js.map +1 -0
  26. package/dist/bin.d.ts +3 -0
  27. package/dist/bin.d.ts.map +1 -0
  28. package/dist/bin.js +76 -0
  29. package/dist/bin.js.map +1 -0
  30. package/dist/clone.d.ts +13 -0
  31. package/dist/clone.d.ts.map +1 -0
  32. package/dist/clone.js +42 -0
  33. package/dist/clone.js.map +1 -0
  34. package/dist/cosmic.d.ts +35 -0
  35. package/dist/cosmic.d.ts.map +1 -0
  36. package/dist/cosmic.js +122 -0
  37. package/dist/cosmic.js.map +1 -0
  38. package/dist/engine.d.ts +8 -0
  39. package/dist/engine.d.ts.map +1 -0
  40. package/dist/engine.js +138 -0
  41. package/dist/engine.js.map +1 -0
  42. package/dist/gauntlet.d.ts +9 -0
  43. package/dist/gauntlet.d.ts.map +1 -0
  44. package/dist/gauntlet.js +47 -0
  45. package/dist/gauntlet.js.map +1 -0
  46. package/dist/index.d.ts +21 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +21 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/privacy.d.ts +12 -0
  51. package/dist/privacy.d.ts.map +1 -0
  52. package/dist/privacy.js +43 -0
  53. package/dist/privacy.js.map +1 -0
  54. package/dist/publish.d.ts +9 -0
  55. package/dist/publish.d.ts.map +1 -0
  56. package/dist/publish.js +28 -0
  57. package/dist/publish.js.map +1 -0
  58. package/dist/server.d.ts +29 -0
  59. package/dist/server.d.ts.map +1 -0
  60. package/dist/server.js +482 -0
  61. package/dist/server.js.map +1 -0
  62. package/dist/sign.d.ts +7 -0
  63. package/dist/sign.d.ts.map +1 -0
  64. package/dist/sign.js +33 -0
  65. package/dist/sign.js.map +1 -0
  66. package/dist/types.d.ts +148 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +16 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/util.d.ts +21 -0
  71. package/dist/util.d.ts.map +1 -0
  72. package/dist/util.js +111 -0
  73. package/dist/util.js.map +1 -0
  74. package/package.json +55 -0
  75. package/public/card.js +45 -0
  76. package/public/cosmic.html +74 -0
  77. package/public/favicon.svg +1 -0
  78. package/public/index.html +294 -0
  79. package/public/report.html +76 -0
package/dist/bin.js ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mneme-xray — run an X-Ray from the terminal.
4
+ * mneme-xray <path|https-git-url> [--json] [--no-sign]
5
+ * mneme-xray <path> --publish [--server URL] [--token KEY]
6
+ *
7
+ * --publish is THE BRIDGE for private repos: analyse locally, then send ONLY
8
+ * the signed, raw-free report to a Lighthouse server (source never leaves).
9
+ * Server/token may also come from env XRAY_SERVER / XRAY_TOKEN.
10
+ */
11
+ import { buildXRay } from "./engine.js";
12
+ import { sealXRay, verifyXRay } from "./sign.js";
13
+ import { xrayLeaksRaw } from "./privacy.js";
14
+ import { publishReport } from "./publish.js";
15
+ import { existsSync } from "node:fs";
16
+ function flagVal(args, name) {
17
+ const i = args.indexOf(name);
18
+ return i >= 0 && args[i + 1] && !args[i + 1].startsWith("--") ? args[i + 1] : undefined;
19
+ }
20
+ async function main() {
21
+ const args = process.argv.slice(2);
22
+ const target = args.find((a) => !a.startsWith("--") && a !== flagVal(args, "--server") && a !== flagVal(args, "--token"));
23
+ const asJson = args.includes("--json");
24
+ const noSign = args.includes("--no-sign");
25
+ const doPublish = args.includes("--publish");
26
+ const server = flagVal(args, "--server") || process.env.XRAY_SERVER || "";
27
+ const token = flagVal(args, "--token") || process.env.XRAY_TOKEN || "";
28
+ if (!target) {
29
+ process.stderr.write("usage: mneme-xray <path|https-git-url> [--json] [--no-sign] [--publish --server URL --token KEY]\n");
30
+ process.exit(2);
31
+ }
32
+ const isUrl = /^https:\/\//.test(target);
33
+ const report = await buildXRay(isUrl ? { gitUrl: target } : { repoPath: target });
34
+ // privacy gate — never emit a report that leaks raw source
35
+ const leak = xrayLeaksRaw(report);
36
+ if (leak.leaks) {
37
+ process.stderr.write(`✗ refusing to emit: report failed raw-free check (${leak.reasons.join("; ")})\n`);
38
+ process.exit(1);
39
+ }
40
+ const repoRootForKey = isUrl ? process.cwd() : (existsSync(target) ? target : process.cwd());
41
+ const signed = (noSign && !doPublish) ? { report, receipt: null } : sealXRay(repoRootForKey, report);
42
+ if (doPublish) {
43
+ if (!server || !token) {
44
+ process.stderr.write("✗ --publish needs --server URL and --token KEY (or env XRAY_SERVER / XRAY_TOKEN)\n");
45
+ process.exit(2);
46
+ }
47
+ const pr = await publishReport(server, token, signed);
48
+ if (!pr.ok) {
49
+ process.stderr.write(`✗ publish failed: ${pr.error}\n`);
50
+ process.exit(1);
51
+ }
52
+ process.stdout.write(`✓ published ${report.subject.repoName} → ${server}\n profile ${pr.profileId} · fingerprint ${pr.fingerprint?.slice(0, 24)}…\n (source never left this machine — only the signed, raw-free report was sent)\n`);
53
+ return;
54
+ }
55
+ if (asJson) {
56
+ process.stdout.write(JSON.stringify(signed, null, 2) + "\n");
57
+ return;
58
+ }
59
+ const r = report;
60
+ const line = "─".repeat(58);
61
+ process.stdout.write(`\n REPO X-RAY · ${r.subject.repoName} @ ${r.subject.commitHash.slice(0, 12)}\n ${line}\n`);
62
+ process.stdout.write(` GRADE ${r.summary.grade} ${r.summary.headline}\n ${line}\n`);
63
+ for (const b of r.summary.bullets)
64
+ process.stdout.write(` ${b}\n`);
65
+ process.stdout.write(` ${line}\n fingerprint ${r.fingerprint.slice(0, 24)}…\n`);
66
+ if (!noSign) {
67
+ const v = verifyXRay(signed);
68
+ process.stdout.write(` signed: ${v.valid ? "✓ Ed25519 receipt verifies offline" : "✗ " + v.reason}\n`);
69
+ }
70
+ process.stdout.write("\n");
71
+ }
72
+ main().catch((e) => {
73
+ process.stderr.write(`✗ x-ray failed: ${e.message}\n`);
74
+ process.exit(1);
75
+ });
76
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export declare function isAllowedPublicUrl(url: string): boolean;
2
+ export interface CloneHandle {
3
+ path: string;
4
+ dispose: () => void;
5
+ }
6
+ /**
7
+ * Blobless clone: FULL commit history (so age / commit-count / bus-factor are
8
+ * ACCURATE) but file blobs are fetched lazily — fast, like a shallow clone,
9
+ * without truncating history. A `--depth N` clone would cap commits at N and
10
+ * report a wrong "first commit" / age for any active repo; blob:none does not.
11
+ */
12
+ export declare function shallowClone(url: string): CloneHandle;
13
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"AAYA,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 ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shallow-clone a PUBLIC git URL to a temp dir for analysis, then delete it.
3
+ * The clone is read-only and removed in a finally block — nothing persists.
4
+ * Only public URLs are accepted (no credentials in the URL).
5
+ */
6
+ import { spawnSync } from "node:child_process";
7
+ import { mkdtempSync, rmSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ const ALLOWED = /^https:\/\/(github\.com|gitlab\.com|bitbucket\.org)\/[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+(\.git)?\/?$/;
11
+ export function isAllowedPublicUrl(url) {
12
+ if (/@|:\/\/[^/]*:[^/]*@/.test(url.replace("https://", "")))
13
+ return false; // reject embedded creds
14
+ return ALLOWED.test(url.trim());
15
+ }
16
+ /**
17
+ * Blobless clone: FULL commit history (so age / commit-count / bus-factor are
18
+ * ACCURATE) but file blobs are fetched lazily — fast, like a shallow clone,
19
+ * without truncating history. A `--depth N` clone would cap commits at N and
20
+ * report a wrong "first commit" / age for any active repo; blob:none does not.
21
+ */
22
+ export function shallowClone(url) {
23
+ if (!isAllowedPublicUrl(url)) {
24
+ throw new Error("Only public github.com / gitlab.com / bitbucket.org URLs (no credentials) are accepted.");
25
+ }
26
+ const dir = mkdtempSync(join(tmpdir(), "mneme-xray-"));
27
+ const r = spawnSync("git", ["clone", "--filter=blob:none", "--no-tags", "--single-branch", url.trim(), dir], {
28
+ encoding: "utf8",
29
+ timeout: 180_000,
30
+ maxBuffer: 64 * 1024 * 1024,
31
+ });
32
+ const dispose = () => { try {
33
+ rmSync(dir, { recursive: true, force: true });
34
+ }
35
+ catch { /* best-effort */ } };
36
+ if (r.status !== 0) {
37
+ dispose();
38
+ throw new Error(`git clone failed: ${(r.stderr || "unknown error").slice(0, 200)}`);
39
+ }
40
+ return { path: dir, dispose };
41
+ }
42
+ //# sourceMappingURL=clone.js.map
@@ -0,0 +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,mGAAmG,CAAC;AAEpH,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"}
@@ -0,0 +1,35 @@
1
+ export interface Sample {
2
+ ts: number;
3
+ ok: boolean;
4
+ latencyMs: number;
5
+ }
6
+ export interface CosmicStatus {
7
+ url: string;
8
+ up: boolean;
9
+ lastCheck: number | null;
10
+ uptimePct: number;
11
+ checks: number;
12
+ p50Ms: number;
13
+ p95Ms: number;
14
+ sinceTs: number | null;
15
+ windowMs: number;
16
+ }
17
+ /** Pure: compute status from a sample set (so the math is unit-testable). */
18
+ export declare function computeStatus(samples: Sample[], url: string, now: number, windowMs: number): CosmicStatus;
19
+ export declare class CosmicMonitor {
20
+ private readonly url;
21
+ private readonly file;
22
+ private samples;
23
+ private timer;
24
+ private readonly cap;
25
+ constructor(url: string, file?: string | null);
26
+ probe(): Promise<Sample>;
27
+ start(intervalMs?: number): void;
28
+ stop(): void;
29
+ status(windowMs?: number): CosmicStatus;
30
+ }
31
+ export declare function cosmicBadgeSvg(st: CosmicStatus): string;
32
+ /** A signed, offline-verifiable liveness attestation. Anyone can verify with the
33
+ * embedded public key that Mneme observed cosmic with this uptime. */
34
+ export declare function signCosmicStatus(repoRoot: string, st: CosmicStatus): unknown;
35
+ //# sourceMappingURL=cosmic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cosmic.d.ts","sourceRoot":"","sources":["../src/cosmic.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,MAAM;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAEtE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAkBzG;AAED,qBAAa,aAAa;IAKZ,OAAO,CAAC,QAAQ,CAAC,GAAG;IAAU,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJ/D,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;gBAEC,GAAG,EAAE,MAAM,EAAmB,IAAI,GAAE,MAAM,GAAG,IAAW;IAQ/E,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IA0B9B,KAAK,CAAC,UAAU,SAAQ,GAAG,IAAI;IAM/B,IAAI,IAAI,IAAI;IAEZ,MAAM,CAAC,QAAQ,SAAsB,GAAG,YAAY;CAGrD;AAaD,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,CAKvD;AAED;uEACuE;AACvE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,GAAG,OAAO,CAO5E"}
package/dist/cosmic.js ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * COSMIC MONITOR — an ADDITIVE superpower for the existing cosmic-link server.
3
+ *
4
+ * The X-Ray server (which we control, isolated on its own port) periodically
5
+ * pings the cosmic server and turns its liveness into something cosmic never
6
+ * had: a MEASURED, SIGNED, offline-verifiable uptime record + a public badge +
7
+ * a status page. We never touch the cosmic process — only observe it over
8
+ * localhost. This is the "make cosmic cooler without breaking it" move:
9
+ * capability added from the outside, zero risk to the running service.
10
+ */
11
+ import { notary } from "@mneme-ai/core";
12
+ import { existsSync, appendFileSync, readFileSync, mkdirSync } from "node:fs";
13
+ import { dirname } from "node:path";
14
+ /** Pure: compute status from a sample set (so the math is unit-testable). */
15
+ export function computeStatus(samples, url, now, windowMs) {
16
+ const win = samples.filter((s) => now - s.ts <= windowMs);
17
+ const checks = win.length;
18
+ const okN = win.filter((s) => s.ok).length;
19
+ const lats = win.filter((s) => s.ok).map((s) => s.latencyMs).sort((a, b) => a - b);
20
+ const q = (p) => (lats.length ? lats[Math.min(lats.length - 1, Math.floor(p * lats.length))] : 0);
21
+ const last = samples.length ? samples[samples.length - 1] : null;
22
+ return {
23
+ url,
24
+ up: last ? last.ok : false,
25
+ lastCheck: last ? last.ts : null,
26
+ uptimePct: checks ? Math.round((okN / checks) * 10000) / 100 : 0,
27
+ checks,
28
+ p50Ms: q(0.5),
29
+ p95Ms: q(0.95),
30
+ sinceTs: win.length ? win[0].ts : null,
31
+ windowMs,
32
+ };
33
+ }
34
+ export class CosmicMonitor {
35
+ url;
36
+ file;
37
+ samples = [];
38
+ timer = null;
39
+ cap = 5760; // ~24h at 15s
40
+ constructor(url, file = null) {
41
+ this.url = url;
42
+ this.file = file;
43
+ if (file && existsSync(file)) {
44
+ try {
45
+ this.samples = readFileSync(file, "utf8").trim().split("\n").filter(Boolean).slice(-this.cap).map((l) => JSON.parse(l));
46
+ }
47
+ catch { /* ignore corrupt history */ }
48
+ }
49
+ }
50
+ async probe() {
51
+ const t0 = Date.now();
52
+ let ok = false, latencyMs = 0;
53
+ try {
54
+ const ctrl = new AbortController();
55
+ const to = setTimeout(() => ctrl.abort(), 8000);
56
+ const res = await fetch(this.url, { signal: ctrl.signal });
57
+ clearTimeout(to);
58
+ ok = res.status >= 200 && res.status < 500; // reachable + not a server error
59
+ latencyMs = Date.now() - t0;
60
+ }
61
+ catch {
62
+ latencyMs = Date.now() - t0;
63
+ ok = false;
64
+ }
65
+ const s = { ts: Date.now(), ok, latencyMs };
66
+ this.samples.push(s);
67
+ if (this.samples.length > this.cap)
68
+ this.samples.shift();
69
+ if (this.file) {
70
+ try {
71
+ if (!existsSync(dirname(this.file)))
72
+ mkdirSync(dirname(this.file), { recursive: true });
73
+ appendFileSync(this.file, JSON.stringify(s) + "\n");
74
+ }
75
+ catch { /* best-effort persistence */ }
76
+ }
77
+ return s;
78
+ }
79
+ start(intervalMs = 15000) {
80
+ if (this.timer)
81
+ return;
82
+ void this.probe();
83
+ this.timer = setInterval(() => { void this.probe(); }, intervalMs);
84
+ if (typeof this.timer.unref === "function")
85
+ this.timer.unref();
86
+ }
87
+ stop() { if (this.timer) {
88
+ clearInterval(this.timer);
89
+ this.timer = null;
90
+ } }
91
+ status(windowMs = 24 * 60 * 60 * 1000) {
92
+ return computeStatus(this.samples, this.url, Date.now(), windowMs);
93
+ }
94
+ }
95
+ const C_LABEL = "cosmic link";
96
+ function flatBadge(label, value, color) {
97
+ const lw = 7 + label.length * 6.2, vw = 12 + value.length * 6.2, w = Math.round(lw + vw), h = 20;
98
+ const lm = Math.round(lw / 2), vm = Math.round(lw + vw / 2);
99
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" role="img" aria-label="${label}: ${value}">
100
+ <clipPath id="r"><rect width="${w}" height="${h}" rx="3" fill="#fff"/></clipPath>
101
+ <g clip-path="url(#r)"><rect width="${Math.round(lw)}" height="${h}" fill="#0a0a0a"/><rect x="${Math.round(lw)}" width="${Math.round(vw)}" height="${h}" fill="${color}"/></g>
102
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11">
103
+ <text x="${lm}" y="14">${label}</text><text x="${vm}" y="14" font-weight="bold">${value}</text></g></svg>`;
104
+ }
105
+ export function cosmicBadgeSvg(st) {
106
+ if (!st.up)
107
+ return flatBadge(C_LABEL, "down", "#dc2626");
108
+ const val = st.checks >= 5 ? `up ${st.uptimePct}%` : "up";
109
+ const color = st.uptimePct >= 99 || st.checks < 5 ? "#16a34a" : st.uptimePct >= 95 ? "#65a30d" : "#d97706";
110
+ return flatBadge(C_LABEL, val, color);
111
+ }
112
+ /** A signed, offline-verifiable liveness attestation. Anyone can verify with the
113
+ * embedded public key that Mneme observed cosmic with this uptime. */
114
+ export function signCosmicStatus(repoRoot, st) {
115
+ return notary.issueReceipt(repoRoot, {
116
+ kind: "claim-verdict",
117
+ subject: `cosmic-liveness:${st.url}`,
118
+ payload: st,
119
+ includePayload: true,
120
+ });
121
+ }
122
+ //# sourceMappingURL=cosmic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cosmic.js","sourceRoot":"","sources":["../src/cosmic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { XRayReport, XRayInput } from "./types.js";
2
+ import { type MetaFetcher } from "./battery/deps.js";
3
+ export interface BuildOptions extends XRayInput {
4
+ /** Injectable npm-metadata fetcher (tests). */
5
+ depFetcher?: MetaFetcher;
6
+ }
7
+ export declare function buildXRay(opts: BuildOptions): Promise<XRayReport>;
8
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +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;AASlE,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,CAiDvE"}
package/dist/engine.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * buildXRay — the orchestrator. Runs the deterministic battery against a local
3
+ * repo path or a shallow-cloned public git URL, composes a graded summary, and
4
+ * returns a raw-free report. Each signal is fail-safe: if one analyzer cannot
5
+ * run, the report still returns with that block degraded and `signalsRun` lowered.
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ import { existsSync } from "node:fs";
9
+ import { analyzeDeps } from "./battery/deps.js";
10
+ import { scanSecrets } from "./battery/secrets.js";
11
+ import { analyzeBusFactor } from "./battery/busfactor.js";
12
+ import { analyzeAge } from "./battery/age.js";
13
+ import { analyzeComplexity } from "./battery/complexity.js";
14
+ import { analyzeHotspots } from "./battery/hotspots.js";
15
+ import { shallowClone } from "./clone.js";
16
+ import { headCommit, repoNameFromUrl, repoNameFromPath } from "./util.js";
17
+ export async function buildXRay(opts) {
18
+ const now = opts.now ?? Date.now();
19
+ const maxFiles = opts.maxFiles ?? 4000;
20
+ let repoPath;
21
+ let dispose = null;
22
+ let subject;
23
+ if (opts.gitUrl) {
24
+ const h = shallowClone(opts.gitUrl);
25
+ repoPath = h.path;
26
+ dispose = h.dispose;
27
+ subject = { kind: "git-url", ref: opts.gitUrl.trim(), repoName: repoNameFromUrl(opts.gitUrl), commitHash: "" };
28
+ }
29
+ else if (opts.repoPath) {
30
+ if (!existsSync(opts.repoPath))
31
+ throw new Error(`repoPath does not exist: ${opts.repoPath}`);
32
+ repoPath = opts.repoPath;
33
+ subject = { kind: "local-path", ref: "local", repoName: repoNameFromPath(opts.repoPath), commitHash: "" };
34
+ }
35
+ else {
36
+ throw new Error("buildXRay requires either gitUrl or repoPath.");
37
+ }
38
+ try {
39
+ subject.commitHash = headCommit(repoPath);
40
+ const [deps] = await Promise.all([analyzeDeps(repoPath, now, opts.depFetcher)]);
41
+ const secrets = scanSecrets(repoPath, maxFiles);
42
+ const busFactor = analyzeBusFactor(repoPath);
43
+ const age = analyzeAge(repoPath, now);
44
+ const complexity = analyzeComplexity(repoPath, maxFiles);
45
+ const hotspots = analyzeHotspots(repoPath, now);
46
+ const summary = grade({ deps, secrets, busFactor, age, complexity, hotspots });
47
+ const blocks = { deps, secrets, busFactor, age, complexity, hotspots };
48
+ const fingerprint = createHash("sha256")
49
+ .update(JSON.stringify({ subject: { repoName: subject.repoName, commitHash: subject.commitHash }, blocks }))
50
+ .digest("hex");
51
+ return {
52
+ v: 1,
53
+ subject,
54
+ generatedAt: new Date(now).toISOString(),
55
+ summary,
56
+ ...blocks,
57
+ fingerprint,
58
+ };
59
+ }
60
+ finally {
61
+ if (dispose)
62
+ dispose();
63
+ }
64
+ }
65
+ /** Deterministic 0-100 health score → letter grade, with one-line bullets. */
66
+ function grade(b) {
67
+ let score = 100;
68
+ const bullets = [];
69
+ let signalsRun = 0;
70
+ // secrets — a high-confidence BLOCK (strong leak signal) weighs heavily; mere
71
+ // pattern matches are a "review" flag with a small, capped penalty, because
72
+ // regex matching has a real false-positive rate (broad api-key patterns, a
73
+ // security repo's own sample data). We never auto-fail a repo on pattern noise.
74
+ if (b.secrets.filesScanned > 0) {
75
+ signalsRun++;
76
+ if (b.secrets.worstVerdict === "BLOCK")
77
+ score -= 35;
78
+ else if (b.secrets.totalFindings > 0)
79
+ score -= Math.min(8, b.secrets.totalFindings);
80
+ const exTail = b.secrets.excludedTestHits > 0 ? ` (+${b.secrets.excludedTestHits} in tests/docs, excluded)` : "";
81
+ bullets.push(b.secrets.totalFindings === 0
82
+ ? `🔑 No credential patterns in production code${exTail}.`
83
+ : `🔑 ${b.secrets.totalFindings} credential-pattern match(es) in production code — review${exTail}.`);
84
+ }
85
+ // deps
86
+ if (b.deps.total > 0) {
87
+ signalsRun++;
88
+ const dying = b.deps.byBand.moribund + b.deps.byBand.dead;
89
+ score -= Math.min(20, dying * 5);
90
+ bullets.push(dying === 0
91
+ ? `📦 ${b.deps.total} deps, none dying.`
92
+ : `💀 ${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})` : ""}.`);
93
+ }
94
+ // bus factor
95
+ if (b.busFactor.authors > 0) {
96
+ signalsRun++;
97
+ if (b.busFactor.busFactor <= 1)
98
+ score -= 15;
99
+ if (b.busFactor.singleOwnerFilePct >= 50)
100
+ score -= 10;
101
+ bullets.push(b.busFactor.busFactor <= 1
102
+ ? `🚌 Bus factor 1 — one person holds ${b.busFactor.topContributorShare}% of commits.`
103
+ : `🚌 Bus factor ${b.busFactor.busFactor}; ${b.busFactor.singleOwnerFilePct}% of files single-owner.`);
104
+ }
105
+ // age / vitality
106
+ if (b.age.totalCommits > 0) {
107
+ signalsRun++;
108
+ if (b.age.vitality === "archived")
109
+ score -= 20;
110
+ else if (b.age.vitality === "dormant")
111
+ score -= 15;
112
+ else if (b.age.vitality === "slowing")
113
+ score -= 5;
114
+ bullets.push(`🕰️ ${b.age.vitality} · ${b.age.lifespan} old · ${b.age.totalCommits} commits.`);
115
+ }
116
+ // complexity (mild)
117
+ if (b.complexity.filesAnalysed > 0) {
118
+ signalsRun++;
119
+ const huge = b.complexity.hotspots.filter((h) => h.bodyLines >= 150).length;
120
+ score -= Math.min(10, huge * 2);
121
+ bullets.push(b.complexity.hotspots[0]
122
+ ? `🧩 Largest symbol ${b.complexity.hotspots[0].bodyLines} lines (${b.complexity.hotspots[0].file}).`
123
+ : `🧩 ${b.complexity.totalSymbols} symbols analysed.`);
124
+ }
125
+ // hotspots — informational (refactor-ROI guidance, not a risk penalty)
126
+ if (b.hotspots.hotspots.length > 0) {
127
+ signalsRun++;
128
+ const h = b.hotspots.hotspots[0];
129
+ bullets.push(`🔥 Refactor first: ${h.file} — changed ${h.changes}× · ${h.loc} lines (churn × size).`);
130
+ }
131
+ score = Math.max(0, Math.min(100, Math.round(score)));
132
+ const g = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 55 ? "D" : "F";
133
+ const headline = g === "A" ? "Healthy — strong signals across the board."
134
+ : g === "F" ? "Critical — multiple high-risk signals."
135
+ : "Mixed — some signals need attention.";
136
+ return { headline, grade: g, signalsRun, bullets };
137
+ }
138
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface XRayGauntlet {
2
+ cleanReportPasses: boolean;
3
+ catchesInjectedSource: boolean;
4
+ allowsStructuralSymbolName: boolean;
5
+ totalOnGarbage: boolean;
6
+ score: number;
7
+ }
8
+ export declare function xrayGauntlet(): XRayGauntlet;
9
+ //# sourceMappingURL=gauntlet.d.ts.map
@@ -0,0 +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;AAkBD,wBAAgB,YAAY,IAAI,YAAY,CAyB3C"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * xrayGauntlet — a falsifiable, in-memory proof of the X-Ray invariants that
3
+ * do not need a live repo: the privacy moat catches injected source, allows
4
+ * legitimately-structural symbol names, is total on garbage, and a clean
5
+ * report passes. Score is 0 or 100 (every invariant must hold).
6
+ */
7
+ import { xrayLeaksRaw } from "./privacy.js";
8
+ function cleanFixture() {
9
+ return {
10
+ v: 1,
11
+ subject: { kind: "git-url", ref: "https://github.com/acme/widget", repoName: "acme/widget", commitHash: "abc123def456" },
12
+ generatedAt: "2026-01-01T00:00:00.000Z",
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." },
15
+ secrets: { filesScanned: 40, totalFindings: 0, excludedTestHits: 3, byKind: {}, hits: [], worstVerdict: "ALLOW", note: "clean" },
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
+ 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
+ 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." },
20
+ fingerprint: "deadbeef",
21
+ };
22
+ }
23
+ export function xrayGauntlet() {
24
+ // 1. a normal metric-only report must pass (the structural signature in
25
+ // complexity.hotspots[].symbol must NOT trip the scanner).
26
+ const clean = cleanFixture();
27
+ const cleanReportPasses = xrayLeaksRaw(clean).leaks === false;
28
+ const allowsStructuralSymbolName = cleanReportPasses; // same assertion, named for clarity
29
+ // 2. inject a real source body into a non-structural field → must be caught.
30
+ const poisoned = cleanFixture();
31
+ poisoned.age.note = "function leak(){ const secret = 42; return secret }";
32
+ const catchesInjectedSource = xrayLeaksRaw(poisoned).leaks === true;
33
+ // 3. total on garbage (never throws; fail-closed to "leaks").
34
+ let totalOnGarbage = true;
35
+ try {
36
+ const a = xrayLeaksRaw(null);
37
+ const b = xrayLeaksRaw({ circular: undefined });
38
+ const c = xrayLeaksRaw(undefined);
39
+ totalOnGarbage = a.leaks === true && c.leaks === true && typeof b.leaks === "boolean";
40
+ }
41
+ catch {
42
+ totalOnGarbage = false;
43
+ }
44
+ const all = cleanReportPasses && catchesInjectedSource && allowsStructuralSymbolName && totalOnGarbage;
45
+ return { cleanReportPasses, catchesInjectedSource, allowsStructuralSymbolName, totalOnGarbage, score: all ? 100 : 0 };
46
+ }
47
+ //# sourceMappingURL=gauntlet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gauntlet.js","sourceRoot":"","sources":["../src/gauntlet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @mneme-ai/xray — a signed, raw-free, deterministic X-Ray of any repo.
3
+ *
4
+ * Every number originates from a deterministic @mneme-ai/core analyzer
5
+ * (git history · AST outline · npm registry metadata · regex/entropy secret
6
+ * scan). No LLM is consulted, so the same repo at the same commit always
7
+ * produces the same report. The report is raw-free by construction
8
+ * (xrayLeaksRaw proves it) and sealed with an offline-verifiable Ed25519
9
+ * NOTARY receipt, so a third party trusts the ANALYSIS, not the sender.
10
+ */
11
+ export * from "./types.js";
12
+ export { buildXRay, type BuildOptions } from "./engine.js";
13
+ export { sealXRay, verifyXRay } from "./sign.js";
14
+ export { xrayLeaksRaw, type LeakVerdict } from "./privacy.js";
15
+ export { xrayGauntlet, type XRayGauntlet } from "./gauntlet.js";
16
+ export { isAllowedPublicUrl } from "./clone.js";
17
+ export { defaultFetcher, type MetaFetcher } from "./battery/deps.js";
18
+ export { publishReport, type PublishResult } from "./publish.js";
19
+ export { createXRayServer } from "./server.js";
20
+ export { CosmicMonitor, computeStatus, cosmicBadgeSvg, type CosmicStatus, type Sample } from "./cosmic.js";
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @mneme-ai/xray — a signed, raw-free, deterministic X-Ray of any repo.
3
+ *
4
+ * Every number originates from a deterministic @mneme-ai/core analyzer
5
+ * (git history · AST outline · npm registry metadata · regex/entropy secret
6
+ * scan). No LLM is consulted, so the same repo at the same commit always
7
+ * produces the same report. The report is raw-free by construction
8
+ * (xrayLeaksRaw proves it) and sealed with an offline-verifiable Ed25519
9
+ * NOTARY receipt, so a third party trusts the ANALYSIS, not the sender.
10
+ */
11
+ export * from "./types.js";
12
+ export { buildXRay } from "./engine.js";
13
+ export { sealXRay, verifyXRay } from "./sign.js";
14
+ export { xrayLeaksRaw } from "./privacy.js";
15
+ export { xrayGauntlet } from "./gauntlet.js";
16
+ export { isAllowedPublicUrl } from "./clone.js";
17
+ export { defaultFetcher } from "./battery/deps.js";
18
+ export { publishReport } from "./publish.js";
19
+ export { createXRayServer } from "./server.js";
20
+ export { CosmicMonitor, computeStatus, cosmicBadgeSvg } from "./cosmic.js";
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,12 @@
1
+ export interface LeakVerdict {
2
+ leaks: boolean;
3
+ reasons: string[];
4
+ }
5
+ /**
6
+ * Field-aware: symbol NAMES and signatures are allowed (they are structural),
7
+ * but they live in known fields. We scan the JSON with those known structural
8
+ * fields stripped, so a signature in `hotspots[].symbol` can't be mistaken for
9
+ * a leak, while an injected code body anywhere else is caught.
10
+ */
11
+ export declare function xrayLeaksRaw(report: unknown): LeakVerdict;
12
+ //# sourceMappingURL=privacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAAG,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE;AAYlE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW,CAyBzD"}