litmus-cli 1.0.14 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +151 -20
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +12 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +41 -19
- package/dist/commands/submit.js.map +1 -1
- package/dist/lib/api.d.ts +2 -2
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +3 -6
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/config.d.ts +9 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +16 -3
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/hook-logger.cjs +180 -0
- package/dist/lib/tracker.d.ts +1 -1
- package/dist/lib/tracker.d.ts.map +1 -1
- package/dist/lib/tracker.js +29 -21
- package/dist/lib/tracker.js.map +1 -1
- package/dist/lib/watcher.cjs +354 -19
- package/dist/lib/zip.d.ts.map +1 -1
- package/dist/lib/zip.js +32 -15
- package/dist/lib/zip.js.map +1 -1
- package/dist/staging.d.ts +2 -0
- package/dist/staging.d.ts.map +1 -0
- package/dist/staging.js +6 -0
- package/dist/staging.js.map +1 -0
- package/dist/utils/detect-project.d.ts +0 -1
- package/dist/utils/detect-project.d.ts.map +1 -1
- package/dist/utils/detect-project.js +10 -14
- package/dist/utils/detect-project.js.map +1 -1
- package/dist/utils/errors.js +1 -1
- package/dist/utils/errors.js.map +1 -1
- package/package.json +4 -3
- package/dist/watcher.cjs +0 -79
package/dist/lib/watcher.cjs
CHANGED
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Litmus file activity watcher.
|
|
4
4
|
* Runs as a detached background process started by `litmus init`.
|
|
5
|
-
* Usage: node watcher.cjs <projectDir> <activityLogPath>
|
|
5
|
+
* Usage: node watcher.cjs <projectDir> <activityLogPath> [cliBinPath]
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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.
|
|
10
15
|
*/
|
|
11
16
|
|
|
12
17
|
const fs = require("fs")
|
|
13
18
|
const path = require("path")
|
|
19
|
+
const { execSync, execFile } = require("child_process")
|
|
20
|
+
const https = require("https")
|
|
21
|
+
const http = require("http")
|
|
14
22
|
|
|
15
|
-
const [, , projectDir, activityLogPath] = process.argv
|
|
23
|
+
const [, , projectDir, activityLogPath, cliBinPath] = process.argv
|
|
16
24
|
|
|
17
25
|
process.stderr.write(`[watcher] starting: projectDir=${projectDir} log=${activityLogPath}\n`)
|
|
18
26
|
process.stderr.write(`[watcher] node ${process.version}, platform=${process.platform}, arch=${process.arch}\n`)
|
|
@@ -33,29 +41,152 @@ fs.mkdirSync(path.dirname(activityLogPath), { recursive: true })
|
|
|
33
41
|
// Open the log file for appending
|
|
34
42
|
const log = fs.createWriteStream(activityLogPath, { flags: "a" })
|
|
35
43
|
|
|
44
|
+
// ── Config ──────────────────────────────────────────────────────
|
|
45
|
+
let litmusConfig = null
|
|
46
|
+
try {
|
|
47
|
+
const configPath = path.join(projectDir, ".litmus", "config.json")
|
|
48
|
+
litmusConfig = JSON.parse(fs.readFileSync(configPath, "utf8"))
|
|
49
|
+
} catch { /* config not available */ }
|
|
50
|
+
|
|
51
|
+
// ── Server upload config ────────────────────────────────────────
|
|
52
|
+
let uploadConfig = null
|
|
53
|
+
let uploadBuffer = []
|
|
54
|
+
const MAX_BUFFER_SIZE = 10_000
|
|
55
|
+
|
|
56
|
+
if (litmusConfig && litmusConfig.token && litmusConfig.backendUrl) {
|
|
57
|
+
uploadConfig = { token: litmusConfig.token, backendUrl: litmusConfig.backendUrl }
|
|
58
|
+
process.stderr.write("[watcher] upload enabled\n")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Upload buffered events to server.
|
|
63
|
+
* Returns a Promise that resolves when the HTTP request completes (used by shutdown).
|
|
64
|
+
* During normal operation the returned promise is ignored (fire-and-forget).
|
|
65
|
+
*/
|
|
66
|
+
function uploadEvents() {
|
|
67
|
+
if (!uploadConfig || uploadBuffer.length === 0) return Promise.resolve()
|
|
68
|
+
|
|
69
|
+
const events = uploadBuffer.splice(0) // take all, clear buffer
|
|
70
|
+
const body = JSON.stringify({ events })
|
|
71
|
+
const url = new URL(
|
|
72
|
+
`${uploadConfig.backendUrl}/cli/activity?token=${encodeURIComponent(uploadConfig.token)}`
|
|
73
|
+
)
|
|
74
|
+
const reqFn = url.protocol === "https:" ? https.request : http.request
|
|
75
|
+
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
const req = reqFn(url, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
|
|
80
|
+
timeout: 10_000,
|
|
81
|
+
}, (res) => {
|
|
82
|
+
// Drain response
|
|
83
|
+
res.resume()
|
|
84
|
+
res.on("end", () => {
|
|
85
|
+
if (res.statusCode >= 500) {
|
|
86
|
+
process.stderr.write(`[watcher] upload failed (will retry): ${res.statusCode}\n`)
|
|
87
|
+
uploadBuffer = events.concat(uploadBuffer)
|
|
88
|
+
if (uploadBuffer.length > MAX_BUFFER_SIZE) {
|
|
89
|
+
uploadBuffer = uploadBuffer.slice(-MAX_BUFFER_SIZE)
|
|
90
|
+
}
|
|
91
|
+
} else if (res.statusCode >= 400) {
|
|
92
|
+
process.stderr.write(`[watcher] upload rejected: ${res.statusCode}\n`)
|
|
93
|
+
}
|
|
94
|
+
resolve()
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
req.on("error", () => {
|
|
98
|
+
// Network error — retry on next heartbeat
|
|
99
|
+
uploadBuffer = events.concat(uploadBuffer)
|
|
100
|
+
if (uploadBuffer.length > MAX_BUFFER_SIZE) {
|
|
101
|
+
uploadBuffer = uploadBuffer.slice(-MAX_BUFFER_SIZE)
|
|
102
|
+
}
|
|
103
|
+
resolve()
|
|
104
|
+
})
|
|
105
|
+
req.on("timeout", () => { req.destroy() })
|
|
106
|
+
req.end(body)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Ignore patterns ──────────────────────────────────────────────
|
|
36
111
|
const IGNORED = [
|
|
37
|
-
/[/\\]\./,
|
|
112
|
+
/(?:^|[/\\])\./, // dotfiles and dotdirs (including .git, .litmus)
|
|
38
113
|
/node_modules/,
|
|
39
114
|
/__pycache__/,
|
|
40
115
|
/\.pyc$/,
|
|
41
116
|
/\.class$/,
|
|
42
|
-
/[/\\]venv[/\\]/,
|
|
43
|
-
/[/\\]\.venv[/\\]/,
|
|
117
|
+
/(?:^|[/\\])venv[/\\]/,
|
|
118
|
+
/(?:^|[/\\])\.venv[/\\]/,
|
|
44
119
|
]
|
|
45
120
|
|
|
46
121
|
function shouldIgnore(relPath) {
|
|
47
122
|
return IGNORED.some((re) => re.test(relPath))
|
|
48
123
|
}
|
|
49
124
|
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
125
|
+
function emit(event) {
|
|
126
|
+
log.write(JSON.stringify(event) + "\n")
|
|
127
|
+
if (uploadConfig && uploadBuffer.length < MAX_BUFFER_SIZE) {
|
|
128
|
+
uploadBuffer.push(event)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── File size tracking ───────────────────────────────────────────
|
|
133
|
+
const LARGE_PASTE_THRESHOLD = 5000 // bytes — matches backend constant
|
|
134
|
+
const fileSizes = new Map() // relativePath -> lastKnownSize
|
|
135
|
+
const debounceTimers = new Map() // relativePath -> timeoutId
|
|
136
|
+
|
|
137
|
+
function emitFileEvent(type, relPath) {
|
|
138
|
+
const fullPath = path.join(projectDir, relPath)
|
|
139
|
+
const event = { ts: new Date().toISOString(), type, path: relPath }
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const stat = fs.statSync(fullPath)
|
|
143
|
+
const fileSize = stat.size
|
|
144
|
+
event.fileSize = fileSize
|
|
145
|
+
|
|
146
|
+
const prev = fileSizes.get(relPath)
|
|
147
|
+
const sizeDelta = prev !== undefined ? fileSize - prev : fileSize
|
|
148
|
+
event.sizeDelta = sizeDelta
|
|
149
|
+
fileSizes.set(relPath, fileSize)
|
|
150
|
+
|
|
151
|
+
if (sizeDelta > LARGE_PASTE_THRESHOLD) {
|
|
152
|
+
event.largePaste = true
|
|
153
|
+
event.bytesAdded = sizeDelta
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
// File was deleted or unreadable — still log the event, just without size data
|
|
157
|
+
fileSizes.delete(relPath)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
emit(event)
|
|
57
161
|
}
|
|
58
162
|
|
|
163
|
+
// ── Burst-edit detection (AI-driven multi-file edits) ────────────
|
|
164
|
+
const BURST_WINDOW_MS = 2000 // 2 seconds
|
|
165
|
+
const BURST_MIN_FILES = 3 // 3+ distinct files = burst
|
|
166
|
+
const recentEdits = [] // [{ file, ts }]
|
|
167
|
+
|
|
168
|
+
function checkBurstEdit(filename) {
|
|
169
|
+
const now = Date.now()
|
|
170
|
+
recentEdits.push({ file: filename, ts: now })
|
|
171
|
+
// Prune edits outside the window
|
|
172
|
+
while (recentEdits.length > 0 && now - recentEdits[0].ts > BURST_WINDOW_MS) {
|
|
173
|
+
recentEdits.shift()
|
|
174
|
+
}
|
|
175
|
+
// Count distinct files in the window
|
|
176
|
+
const distinctFiles = new Set(recentEdits.map((e) => e.file))
|
|
177
|
+
if (distinctFiles.size >= BURST_MIN_FILES) {
|
|
178
|
+
const files = [...distinctFiles]
|
|
179
|
+
emit({
|
|
180
|
+
ts: new Date().toISOString(),
|
|
181
|
+
type: "burst_edit",
|
|
182
|
+
fileCount: files.length,
|
|
183
|
+
files: files.slice(0, 10), // Cap at 10 filenames
|
|
184
|
+
})
|
|
185
|
+
recentEdits.length = 0 // Reset to avoid duplicate bursts
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ── File system watcher (debounced) ──────────────────────────────
|
|
59
190
|
process.on("uncaughtException", (err) => {
|
|
60
191
|
process.stderr.write(`[watcher] uncaughtException: ${err.stack || err}\n`)
|
|
61
192
|
process.exit(1)
|
|
@@ -65,15 +196,219 @@ try {
|
|
|
65
196
|
const watcher = fs.watch(projectDir, { recursive: true }, (eventType, filename) => {
|
|
66
197
|
if (!filename) return
|
|
67
198
|
if (shouldIgnore(filename)) return
|
|
68
|
-
|
|
199
|
+
|
|
200
|
+
// Check for burst edits on raw events (before debounce — needs real timing)
|
|
201
|
+
checkBurstEdit(filename)
|
|
202
|
+
|
|
203
|
+
// Debounce per file: collapse multiple events within 500ms into one
|
|
204
|
+
if (debounceTimers.has(filename)) {
|
|
205
|
+
clearTimeout(debounceTimers.get(filename))
|
|
206
|
+
}
|
|
207
|
+
debounceTimers.set(filename, setTimeout(() => {
|
|
208
|
+
debounceTimers.delete(filename)
|
|
209
|
+
emitFileEvent(eventType, filename)
|
|
210
|
+
}, 500))
|
|
69
211
|
})
|
|
70
212
|
|
|
71
213
|
watcher.on("error", (err) => {
|
|
72
214
|
process.stderr.write(`[watcher] fs.watch error: ${err}\n`)
|
|
73
215
|
})
|
|
74
216
|
|
|
75
|
-
process.stderr.write("[watcher] started
|
|
217
|
+
process.stderr.write("[watcher] file watcher started\n")
|
|
76
218
|
} catch (err) {
|
|
77
|
-
|
|
78
|
-
|
|
219
|
+
// fs.watch with recursive: true is not supported on Linux < Node 22.
|
|
220
|
+
// Continue without file watching — heartbeats and git commit polling still work.
|
|
221
|
+
process.stderr.write(`[watcher] file watching unavailable: ${err.message} (heartbeat + git tracking still active)\n`)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── Heartbeats ───────────────────────────────────────────────────
|
|
225
|
+
function emitHeartbeat() {
|
|
226
|
+
emit({ ts: new Date().toISOString(), type: "heartbeat" })
|
|
227
|
+
uploadEvents() // Upload buffered events to server
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
emitHeartbeat() // Immediate heartbeat + initial upload
|
|
231
|
+
setInterval(emitHeartbeat, 5 * 60 * 1000) // Every 5 minutes
|
|
232
|
+
|
|
233
|
+
// ── Git commit polling ───────────────────────────────────────────
|
|
234
|
+
let lastCommitHash = null
|
|
235
|
+
|
|
236
|
+
function readCurrentCommitHash() {
|
|
237
|
+
try {
|
|
238
|
+
const headContent = fs.readFileSync(path.join(projectDir, ".git", "HEAD"), "utf8").trim()
|
|
239
|
+
if (headContent.startsWith("ref: ")) {
|
|
240
|
+
const ref = headContent.slice(5)
|
|
241
|
+
const refPath = path.join(projectDir, ".git", ref)
|
|
242
|
+
// Try loose ref first, then fall back to packed-refs
|
|
243
|
+
if (fs.existsSync(refPath)) {
|
|
244
|
+
return fs.readFileSync(refPath, "utf8").trim()
|
|
245
|
+
}
|
|
246
|
+
const packedPath = path.join(projectDir, ".git", "packed-refs")
|
|
247
|
+
if (fs.existsSync(packedPath)) {
|
|
248
|
+
const packed = fs.readFileSync(packedPath, "utf8")
|
|
249
|
+
for (const line of packed.split("\n")) {
|
|
250
|
+
if (line.endsWith(" " + ref)) {
|
|
251
|
+
return line.split(" ")[0]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
return headContent // Detached HEAD — the hash itself
|
|
257
|
+
}
|
|
258
|
+
} catch { /* .git may not exist */ }
|
|
259
|
+
return null
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Initialize with current commit hash
|
|
263
|
+
lastCommitHash = readCurrentCommitHash()
|
|
264
|
+
|
|
265
|
+
function checkGitCommits() {
|
|
266
|
+
try {
|
|
267
|
+
const currentHash = readCurrentCommitHash()
|
|
268
|
+
if (!currentHash || currentHash === lastCommitHash) return
|
|
269
|
+
|
|
270
|
+
lastCommitHash = currentHash
|
|
271
|
+
|
|
272
|
+
const info = execSync(
|
|
273
|
+
'git log -1 --numstat --pretty=format:"%H|%aI|%s"',
|
|
274
|
+
{ cwd: projectDir, timeout: 5000, encoding: "utf8" }
|
|
275
|
+
)
|
|
276
|
+
const lines = info.trim().split("\n")
|
|
277
|
+
const header = lines[0].replace(/^"|"$/g, "")
|
|
278
|
+
const firstPipe = header.indexOf("|")
|
|
279
|
+
const secondPipe = header.indexOf("|", firstPipe + 1)
|
|
280
|
+
const hash = header.slice(0, firstPipe)
|
|
281
|
+
const message = secondPipe >= 0 ? header.slice(secondPipe + 1) : ""
|
|
282
|
+
let linesAdded = 0, linesDeleted = 0, filesChanged = 0
|
|
283
|
+
|
|
284
|
+
for (let i = 1; i < lines.length; i++) {
|
|
285
|
+
const line = lines[i].trim()
|
|
286
|
+
if (!line) continue
|
|
287
|
+
const parts = line.split("\t")
|
|
288
|
+
if (parts[0] !== "-") linesAdded += parseInt(parts[0], 10) || 0
|
|
289
|
+
if (parts[1] !== "-") linesDeleted += parseInt(parts[1], 10) || 0
|
|
290
|
+
filesChanged++
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
emit({
|
|
294
|
+
ts: new Date().toISOString(),
|
|
295
|
+
type: "commit",
|
|
296
|
+
hash: hash ? hash.slice(0, 12) : currentHash.slice(0, 12),
|
|
297
|
+
message: message || "",
|
|
298
|
+
linesAdded,
|
|
299
|
+
linesDeleted,
|
|
300
|
+
filesChanged,
|
|
301
|
+
})
|
|
302
|
+
} catch { /* git not available or command failed — skip */ }
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
setInterval(checkGitCommits, 30 * 1000) // Every 30 seconds
|
|
306
|
+
|
|
307
|
+
// ── Editor & AI tool detection ───────────────────────────────────
|
|
308
|
+
const EDITOR_PATTERNS = {
|
|
309
|
+
vscode: /\bcode\b/,
|
|
310
|
+
cursor: /\bcursor\b/,
|
|
311
|
+
vim: /\b[gn]?vim\b/,
|
|
312
|
+
intellij: /\bidea\b/,
|
|
313
|
+
webstorm: /\bwebstorm\b/,
|
|
314
|
+
sublime: /\bsubl\b/,
|
|
315
|
+
emacs: /\bemacs\b/,
|
|
316
|
+
zed: /\bzed\b/,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const AI_TOOL_PATTERNS = {
|
|
320
|
+
claude: /\bclaude\b/,
|
|
321
|
+
aider: /\baider\b/,
|
|
322
|
+
codeium: /\bcodeium\b/,
|
|
323
|
+
codex: /\bcodex\b/,
|
|
324
|
+
copilot: /\bcopilot\b/,
|
|
79
325
|
}
|
|
326
|
+
|
|
327
|
+
function detectEnvironment() {
|
|
328
|
+
try {
|
|
329
|
+
const ps = execSync("ps aux", { timeout: 5000, encoding: "utf8" })
|
|
330
|
+
|
|
331
|
+
const editors = Object.entries(EDITOR_PATTERNS)
|
|
332
|
+
.filter(([, re]) => re.test(ps))
|
|
333
|
+
.map(([name]) => name)
|
|
334
|
+
|
|
335
|
+
const aiTools = Object.entries(AI_TOOL_PATTERNS)
|
|
336
|
+
.filter(([, re]) => re.test(ps))
|
|
337
|
+
.map(([name]) => name)
|
|
338
|
+
|
|
339
|
+
// Cursor is both an editor and an AI tool
|
|
340
|
+
if (editors.includes("cursor") && !aiTools.includes("cursor")) {
|
|
341
|
+
aiTools.push("cursor")
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (editors.length > 0 || aiTools.length > 0) {
|
|
345
|
+
const event = { ts: new Date().toISOString(), type: "env_detected" }
|
|
346
|
+
if (editors.length) event.editors = editors
|
|
347
|
+
if (aiTools.length) event.aiTools = aiTools
|
|
348
|
+
emit(event)
|
|
349
|
+
}
|
|
350
|
+
} catch { /* ps may fail, skip */ }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
detectEnvironment() // Run immediately on startup
|
|
354
|
+
setInterval(detectEnvironment, 10 * 60 * 1000) // Every 10 minutes
|
|
355
|
+
|
|
356
|
+
// ── Auto-submit at deadline ──────────────────────────────────────
|
|
357
|
+
// NOTE: Deadline calculation duplicated from config.ts getEffectiveDeadline()
|
|
358
|
+
// (this file is CommonJS and cannot import the TS module). Keep both in sync.
|
|
359
|
+
if (cliBinPath && litmusConfig) {
|
|
360
|
+
const deadlineCandidates = [
|
|
361
|
+
litmusConfig.deadline ? new Date(litmusConfig.deadline).getTime() : Infinity,
|
|
362
|
+
litmusConfig.startedAt && litmusConfig.timeLimit
|
|
363
|
+
? new Date(litmusConfig.startedAt).getTime() + litmusConfig.timeLimit * 60_000
|
|
364
|
+
: Infinity,
|
|
365
|
+
].filter(t => t !== Infinity)
|
|
366
|
+
|
|
367
|
+
if (deadlineCandidates.length > 0) {
|
|
368
|
+
const effectiveDeadline = Math.min(...deadlineCandidates)
|
|
369
|
+
const msUntilDeadline = effectiveDeadline - Date.now()
|
|
370
|
+
|
|
371
|
+
const MAX_TIMEOUT = 2_147_483_647 // setTimeout max (2^31 - 1 ms, ~24.8 days)
|
|
372
|
+
if (msUntilDeadline > 0 && msUntilDeadline <= MAX_TIMEOUT) {
|
|
373
|
+
process.stderr.write(`[watcher] deadline timer set for ${new Date(effectiveDeadline).toISOString()} (${Math.round(msUntilDeadline / 1000)}s)\n`)
|
|
374
|
+
setTimeout(() => {
|
|
375
|
+
process.stderr.write("[watcher] deadline reached, auto-submitting\n")
|
|
376
|
+
execFile(process.execPath, [cliBinPath, "submit", "--yes"], { cwd: projectDir, timeout: 5 * 60_000 }, (err, _stdout, stderr) => {
|
|
377
|
+
if (!err) {
|
|
378
|
+
process.stderr.write("[watcher] auto-submit succeeded\n")
|
|
379
|
+
emit({ ts: new Date().toISOString(), type: "auto_submit" })
|
|
380
|
+
} else {
|
|
381
|
+
process.stderr.write(`[watcher] auto-submit failed: ${stderr || err.message}\n`)
|
|
382
|
+
emit({ ts: new Date().toISOString(), type: "auto_submit_failed", error: (stderr || err.message).slice(0, 500) })
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
}, msUntilDeadline)
|
|
386
|
+
} else if (msUntilDeadline > MAX_TIMEOUT) {
|
|
387
|
+
process.stderr.write(`[watcher] deadline too far in the future (${Math.round(msUntilDeadline / 86_400_000)}d), skipping auto-submit timer\n`)
|
|
388
|
+
} else {
|
|
389
|
+
process.stderr.write("[watcher] deadline already passed, skipping auto-submit\n")
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ── Graceful shutdown ────────────────────────────────────────────
|
|
395
|
+
let shuttingDown = false
|
|
396
|
+
function shutdown() {
|
|
397
|
+
if (shuttingDown) return
|
|
398
|
+
shuttingDown = true
|
|
399
|
+
|
|
400
|
+
// Flush buffered events to server, then close log and exit
|
|
401
|
+
const done = () => log.end(() => process.exit(0))
|
|
402
|
+
const uploadPromise = uploadEvents()
|
|
403
|
+
if (uploadPromise) {
|
|
404
|
+
uploadPromise.then(done, done)
|
|
405
|
+
// Hard exit after 5s if upload hangs
|
|
406
|
+
setTimeout(() => process.exit(0), 5000).unref()
|
|
407
|
+
} else {
|
|
408
|
+
done()
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
process.on("SIGTERM", shutdown)
|
|
412
|
+
process.on("SIGINT", shutdown)
|
|
413
|
+
|
|
414
|
+
process.stderr.write("[watcher] all monitors started\n")
|
package/dist/lib/zip.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../../src/lib/zip.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../../src/lib/zip.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC1C;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAwDhF"}
|
package/dist/lib/zip.js
CHANGED
|
@@ -3,10 +3,10 @@ import path from "path";
|
|
|
3
3
|
import os from "os";
|
|
4
4
|
import fg from "fast-glob";
|
|
5
5
|
import { Zip } from "zip-lib";
|
|
6
|
-
// Directories always excluded from submissions.
|
|
7
6
|
// NOTE: .git is intentionally NOT excluded — git history is included so the
|
|
8
7
|
// analysis backend can run parse_git_history() on the submission.
|
|
9
|
-
|
|
8
|
+
// Directories always excluded (generates dir/** and **/dir/** patterns).
|
|
9
|
+
const EXCLUDE_DIRS = [
|
|
10
10
|
"node_modules",
|
|
11
11
|
"__pycache__",
|
|
12
12
|
"venv",
|
|
@@ -18,6 +18,9 @@ const ALWAYS_EXCLUDE = [
|
|
|
18
18
|
"dist",
|
|
19
19
|
".gradle",
|
|
20
20
|
".idea",
|
|
21
|
+
];
|
|
22
|
+
// Files and glob patterns always excluded (generates entry and **/entry patterns).
|
|
23
|
+
const EXCLUDE_FILES = [
|
|
21
24
|
".DS_Store",
|
|
22
25
|
"*.pyc",
|
|
23
26
|
"*.class",
|
|
@@ -48,10 +51,24 @@ export async function createSubmissionZip(projectDir) {
|
|
|
48
51
|
followSymbolicLinks: false,
|
|
49
52
|
ignore: ignoredGlobs,
|
|
50
53
|
});
|
|
51
|
-
// Always include
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
// Always include .litmus diagnostic files if they exist (hidden from reviewer file browser).
|
|
55
|
+
// Cap watcher.err at 256KB — it can grow large on repeated polling errors.
|
|
56
|
+
const WATCHER_ERR_MAX = 256 * 1024;
|
|
57
|
+
for (const litmusFile of [".litmus/activity.jsonl", ".litmus/watcher.err"]) {
|
|
58
|
+
const abs = path.join(projectDir, litmusFile);
|
|
59
|
+
if (!existsSync(abs) || files.includes(litmusFile))
|
|
60
|
+
continue;
|
|
61
|
+
if (litmusFile === ".litmus/watcher.err") {
|
|
62
|
+
try {
|
|
63
|
+
const size = statSync(abs).size;
|
|
64
|
+
if (size > WATCHER_ERR_MAX)
|
|
65
|
+
continue; // skip oversized error log
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
files.push(litmusFile);
|
|
55
72
|
}
|
|
56
73
|
// Measure uncompressed sizes and track per-directory totals
|
|
57
74
|
let uncompressedBytes = 0;
|
|
@@ -79,15 +96,15 @@ export async function createSubmissionZip(projectDir) {
|
|
|
79
96
|
}
|
|
80
97
|
function buildIgnoreGlobs(projectDir) {
|
|
81
98
|
const patterns = [];
|
|
82
|
-
// Always-excluded
|
|
83
|
-
for (const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
99
|
+
// Always-excluded directories
|
|
100
|
+
for (const dir of EXCLUDE_DIRS) {
|
|
101
|
+
patterns.push(`${dir}/**`);
|
|
102
|
+
patterns.push(`**/${dir}/**`);
|
|
103
|
+
}
|
|
104
|
+
// Always-excluded files and globs
|
|
105
|
+
for (const file of EXCLUDE_FILES) {
|
|
106
|
+
patterns.push(file);
|
|
107
|
+
patterns.push(`**/${file}`);
|
|
91
108
|
}
|
|
92
109
|
// Basic .gitignore support
|
|
93
110
|
const gitignorePath = path.join(projectDir, ".gitignore");
|
package/dist/lib/zip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../../src/lib/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AACvD,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,EAAE,MAAM,WAAW,CAAA;AAC1B,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAE7B,
|
|
1
|
+
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../../src/lib/zip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../src/staging.ts"],"names":[],"mappings":""}
|
package/dist/staging.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging.js","sourceRoot":"","sources":["../src/staging.ts"],"names":[],"mappings":";;AACA,kEAAkE;AAClE,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,4BAA4B,CAAC;AAC1D,MAAM,CAAC,YAAY,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-project.d.ts","sourceRoot":"","sources":["../../src/utils/detect-project.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,QAAQ,GACR,IAAI,GACJ,MAAM,GACN,QAAQ,GACR,MAAM,GACN,MAAM,GACN,KAAK,GACL,SAAS,CAAA;AAEb,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"detect-project.d.ts","sourceRoot":"","sources":["../../src/utils/detect-project.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,QAAQ,GACR,IAAI,GACJ,MAAM,GACN,QAAQ,GACR,MAAM,GACN,MAAM,GACN,KAAK,GACL,SAAS,CAAA;AAEb,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAwDtD"}
|
|
@@ -12,7 +12,7 @@ export function detectProject(dir) {
|
|
|
12
12
|
: hasYarnLock
|
|
13
13
|
? "yarn"
|
|
14
14
|
: "npm install";
|
|
15
|
-
return { type: "node", installCommand
|
|
15
|
+
return { type: "node", installCommand };
|
|
16
16
|
}
|
|
17
17
|
if (existsSync(path.join(dir, "requirements.txt")) ||
|
|
18
18
|
existsSync(path.join(dir, "pyproject.toml")) ||
|
|
@@ -23,34 +23,30 @@ export function detectProject(dir) {
|
|
|
23
23
|
const installCommand = existsSync(path.join(dir, "requirements.txt"))
|
|
24
24
|
? `python3 -m venv venv && ${pipBin} install -r requirements.txt`
|
|
25
25
|
: `python3 -m venv venv && ${pipBin} install -e .`;
|
|
26
|
-
return {
|
|
27
|
-
type: "python",
|
|
28
|
-
installCommand,
|
|
29
|
-
runHint: "source venv/bin/activate # then: python main.py / pytest",
|
|
30
|
-
};
|
|
26
|
+
return { type: "python", installCommand };
|
|
31
27
|
}
|
|
32
28
|
if (existsSync(path.join(dir, "go.mod"))) {
|
|
33
|
-
return { type: "go", installCommand: "go mod download"
|
|
29
|
+
return { type: "go", installCommand: "go mod download" };
|
|
34
30
|
}
|
|
35
31
|
if (existsSync(path.join(dir, "pom.xml"))) {
|
|
36
|
-
return { type: "java", installCommand: "mvn install"
|
|
32
|
+
return { type: "java", installCommand: "mvn install" };
|
|
37
33
|
}
|
|
38
34
|
if (existsSync(path.join(dir, "build.gradle")) || existsSync(path.join(dir, "build.gradle.kts"))) {
|
|
39
|
-
return { type: "java", installCommand: "gradle build"
|
|
35
|
+
return { type: "java", installCommand: "gradle build" };
|
|
40
36
|
}
|
|
41
37
|
const files = readdirSync(dir);
|
|
42
38
|
if (files.some(f => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
43
|
-
return { type: "dotnet", installCommand: "dotnet restore"
|
|
39
|
+
return { type: "dotnet", installCommand: "dotnet restore" };
|
|
44
40
|
}
|
|
45
41
|
if (existsSync(path.join(dir, "Cargo.toml"))) {
|
|
46
|
-
return { type: "rust", installCommand: null
|
|
42
|
+
return { type: "rust", installCommand: null };
|
|
47
43
|
}
|
|
48
44
|
if (existsSync(path.join(dir, "Gemfile"))) {
|
|
49
|
-
return { type: "ruby", installCommand: "bundle install"
|
|
45
|
+
return { type: "ruby", installCommand: "bundle install" };
|
|
50
46
|
}
|
|
51
47
|
if (existsSync(path.join(dir, "composer.json"))) {
|
|
52
|
-
return { type: "php", installCommand: "composer install"
|
|
48
|
+
return { type: "php", installCommand: "composer install" };
|
|
53
49
|
}
|
|
54
|
-
return { type: "unknown", installCommand: null
|
|
50
|
+
return { type: "unknown", installCommand: null };
|
|
55
51
|
}
|
|
56
52
|
//# sourceMappingURL=detect-project.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-project.js","sourceRoot":"","sources":["../../src/utils/detect-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AAC5C,OAAO,IAAI,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"detect-project.js","sourceRoot":"","sources":["../../src/utils/detect-project.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -7,7 +7,7 @@ export const FALLBACK_URL = "https://www.litmus.build";
|
|
|
7
7
|
export class FatalError extends Error {
|
|
8
8
|
constructor() { super(); }
|
|
9
9
|
}
|
|
10
|
-
const DEFAULT_API_BASE =
|
|
10
|
+
const DEFAULT_API_BASE = process.env.LITMUS_API_URL || FALLBACK_URL;
|
|
11
11
|
// Module-level context set by each command so fatal() can report errors
|
|
12
12
|
let errorContext = {};
|
|
13
13
|
export function setErrorContext(ctx) {
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEpC,MAAM,CAAC,MAAM,YAAY,GAAG,0BAA0B,CAAA;AAEtD,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,gBAAgB,KAAK,EAAE,CAAA,CAAC,CAAC;CAC1B;AACD,MAAM,gBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"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"}
|