litmus-cli 1.4.2 → 1.4.5

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 (44) hide show
  1. package/dist/commands/doctor.d.ts +2 -21
  2. package/dist/commands/doctor.d.ts.map +1 -1
  3. package/dist/commands/doctor.js +97 -61
  4. package/dist/commands/doctor.js.map +1 -1
  5. package/dist/commands/submit.d.ts.map +1 -1
  6. package/dist/commands/submit.js +6 -0
  7. package/dist/commands/submit.js.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/backfill-tools.d.ts +126 -0
  11. package/dist/lib/backfill-tools.d.ts.map +1 -0
  12. package/dist/lib/backfill-tools.js +787 -0
  13. package/dist/lib/backfill-tools.js.map +1 -0
  14. package/dist/lib/backfill.d.ts +20 -2
  15. package/dist/lib/backfill.d.ts.map +1 -1
  16. package/dist/lib/backfill.js +14 -8
  17. package/dist/lib/backfill.js.map +1 -1
  18. package/dist/lib/config.d.ts +20 -0
  19. package/dist/lib/config.d.ts.map +1 -1
  20. package/dist/lib/config.js +33 -0
  21. package/dist/lib/config.js.map +1 -1
  22. package/dist/lib/detection.d.ts +32 -0
  23. package/dist/lib/detection.d.ts.map +1 -0
  24. package/dist/lib/detection.js +80 -0
  25. package/dist/lib/detection.js.map +1 -0
  26. package/dist/lib/hook-logger.cjs +50 -2
  27. package/dist/lib/platform.d.ts +34 -10
  28. package/dist/lib/platform.d.ts.map +1 -1
  29. package/dist/lib/platform.js +56 -16
  30. package/dist/lib/platform.js.map +1 -1
  31. package/dist/lib/shadow.d.ts +79 -0
  32. package/dist/lib/shadow.d.ts.map +1 -0
  33. package/dist/lib/shadow.js +268 -0
  34. package/dist/lib/shadow.js.map +1 -0
  35. package/dist/lib/tracker.d.ts +1 -1
  36. package/dist/lib/tracker.d.ts.map +1 -1
  37. package/dist/lib/tracker.js +42 -4
  38. package/dist/lib/tracker.js.map +1 -1
  39. package/dist/lib/watcher.js +273 -25
  40. package/dist/lib/watcher.js.map +1 -1
  41. package/dist/lib/zip.d.ts.map +1 -1
  42. package/dist/lib/zip.js +24 -1
  43. package/dist/lib/zip.js.map +1 -1
  44. package/package.json +1 -1
@@ -23,16 +23,18 @@ export function getProcessList() {
23
23
  * Calls cb(err, stdout) with raw connection output.
24
24
  * Handles platform fallbacks internally (lsof → ss on Linux).
25
25
  *
26
- * - macOS: `lsof -i -n -P`
27
- * - Linux: `lsof -i -n -P`, falls back to `ss -tnp` if lsof is unavailable
28
- * - Windows: `netstat -ano`
26
+ * - macOS: `lsof -i -n -P +c0` (+c0 = untruncated COMMAND, so the owning
27
+ * process is attributable default lsof cuts it at 9 chars, which mangles
28
+ * names like `com.apple.WebKit.Networking`)
29
+ * - Linux: same lsof, falls back to `ss -tnp` if lsof is unavailable
30
+ * - Windows: `netstat -ano` (PID only; resolve names via resolvePidNames)
29
31
  */
30
32
  export function getConnections(cb) {
31
33
  if (isWin) {
32
34
  execFile("netstat", ["-ano"], { timeout: 10000, encoding: "utf8", maxBuffer: 2 * 1024 * 1024 }, cb);
33
35
  return;
34
36
  }
35
- execFile("lsof", ["-i", "-n", "-P"], { timeout: 10000, encoding: "utf8", maxBuffer: 2 * 1024 * 1024 }, (err, stdout) => {
37
+ execFile("lsof", ["-i", "-n", "-P", "+c0"], { timeout: 10000, encoding: "utf8", maxBuffer: 2 * 1024 * 1024 }, (err, stdout) => {
36
38
  if (err && err.code === "ENOENT" && isLinux) {
37
39
  // lsof unavailable on Linux — fall back to ss
38
40
  execFile("ss", ["-tnp"], { timeout: 5000, encoding: "utf8", maxBuffer: 1024 * 1024 }, cb);
@@ -43,27 +45,65 @@ export function getConnections(cb) {
43
45
  });
44
46
  }
45
47
  /**
46
- * Extracts the destination IP address from a single connection output line.
47
- * Returns a regex match array [fullMatch, ip, port] or null.
48
+ * Parses a single connection output line into destination IP/port plus the
49
+ * owning process, or null if the line isn't a connection row.
48
50
  *
49
51
  * Format varies by tool:
50
- * lsof: "... ->104.18.7.42:443 ..."
51
- * ss: "... 104.18.7.42:443" (IP:port at end of line)
52
- * netstat: " TCP local:port foreign:port STATE PID" (3rd column)
52
+ * lsof: "COMMAND PID USER ... ->104.18.7.42:443 (ESTABLISHED)" (owner = col 1)
53
+ * ss -tnp: "... dst:port users:((\"chrome\",pid=123,fd=9))" (owner in users:())
54
+ * netstat: " TCP local:port foreign:port STATE PID" (PID only, last col)
53
55
  *
54
- * On Unix, trying both lsof and ss patterns is safe — they don't cross-match.
56
+ * On Unix, trying both lsof and ss patterns is safe — they don't cross-match:
57
+ * lsof peers carry "->", and an ss peer is followed only by the optional
58
+ * users:() column.
55
59
  */
56
- export function extractConnectionIP(line) {
60
+ export function extractConnection(line) {
57
61
  if (isWin) {
58
62
  const parts = line.trim().split(/\s+/);
59
63
  if (parts.length >= 4 && (parts[0] === "TCP" || parts[0] === "UDP")) {
60
- return parts[2].match(/^(\d+\.\d+\.\d+\.\d+):(\d+)$/);
64
+ const m = parts[2].match(/^(\d+\.\d+\.\d+\.\d+):(\d+)$/);
65
+ if (!m)
66
+ return null;
67
+ return { ip: m[1], port: m[2], owner: "", pid: parts[parts.length - 1] };
61
68
  }
62
69
  return null;
63
70
  }
64
- // lsof "->IP:port" (won't match ss output — no "->" in ss)
65
- // ss "IP:port" at EOL (won't match lsof — lines end with "(STATE)")
66
- return line.match(/->(\d+\.\d+\.\d+\.\d+):(\d+)/) ||
67
- line.match(/\s(\d+\.\d+\.\d+\.\d+):(\d+)\s*$/);
71
+ const lsof = line.match(/->(\d+\.\d+\.\d+\.\d+):(\d+)/);
72
+ if (lsof) {
73
+ return { ip: lsof[1], port: lsof[2], owner: line.split(/\s+/, 1)[0] ?? "" };
74
+ }
75
+ // ss: the peer address is followed by the users:() column when ss can see
76
+ // the owner (own processes, or root), and ends the line when it can't.
77
+ const ss = line.match(/\s(\d+\.\d+\.\d+\.\d+):(\d+)(?:\s+users:\(\("([^"]+)".*)?$/);
78
+ if (ss) {
79
+ return { ip: ss[1], port: ss[2], owner: ss[3] ?? "" };
80
+ }
81
+ return null;
82
+ }
83
+ /**
84
+ * Resolves PIDs to process image names where connection output only carries
85
+ * PIDs (Windows netstat). Calls cb with a PID → name map, or null on
86
+ * platforms where connection lines already name their owner (Unix) or when
87
+ * tasklist fails. Never throws.
88
+ */
89
+ export function resolvePidNames(cb) {
90
+ if (!isWin) {
91
+ cb(null);
92
+ return;
93
+ }
94
+ execFile("tasklist", ["/fo", "csv", "/nh"], { timeout: 10000, encoding: "utf8", maxBuffer: 2 * 1024 * 1024 }, (err, stdout) => {
95
+ if (err || !stdout) {
96
+ cb(null);
97
+ return;
98
+ }
99
+ const names = new Map();
100
+ for (const line of stdout.split("\n")) {
101
+ // CSV row: "Image Name","PID","Session Name","Session#","Mem Usage"
102
+ const m = line.match(/^"([^"]+)","(\d+)"/);
103
+ if (m)
104
+ names.set(m[2], m[1]);
105
+ }
106
+ cb(names);
107
+ });
68
108
  }
69
109
  //# sourceMappingURL=platform.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAElD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAA;AAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAA;AAE5C;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAA;IACzC,OAAO,QAAQ,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;AACzE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,EAAgD;IAC7E,IAAI,KAAK,EAAE,CAAC;QACV,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,KAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QACpG,OAAM;IACR,CAAC;IACD,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,KAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;QACtH,IAAI,GAAG,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,EAAE,CAAC;YACvE,8CAA8C;YAC9C,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QAC3F,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QACjB,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACpE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAA;QACvD,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,2DAA2D;IAC3D,oEAAoE;IACpE,OAAO,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAA;AACvD,CAAC"}
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import type { LitmusConfig } from "./config.js";
2
+ /**
3
+ * Shadow store for tracking-critical state (ENG-1453).
4
+ *
5
+ * Everything AI-capture depends on used to live only inside the assessment
6
+ * repo, gitignored — which put it squarely inside `git clean -fdX`'s blast
7
+ * radius (the ENG-1446 incident: one ordinary cleanup command deleted the
8
+ * config, the Copilot hook, and the forensic log at once). This module keeps
9
+ * a copy of the small, load-bearing pieces OUTSIDE the repo, next to the
10
+ * registry that already lives there:
11
+ *
12
+ * ~/.litmus/assessments/<assessmentId>/
13
+ * config.json verbatim mirror of .litmus/config.json + { projectDir, mirroredAt }
14
+ * chain.json last uploaded chain position { seq, hash }
15
+ * tracker.pid mirror — lets doctor find a live watcher after a repo wipe
16
+ * tracker.nonce mirror — same
17
+ * ~/.litmus/logs/<assessmentId>.log tracker log (relocated, not mirrored)
18
+ *
19
+ * Rules (from the ENG-1453 security review):
20
+ * - The in-repo config is ALWAYS authoritative when present; shadows are read
21
+ * only when it is missing.
22
+ * - Every write here is best-effort: a failing shadow write must never break
23
+ * init, doctor, or the watcher.
24
+ * - Shadows die with the assessment: deleted on submit/terminal outcomes and
25
+ * pruned when their project dir is gone and unregistered.
26
+ */
27
+ export interface ShadowConfig extends LitmusConfig {
28
+ /** Absolute assessment root the mirror was written for — the lookup key
29
+ * when the in-repo config (and its directory) no longer exists. */
30
+ projectDir: string;
31
+ mirroredAt: string;
32
+ }
33
+ export interface ChainCursor {
34
+ seq: number;
35
+ hash: string;
36
+ updatedAt: string;
37
+ }
38
+ export declare function shadowRootDir(): string;
39
+ export declare function shadowDirFor(assessmentId: string): string | null;
40
+ export declare function shadowLogPath(assessmentId: string): string | null;
41
+ /** Mirror a just-written config. Best-effort; returns whether it stuck. */
42
+ export declare function writeShadowConfig(projectDir: string, config: LitmusConfig): boolean;
43
+ export declare function readShadowConfigById(assessmentId: string): ShadowConfig | null;
44
+ /** Find the shadow whose mirror was written for this project dir. */
45
+ export declare function findShadowConfigForDir(projectDir: string): ShadowConfig | null;
46
+ /** Record the last chain position the SERVER has (written after each
47
+ * successful upload), so a replacement watcher whose activity.jsonl is gone
48
+ * continues the chain instead of forking a second genesis. */
49
+ export declare function writeShadowChainCursor(assessmentId: string, seq: number, hash: string): void;
50
+ export declare function readShadowChainCursor(assessmentId: string): ChainCursor | null;
51
+ export declare function writeShadowTrackerFile(assessmentId: string, name: "tracker.pid" | "tracker.nonce", content: string): void;
52
+ export declare function readShadowTrackerFile(assessmentId: string, name: "tracker.pid" | "tracker.nonce"): string | null;
53
+ export declare function shadowTrackerFileMtime(assessmentId: string, name: "tracker.pid" | "tracker.nonce"): number | null;
54
+ export declare function deleteShadowForAssessment(assessmentId: string): void;
55
+ /** Delete the shadow keyed to a project dir — works when the in-repo config
56
+ * (which would have named the assessment) is already gone. */
57
+ export declare function deleteShadowForProject(projectDir: string): void;
58
+ /**
59
+ * Remove shadows whose assessment is demonstrably over on this machine:
60
+ * project dir no longer exists AND no registry entry points at it. A live
61
+ * assessment whose folder was merely wiped keeps its registry entry, so it
62
+ * survives pruning until submit/expiry releases it.
63
+ */
64
+ export declare function pruneShadows(registryPaths: string[]): number;
65
+ export declare const REPAIR_FILE_NAME = "LITMUS-REPAIR.md";
66
+ /** Written by the watcher only when self-heal could not act (server
67
+ * unreachable, no usable shadow, AND the in-memory restore failed to write).
68
+ * Rewritten every heartbeat while the outage lasts — a cleanup pass that
69
+ * deletes it gets it back — and removed the moment the config is restored.
70
+ *
71
+ * Deliberately carries NO credentials: a project-root file can be committed,
72
+ * pushed, read into an AI agent's context, or shipped in the submission zip,
73
+ * so the token must never appear here. The candidate's assessment page
74
+ * ("Something not working?") shows the full copy-paste repair command, and
75
+ * `litmus doctor` run bare prompts for the token pointing at that panel. */
76
+ export declare function repairFileContent(): string;
77
+ export declare function writeRepairFile(projectDir: string): void;
78
+ export declare function removeRepairFile(projectDir: string): void;
79
+ //# sourceMappingURL=shadow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../src/lib/shadow.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;wEACoE;IACpE,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CAClB;AAMD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAOD,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AA6BD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAcnF;AAiBD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAQ9E;AAED,qEAAqE;AACrE,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAO9E;AAcD;;+DAE+D;AAC/D,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAS5F;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAU9E;AAID,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAOzH;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,GAAG,IAAI,CAQhH;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,GAAG,IAAI,CAQjH;AAID,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAQpE;AAED;+DAC+D;AAC/D,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAG/D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAe5D;AAID,eAAO,MAAM,gBAAgB,qBAAqB,CAAA;AAElD;;;;;;;;;6EAS6E;AAC7E,wBAAgB,iBAAiB,IAAI,MAAM,CAoB1C;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAIxD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEzD"}
@@ -0,0 +1,268 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ function litmusHome() {
5
+ return path.join(os.homedir(), ".litmus");
6
+ }
7
+ export function shadowRootDir() {
8
+ return path.join(litmusHome(), "assessments");
9
+ }
10
+ /** Assessment ids are server-issued UUIDs; refuse anything path-like. */
11
+ function safeId(assessmentId) {
12
+ return /^[A-Za-z0-9-]{1,64}$/.test(assessmentId) ? assessmentId : null;
13
+ }
14
+ export function shadowDirFor(assessmentId) {
15
+ const id = safeId(assessmentId);
16
+ return id ? path.join(shadowRootDir(), id) : null;
17
+ }
18
+ export function shadowLogPath(assessmentId) {
19
+ const id = safeId(assessmentId);
20
+ return id ? path.join(litmusHome(), "logs", `${id}.log`) : null;
21
+ }
22
+ /** Resolve through whatever ancestors still exist so a deleted project dir
23
+ * still normalizes the same way it did when the mirror was written
24
+ * (macOS /tmp→/private/tmp and symlinked homes). */
25
+ function normalizeDir(p) {
26
+ let base = path.resolve(p);
27
+ const tail = [];
28
+ for (;;) {
29
+ try {
30
+ const real = fs.realpathSync(base);
31
+ return tail.length ? path.join(real, ...tail.reverse()) : real;
32
+ }
33
+ catch { /* keep walking up */ }
34
+ const parent = path.dirname(base);
35
+ if (parent === base)
36
+ return path.resolve(p);
37
+ tail.push(path.basename(base));
38
+ base = parent;
39
+ }
40
+ }
41
+ function atomicWrite(filePath, content) {
42
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
43
+ const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
44
+ fs.writeFileSync(tmp, content, "utf8");
45
+ fs.renameSync(tmp, filePath);
46
+ }
47
+ // ── Config mirror ─────────────────────────────────────────────────
48
+ /** Mirror a just-written config. Best-effort; returns whether it stuck. */
49
+ export function writeShadowConfig(projectDir, config) {
50
+ const dir = shadowDirFor(config.assessmentId);
51
+ if (!dir)
52
+ return false;
53
+ const shadow = {
54
+ ...config,
55
+ projectDir: normalizeDir(projectDir),
56
+ mirroredAt: new Date().toISOString(),
57
+ };
58
+ try {
59
+ atomicWrite(path.join(dir, "config.json"), JSON.stringify(shadow, null, 2));
60
+ return true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ function parseShadowConfig(raw) {
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(raw);
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ const s = parsed;
75
+ // Field validation: a shadow missing any of these cannot restore anything.
76
+ for (const key of ["assessmentId", "token", "startedAt", "apiBase", "projectDir"]) {
77
+ if (typeof s[key] !== "string" || !s[key])
78
+ return null;
79
+ }
80
+ return s;
81
+ }
82
+ export function readShadowConfigById(assessmentId) {
83
+ const dir = shadowDirFor(assessmentId);
84
+ if (!dir)
85
+ return null;
86
+ try {
87
+ return parseShadowConfig(fs.readFileSync(path.join(dir, "config.json"), "utf8"));
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ /** Find the shadow whose mirror was written for this project dir. */
94
+ export function findShadowConfigForDir(projectDir) {
95
+ const target = normalizeDir(projectDir);
96
+ for (const entry of listShadowIds()) {
97
+ const shadow = readShadowConfigById(entry);
98
+ if (shadow && normalizeDir(shadow.projectDir) === target)
99
+ return shadow;
100
+ }
101
+ return null;
102
+ }
103
+ function listShadowIds() {
104
+ try {
105
+ return fs.readdirSync(shadowRootDir(), { withFileTypes: true })
106
+ .filter((d) => d.isDirectory())
107
+ .map((d) => d.name);
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ }
113
+ // ── Chain cursor ──────────────────────────────────────────────────
114
+ /** Record the last chain position the SERVER has (written after each
115
+ * successful upload), so a replacement watcher whose activity.jsonl is gone
116
+ * continues the chain instead of forking a second genesis. */
117
+ export function writeShadowChainCursor(assessmentId, seq, hash) {
118
+ const dir = shadowDirFor(assessmentId);
119
+ if (!dir)
120
+ return;
121
+ try {
122
+ atomicWrite(path.join(dir, "chain.json"), JSON.stringify({ seq, hash, updatedAt: new Date().toISOString() }));
123
+ }
124
+ catch { /* best effort */ }
125
+ }
126
+ export function readShadowChainCursor(assessmentId) {
127
+ const dir = shadowDirFor(assessmentId);
128
+ if (!dir)
129
+ return null;
130
+ try {
131
+ const parsed = JSON.parse(fs.readFileSync(path.join(dir, "chain.json"), "utf8"));
132
+ if (typeof parsed.seq !== "number" || typeof parsed.hash !== "string" || !parsed.hash)
133
+ return null;
134
+ return parsed;
135
+ }
136
+ catch {
137
+ return null;
138
+ }
139
+ }
140
+ // ── Tracker pid/nonce mirrors ─────────────────────────────────────
141
+ export function writeShadowTrackerFile(assessmentId, name, content) {
142
+ const dir = shadowDirFor(assessmentId);
143
+ if (!dir)
144
+ return;
145
+ try {
146
+ fs.mkdirSync(dir, { recursive: true });
147
+ fs.writeFileSync(path.join(dir, name), content);
148
+ }
149
+ catch { /* best effort */ }
150
+ }
151
+ export function readShadowTrackerFile(assessmentId, name) {
152
+ const dir = shadowDirFor(assessmentId);
153
+ if (!dir)
154
+ return null;
155
+ try {
156
+ return fs.readFileSync(path.join(dir, name), "utf8");
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ }
162
+ export function shadowTrackerFileMtime(assessmentId, name) {
163
+ const dir = shadowDirFor(assessmentId);
164
+ if (!dir)
165
+ return null;
166
+ try {
167
+ return fs.statSync(path.join(dir, name)).mtimeMs;
168
+ }
169
+ catch {
170
+ return null;
171
+ }
172
+ }
173
+ // ── Lifecycle ─────────────────────────────────────────────────────
174
+ export function deleteShadowForAssessment(assessmentId) {
175
+ const dir = shadowDirFor(assessmentId);
176
+ if (!dir)
177
+ return;
178
+ try {
179
+ fs.rmSync(dir, { recursive: true, force: true });
180
+ }
181
+ catch { /* best effort */ }
182
+ const logPath = shadowLogPath(assessmentId);
183
+ if (logPath) {
184
+ try {
185
+ fs.rmSync(logPath, { force: true });
186
+ }
187
+ catch { /* best effort */ }
188
+ }
189
+ }
190
+ /** Delete the shadow keyed to a project dir — works when the in-repo config
191
+ * (which would have named the assessment) is already gone. */
192
+ export function deleteShadowForProject(projectDir) {
193
+ const shadow = findShadowConfigForDir(projectDir);
194
+ if (shadow)
195
+ deleteShadowForAssessment(shadow.assessmentId);
196
+ }
197
+ /**
198
+ * Remove shadows whose assessment is demonstrably over on this machine:
199
+ * project dir no longer exists AND no registry entry points at it. A live
200
+ * assessment whose folder was merely wiped keeps its registry entry, so it
201
+ * survives pruning until submit/expiry releases it.
202
+ */
203
+ export function pruneShadows(registryPaths) {
204
+ const registered = new Set(registryPaths.map((p) => normalizeDir(p)));
205
+ let pruned = 0;
206
+ for (const id of listShadowIds()) {
207
+ const shadow = readShadowConfigById(id);
208
+ if (!shadow)
209
+ continue; // unreadable shadow: leave for a human, never silently discard
210
+ const dir = normalizeDir(shadow.projectDir);
211
+ let dirExists = false;
212
+ try {
213
+ dirExists = fs.existsSync(dir);
214
+ }
215
+ catch { /* treat as missing */ }
216
+ if (!dirExists && !registered.has(dir)) {
217
+ deleteShadowForAssessment(id);
218
+ pruned++;
219
+ }
220
+ }
221
+ return pruned;
222
+ }
223
+ // ── Candidate-facing repair file (last resort) ────────────────────
224
+ export const REPAIR_FILE_NAME = "LITMUS-REPAIR.md";
225
+ /** Written by the watcher only when self-heal could not act (server
226
+ * unreachable, no usable shadow, AND the in-memory restore failed to write).
227
+ * Rewritten every heartbeat while the outage lasts — a cleanup pass that
228
+ * deletes it gets it back — and removed the moment the config is restored.
229
+ *
230
+ * Deliberately carries NO credentials: a project-root file can be committed,
231
+ * pushed, read into an AI agent's context, or shipped in the submission zip,
232
+ * so the token must never appear here. The candidate's assessment page
233
+ * ("Something not working?") shows the full copy-paste repair command, and
234
+ * `litmus doctor` run bare prompts for the token pointing at that panel. */
235
+ export function repairFileContent() {
236
+ return [
237
+ "# Litmus needs a one-minute repair",
238
+ "",
239
+ "Your assessment's tracking settings went missing, so your activity is not",
240
+ "being recorded and `litmus submit` will not work until they are restored.",
241
+ "",
242
+ "Open your assessment page (the link from your invite email) and under",
243
+ '"Something not working?" copy the repair command shown there. Or run:',
244
+ "",
245
+ "```",
246
+ "npm install -g litmus-cli@latest",
247
+ "litmus doctor",
248
+ "```",
249
+ "",
250
+ "and paste your assessment token when asked (it is on that same page).",
251
+ "Your work and your deadline are not affected. This file disappears once",
252
+ "the repair has run.",
253
+ "",
254
+ ].join("\n");
255
+ }
256
+ export function writeRepairFile(projectDir) {
257
+ try {
258
+ fs.writeFileSync(path.join(projectDir, REPAIR_FILE_NAME), repairFileContent(), "utf8");
259
+ }
260
+ catch { /* best effort */ }
261
+ }
262
+ export function removeRepairFile(projectDir) {
263
+ try {
264
+ fs.rmSync(path.join(projectDir, REPAIR_FILE_NAME), { force: true });
265
+ }
266
+ catch { /* best effort */ }
267
+ }
268
+ //# sourceMappingURL=shadow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.js","sourceRoot":"","sources":["../../src/lib/shadow.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ export interface TrackerDiagnostics {
23
23
  /** The same predicate startTracker/isTrackerHealthy use. */
24
24
  healthy: boolean;
25
25
  }
26
- export declare function trackerDiagnostics(projectDir: string): TrackerDiagnostics;
26
+ export declare function trackerDiagnostics(projectDir: string, assessmentId?: string): TrackerDiagnostics;
27
27
  /**
28
28
  * Start the file watcher in the background.
29
29
  * Spawns watcher.js as a detached process; logs file events to
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/lib/tracker.ts"],"names":[],"mappings":"AA4FA;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAM5D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,oFAAoF;IACpF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,4DAA4D;IAC5D,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAsBzE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAqErD"}
1
+ {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/lib/tracker.ts"],"names":[],"mappings":"AA6FA;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAM5D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,oFAAoF;IACpF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,4DAA4D;IAC5D,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,kBAAkB,CA4ChG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAiFrD"}
@@ -2,6 +2,7 @@ import { spawn } from "child_process";
2
2
  import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync, statSync, unlinkSync, closeSync } from "fs";
3
3
  import path from "path";
4
4
  import { fileURLToPath } from "url";
5
+ import { readShadowTrackerFile, shadowTrackerFileMtime, shadowLogPath } from "./shadow.js";
5
6
  const __filename = fileURLToPath(import.meta.url);
6
7
  const __dirname = path.dirname(__filename);
7
8
  const NONCE_FILE = "tracker.nonce";
@@ -103,7 +104,7 @@ export function isTrackerHealthy(projectDir) {
103
104
  const litmusDir = path.join(projectDir, ".litmus");
104
105
  return isExistingTrackerOurs(path.join(litmusDir, "tracker.pid"), path.join(litmusDir, NONCE_FILE));
105
106
  }
106
- export function trackerDiagnostics(projectDir) {
107
+ export function trackerDiagnostics(projectDir, assessmentId) {
107
108
  const litmusDir = path.join(projectDir, ".litmus");
108
109
  const pidFile = path.join(litmusDir, "tracker.pid");
109
110
  const noncePath = path.join(litmusDir, NONCE_FILE);
@@ -119,11 +120,34 @@ export function trackerDiagnostics(projectDir) {
119
120
  nonceAgeMs = Date.now() - statSync(noncePath).mtimeMs;
120
121
  }
121
122
  catch { /* no nonce */ }
123
+ // ENG-1453: a wiped .litmus loses the liveness files while the watcher
124
+ // itself keeps running. The watcher mirrors both signals outside the repo,
125
+ // so doctor can still find the live incumbent and hand off gracefully
126
+ // instead of spawning a duplicate.
127
+ if (pid === null && assessmentId) {
128
+ const mirrored = readShadowTrackerFile(assessmentId, "tracker.pid");
129
+ if (mirrored !== null) {
130
+ const parsed = parseInt(mirrored.trim(), 10);
131
+ if (!isNaN(parsed))
132
+ pid = parsed;
133
+ }
134
+ }
135
+ if (nonceAgeMs === null && assessmentId) {
136
+ const mtime = shadowTrackerFileMtime(assessmentId, "tracker.nonce");
137
+ if (mtime !== null)
138
+ nonceAgeMs = Date.now() - mtime;
139
+ }
140
+ const pidAlive = pid !== null && isProcessAlive(pid);
122
141
  return {
123
142
  pid,
124
- pidAlive: pid !== null && isProcessAlive(pid),
143
+ pidAlive,
125
144
  nonceAgeMs,
126
- healthy: isExistingTrackerOurs(pidFile, noncePath),
145
+ // Same predicate as isExistingTrackerOurs, computed over whichever source
146
+ // (in-repo file or shadow mirror) provided each signal. The lower bound
147
+ // tolerates sub-second skew: filesystem mtime granularity can stamp a
148
+ // just-touched nonce fractionally ahead of Date.now(), and a fresh nonce
149
+ // must never read as unhealthy. Far-future mtimes still fail.
150
+ healthy: pidAlive && nonceAgeMs !== null && nonceAgeMs > -1000 && nonceAgeMs <= NONCE_MAX_AGE_MS,
127
151
  };
128
152
  }
129
153
  /**
@@ -135,8 +159,22 @@ export function startTracker(projectDir) {
135
159
  const litmusDir = path.join(projectDir, ".litmus");
136
160
  const activityLog = path.join(litmusDir, "activity.jsonl");
137
161
  const pidFile = path.join(litmusDir, "tracker.pid");
138
- const errLog = path.join(litmusDir, "tracker.log");
139
162
  const noncePath = path.join(litmusDir, NONCE_FILE);
163
+ // ENG-1453: the diagnostics log lives OUTSIDE the repo (~/.litmus/logs/)
164
+ // so a repo cleanup can't destroy the forensic record — the incident that
165
+ // motivated this was uninvestigable precisely because tracker.log died
166
+ // with the .litmus dir. Falls back to the legacy in-repo path when the
167
+ // config (which names the assessment) isn't readable at spawn.
168
+ let assessmentId = null;
169
+ try {
170
+ assessmentId = JSON.parse(readFileSync(path.join(litmusDir, "config.json"), "utf8")).assessmentId ?? null;
171
+ }
172
+ catch { /* legacy path below */ }
173
+ const errLog = (assessmentId ? shadowLogPath(assessmentId) : null) ?? path.join(litmusDir, "tracker.log");
174
+ try {
175
+ mkdirSync(path.dirname(errLog), { recursive: true });
176
+ }
177
+ catch { /* openSync below will surface it */ }
140
178
  // Without .litmus/ we can't write the err log or pid file, and spawning a
141
179
  // tracker that has nowhere to land its output is worse than no tracker.
142
180
  // Bail loudly enough to be caught upstream so the candidate sees the warning.
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/lib/tracker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/lib/tracker.ts"],"names":[],"mappings":"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"}