litmus-cli 1.0.20 → 1.1.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 (61) hide show
  1. package/README.md +2 -2
  2. package/dist/commands/init.d.ts.map +1 -1
  3. package/dist/commands/init.js +4 -3
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/commands/status.js +1 -1
  6. package/dist/commands/status.js.map +1 -1
  7. package/dist/commands/submit.d.ts.map +1 -1
  8. package/dist/commands/submit.js +35 -11
  9. package/dist/commands/submit.js.map +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/lib/api.d.ts +6 -16
  13. package/dist/lib/api.d.ts.map +1 -1
  14. package/dist/lib/api.js +67 -5
  15. package/dist/lib/api.js.map +1 -1
  16. package/dist/lib/config.d.ts +0 -3
  17. package/dist/lib/config.d.ts.map +1 -1
  18. package/dist/lib/config.js +0 -3
  19. package/dist/lib/config.js.map +1 -1
  20. package/dist/lib/detect-project.d.ts.map +1 -0
  21. package/dist/lib/detect-project.js.map +1 -0
  22. package/dist/lib/env-hosts.d.ts +12 -0
  23. package/dist/lib/env-hosts.d.ts.map +1 -0
  24. package/dist/lib/env-hosts.js +19 -0
  25. package/dist/lib/env-hosts.js.map +1 -0
  26. package/dist/{utils → lib}/errors.d.ts +1 -1
  27. package/dist/lib/errors.d.ts.map +1 -0
  28. package/dist/{utils → lib}/errors.js +2 -3
  29. package/dist/lib/errors.js.map +1 -0
  30. package/dist/lib/extract.d.ts +1 -1
  31. package/dist/lib/extract.d.ts.map +1 -1
  32. package/dist/lib/extract.js +19 -3
  33. package/dist/lib/extract.js.map +1 -1
  34. package/dist/lib/hook-logger.cjs +6 -4
  35. package/dist/lib/platform.d.ts +37 -0
  36. package/dist/lib/platform.d.ts.map +1 -0
  37. package/dist/lib/platform.js +69 -0
  38. package/dist/lib/platform.js.map +1 -0
  39. package/dist/lib/schemas.d.ts +24 -0
  40. package/dist/lib/schemas.d.ts.map +1 -0
  41. package/dist/lib/schemas.js +21 -0
  42. package/dist/lib/schemas.js.map +1 -0
  43. package/dist/lib/tracker.d.ts +1 -1
  44. package/dist/lib/tracker.js +2 -2
  45. package/dist/lib/tracker.js.map +1 -1
  46. package/dist/lib/watcher.d.ts +17 -0
  47. package/dist/lib/watcher.d.ts.map +1 -0
  48. package/dist/lib/watcher.js +752 -0
  49. package/dist/lib/watcher.js.map +1 -0
  50. package/package.json +23 -8
  51. package/dist/lib/watcher.cjs +0 -592
  52. package/dist/staging.d.ts +0 -2
  53. package/dist/staging.d.ts.map +0 -1
  54. package/dist/staging.js +0 -6
  55. package/dist/staging.js.map +0 -1
  56. package/dist/utils/detect-project.d.ts.map +0 -1
  57. package/dist/utils/detect-project.js.map +0 -1
  58. package/dist/utils/errors.d.ts.map +0 -1
  59. package/dist/utils/errors.js.map +0 -1
  60. /package/dist/{utils → lib}/detect-project.d.ts +0 -0
  61. /package/dist/{utils → lib}/detect-project.js +0 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Maps a 1-character env code (the prefix on a minted assessment token) to the
3
+ * Vercel preview host that issued it. Production tokens have no prefix and fall
4
+ * through to the FALLBACK_URL in `errors.ts`.
5
+ *
6
+ * Keep in sync with `LITMUS_ENV_CODE` set on the corresponding Vercel envs
7
+ * (see ENG-440).
8
+ */
9
+ export const ENV_HOSTS = {
10
+ c: "https://caroline.litmushiring.com",
11
+ e: "https://elena.litmushiring.com",
12
+ s: "https://shaivi.litmushiring.com",
13
+ };
14
+ /** Resolve apiBase from a token's env prefix, or null if no recognized prefix. */
15
+ export function resolveApiBaseFromToken(token) {
16
+ const m = token.match(/^([a-z])_/);
17
+ return m && ENV_HOSTS[m[1]] ? ENV_HOSTS[m[1]] : null;
18
+ }
19
+ //# sourceMappingURL=env-hosts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-hosts.js","sourceRoot":"","sources":["../../src/lib/env-hosts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAA2B;IAC/C,CAAC,EAAE,mCAAmC;IACtC,CAAC,EAAE,gCAAgC;IACnC,CAAC,EAAE,iCAAiC;CACrC,CAAA;AAED,kFAAkF;AAClF,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAClC,OAAO,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtD,CAAC"}
@@ -1,4 +1,4 @@
1
- export declare const FALLBACK_URL = "https://www.litmus.build";
1
+ export declare const FALLBACK_URL = "https://www.litmushiring.com";
2
2
  export declare class FatalError extends Error {
3
3
  constructor();
4
4
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,iCAAiC,CAAA;AAE1D,qBAAa,UAAW,SAAQ,KAAK;;CAEpC;AAID,QAAA,IAAI,YAAY,EAAE;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAEN,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,YAAY,CAAC,GAAG,IAAI,CAEvE;AAaD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;AAyCnD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAE,GAC/D,KAAK,CA2BP;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C"}
@@ -3,7 +3,7 @@ import os from "os";
3
3
  import { readFileSync } from "fs";
4
4
  import { fileURLToPath } from "url";
5
5
  import { dirname, join } from "path";
6
- export const FALLBACK_URL = "https://www.litmus.build";
6
+ export const FALLBACK_URL = "https://www.litmushiring.com";
7
7
  export class FatalError extends Error {
8
8
  constructor() { super(); }
9
9
  }
@@ -40,7 +40,6 @@ function reportEvent(severity, message, hint, event) {
40
40
  const payload = JSON.stringify({
41
41
  severity,
42
42
  event,
43
- token: errorContext.token,
44
43
  message,
45
44
  hint,
46
45
  candidateEmail: errorContext.candidateEmail,
@@ -80,7 +79,7 @@ export function fatal(message, hint, opts) {
80
79
  if (hint) {
81
80
  console.error(chalk.dim(" Hint: ") + hint);
82
81
  }
83
- console.error(chalk.dim(` Need help? Visit ${FALLBACK_URL} or contact founders@litmus.build`));
82
+ console.error(chalk.dim(` Need help? Visit ${FALLBACK_URL} or contact founders@litmushiring.com`));
84
83
  console.error();
85
84
  // Build the full message for the report (includes internal details the candidate doesn't see)
86
85
  const reportMessage = internalDetail ? `${message}\n\nInternal detail: ${internalDetail}` : message;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Extract a ZIP file from disk to a target directory.
3
- * Using Open.file + extract() is the most reliable unzipper API.
3
+ * Iterates entries individually to validate paths against traversal attacks.
4
4
  */
5
5
  export declare function extractZip(zipPath: string, targetDir: string): Promise<void>;
6
6
  //# sourceMappingURL=extract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
@@ -1,12 +1,28 @@
1
+ import { createWriteStream } from "fs";
1
2
  import { mkdir } from "fs/promises";
3
+ import path from "path";
4
+ import { pipeline } from "stream/promises";
2
5
  import unzipper from "unzipper";
3
6
  /**
4
7
  * Extract a ZIP file from disk to a target directory.
5
- * Using Open.file + extract() is the most reliable unzipper API.
8
+ * Iterates entries individually to validate paths against traversal attacks.
6
9
  */
7
10
  export async function extractZip(zipPath, targetDir) {
8
- await mkdir(targetDir, { recursive: true });
11
+ const resolvedTarget = path.resolve(targetDir) + path.sep;
12
+ await mkdir(resolvedTarget, { recursive: true });
9
13
  const directory = await unzipper.Open.file(zipPath);
10
- await directory.extract({ path: targetDir });
14
+ for (const entry of directory.files) {
15
+ const resolvedPath = path.resolve(resolvedTarget, entry.path);
16
+ if (!resolvedPath.startsWith(resolvedTarget)) {
17
+ throw new Error(`ZIP path traversal detected: "${entry.path}" escapes target directory`);
18
+ }
19
+ if (entry.type === "Directory") {
20
+ await mkdir(resolvedPath, { recursive: true });
21
+ }
22
+ else {
23
+ await mkdir(path.dirname(resolvedPath), { recursive: true });
24
+ await pipeline(entry.stream(), createWriteStream(resolvedPath));
25
+ }
26
+ }
11
27
  }
12
28
  //# sourceMappingURL=extract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,QAAQ,MAAM,UAAU,CAAA;AAE/B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,SAAiB;IAEjB,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3C,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnD,MAAM,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;AAC9C,CAAC"}
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,IAAI,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,QAAQ,MAAM,UAAU,CAAA;AAE/B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,SAAiB;IAEjB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,CAAA;IACzD,MAAM,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAEhD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEnD,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAC7D,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CACb,iCAAiC,KAAK,CAAC,IAAI,4BAA4B,CACxE,CAAA;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC5D,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAA;QACjE,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -58,14 +58,16 @@ function uploadEvent(config, event) {
58
58
  if (!config || !config.token || !config.backendUrl) return
59
59
 
60
60
  const body = JSON.stringify({ events: [event] })
61
- const url = new URL(
62
- `${config.backendUrl}/cli/activity?token=${encodeURIComponent(config.token)}`
63
- )
61
+ const url = new URL(`${config.backendUrl}/cli/activity`)
64
62
  const reqFn = url.protocol === "https:" ? https.request : http.request
65
63
 
66
64
  const req = reqFn(url, {
67
65
  method: "POST",
68
- headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ "Content-Length": Buffer.byteLength(body),
69
+ "Authorization": `Bearer ${config.token}`,
70
+ },
69
71
  timeout: 5000,
70
72
  })
71
73
  req.on("error", () => {}) // Swallow — fire-and-forget
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Platform adapter — encapsulates OS-specific commands so detection logic
3
+ * stays platform-agnostic. Callers never branch on process.platform.
4
+ */
5
+ /**
6
+ * Returns lowercase text listing all running processes.
7
+ * Used for regex matching against editor/AI tool name patterns.
8
+ *
9
+ * - macOS/Linux: `ps aux` (full command lines)
10
+ * - Windows: `tasklist` (image names like Code.exe, Cursor.exe)
11
+ *
12
+ * Output is lowercased so patterns like /\bcode\b/ match uniformly.
13
+ */
14
+ export declare function getProcessList(): string;
15
+ /**
16
+ * Lists active network connections asynchronously.
17
+ * Calls cb(err, stdout) with raw connection output.
18
+ * Handles platform fallbacks internally (lsof → ss on Linux).
19
+ *
20
+ * - macOS: `lsof -i -n -P`
21
+ * - Linux: `lsof -i -n -P`, falls back to `ss -tnp` if lsof is unavailable
22
+ * - Windows: `netstat -ano`
23
+ */
24
+ export declare function getConnections(cb: (err: Error | null, stdout?: string) => void): void;
25
+ /**
26
+ * Extracts the destination IP address from a single connection output line.
27
+ * Returns a regex match array [fullMatch, ip, port] or null.
28
+ *
29
+ * Format varies by tool:
30
+ * lsof: "... ->104.18.7.42:443 ..."
31
+ * ss: "... 104.18.7.42:443" (IP:port at end of line)
32
+ * netstat: " TCP local:port foreign:port STATE PID" (3rd column)
33
+ *
34
+ * On Unix, trying both lsof and ss patterns is safe — they don't cross-match.
35
+ */
36
+ export declare function extractConnectionIP(line: string): RegExpMatchArray | null;
37
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAarF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAYzE"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Platform adapter — encapsulates OS-specific commands so detection logic
3
+ * stays platform-agnostic. Callers never branch on process.platform.
4
+ */
5
+ import { execSync, execFile } from "child_process";
6
+ const isWin = process.platform === "win32";
7
+ const isLinux = process.platform === "linux";
8
+ /**
9
+ * Returns lowercase text listing all running processes.
10
+ * Used for regex matching against editor/AI tool name patterns.
11
+ *
12
+ * - macOS/Linux: `ps aux` (full command lines)
13
+ * - Windows: `tasklist` (image names like Code.exe, Cursor.exe)
14
+ *
15
+ * Output is lowercased so patterns like /\bcode\b/ match uniformly.
16
+ */
17
+ export function getProcessList() {
18
+ const cmd = isWin ? "tasklist" : "ps aux";
19
+ return execSync(cmd, { timeout: 5000, encoding: "utf8" }).toLowerCase();
20
+ }
21
+ /**
22
+ * Lists active network connections asynchronously.
23
+ * Calls cb(err, stdout) with raw connection output.
24
+ * Handles platform fallbacks internally (lsof → ss on Linux).
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`
29
+ */
30
+ export function getConnections(cb) {
31
+ if (isWin) {
32
+ execFile("netstat", ["-ano"], { timeout: 10000, encoding: "utf8", maxBuffer: 2 * 1024 * 1024 }, cb);
33
+ return;
34
+ }
35
+ execFile("lsof", ["-i", "-n", "-P"], { timeout: 10000, encoding: "utf8", maxBuffer: 2 * 1024 * 1024 }, (err, stdout) => {
36
+ if (err && err.code === "ENOENT" && isLinux) {
37
+ // lsof unavailable on Linux — fall back to ss
38
+ execFile("ss", ["-tnp"], { timeout: 5000, encoding: "utf8", maxBuffer: 1024 * 1024 }, cb);
39
+ }
40
+ else {
41
+ cb(err, stdout);
42
+ }
43
+ });
44
+ }
45
+ /**
46
+ * Extracts the destination IP address from a single connection output line.
47
+ * Returns a regex match array [fullMatch, ip, port] or null.
48
+ *
49
+ * 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)
53
+ *
54
+ * On Unix, trying both lsof and ss patterns is safe — they don't cross-match.
55
+ */
56
+ export function extractConnectionIP(line) {
57
+ if (isWin) {
58
+ const parts = line.trim().split(/\s+/);
59
+ if (parts.length >= 4 && (parts[0] === "TCP" || parts[0] === "UDP")) {
60
+ return parts[2].match(/^(\d+\.\d+\.\d+\.\d+):(\d+)$/);
61
+ }
62
+ return null;
63
+ }
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*$/);
68
+ }
69
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +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"}
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ export declare const InitMetadataSchema: z.ZodObject<{
3
+ assessmentId: z.ZodUUID;
4
+ assessmentName: z.ZodString;
5
+ folderName: z.ZodString;
6
+ backendUrl: z.ZodURL;
7
+ candidateEmail: z.ZodEmail;
8
+ candidateName: z.ZodString;
9
+ deadline: z.ZodNullable<z.ZodISODateTime>;
10
+ timeLimit: z.ZodNullable<z.ZodNumber>;
11
+ startedAt: z.ZodISODateTime;
12
+ }, z.core.$strip>;
13
+ export type InitMetadata = z.infer<typeof InitMetadataSchema>;
14
+ export declare const SubmitResultSchema: z.ZodObject<{
15
+ submissionId: z.ZodUUID;
16
+ success: z.ZodBoolean;
17
+ message: z.ZodString;
18
+ }, z.core.$strip>;
19
+ export type SubmitResult = z.infer<typeof SubmitResultSchema>;
20
+ export declare const SubmitStatusSchema: z.ZodObject<{
21
+ videoUploaded: z.ZodBoolean;
22
+ }, z.core.$strip>;
23
+ export type SubmitStatus = z.infer<typeof SubmitStatusSchema>;
24
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,kBAAkB;;;;;;;;;;iBAa7B,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE7D,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE7D,eAAO,MAAM,kBAAkB;;iBAE7B,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ export const InitMetadataSchema = z.object({
3
+ assessmentId: z.uuid(),
4
+ assessmentName: z.string(),
5
+ folderName: z.string(),
6
+ backendUrl: z.url(),
7
+ candidateEmail: z.email(),
8
+ candidateName: z.string(),
9
+ deadline: z.iso.datetime().nullable(),
10
+ timeLimit: z.number().nullable(),
11
+ startedAt: z.iso.datetime(),
12
+ });
13
+ export const SubmitResultSchema = z.object({
14
+ submissionId: z.uuid(),
15
+ success: z.boolean(),
16
+ message: z.string(),
17
+ });
18
+ export const SubmitStatusSchema = z.object({
19
+ videoUploaded: z.boolean(),
20
+ });
21
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE;IACtB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAE1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,GAAG,EAAE;IAEnB,cAAc,EAAE,CAAC,CAAC,KAAK,EAAE;IACzB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IAEzB,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAA;AAIF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE;IACtB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAA;AAIF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE;CAC3B,CAAC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Start the file watcher in the background.
3
- * Spawns watcher.cjs as a detached process; logs file events to
3
+ * Spawns watcher.js as a detached process; logs file events to
4
4
  * <projectDir>/.litmus/activity.jsonl for later inclusion in the submission ZIP.
5
5
  */
6
6
  export declare function startTracker(projectDir: string): void;
@@ -15,7 +15,7 @@ function isProcessAlive(pid) {
15
15
  }
16
16
  /**
17
17
  * Start the file watcher in the background.
18
- * Spawns watcher.cjs as a detached process; logs file events to
18
+ * Spawns watcher.js as a detached process; logs file events to
19
19
  * <projectDir>/.litmus/activity.jsonl for later inclusion in the submission ZIP.
20
20
  */
21
21
  export function startTracker(projectDir) {
@@ -42,7 +42,7 @@ export function startTracker(projectDir) {
42
42
  }
43
43
  }
44
44
  const errFd = openSync(errLog, "a");
45
- const watcherPath = path.join(__dirname, "watcher.cjs");
45
+ const watcherPath = path.join(__dirname, "watcher.js");
46
46
  const cliBinPath = path.join(__dirname, "..", "index.js");
47
47
  const child = spawn(process.execPath, [watcherPath, projectDir, activityLog, cliBinPath], {
48
48
  detached: true,
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/lib/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AACjF,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AAEnC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAE1C,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA,CAAC,qCAAqC;QAC1D,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IAElD,IAAI,CAAC;QACH,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IAED,wCAAwC;IACxC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;YACtE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;gBACvD,OAAM,CAAC,0BAA0B;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uDAAuD;QACzD,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAA;IAEzD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE;QACxF,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC;KACnC,CAAC,CAAA;IAEF,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,IAAI,CAAC;YACH,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAA;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/lib/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AACjF,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AAEnC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAE1C,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA,CAAC,qCAAqC;QAC1D,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IAElD,IAAI,CAAC;QACH,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IAED,wCAAwC;IACxC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;YACtE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;gBACvD,OAAM,CAAC,0BAA0B;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uDAAuD;QACzD,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAA;IAEzD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE;QACxF,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC;KACnC,CAAC,CAAA;IAEF,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,IAAI,CAAC;YACH,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAA;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Litmus file activity watcher.
4
+ * Runs as a detached background process started by `litmus init`.
5
+ * Usage: node watcher.js <projectDir> <activityLogPath> [cliBinPath]
6
+ *
7
+ * Tracks:
8
+ * - File changes with size deltas and large-paste detection (debounced 500ms per file)
9
+ * - Heartbeat events every 5 minutes
10
+ * - Git commit events (polled every 30s)
11
+ * - Editor and AI tool detection (polled every 10 min)
12
+ * - Auto-submit at deadline (if deadline/timeLimit configured)
13
+ *
14
+ * Writes newline-delimited JSON to the activity log.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/lib/watcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}