gitdone-agent 0.6.12 → 0.6.13

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 (2) hide show
  1. package/index.js +26 -7
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -27,7 +27,7 @@ import { randomUUID } from 'node:crypto'
27
27
  // Reported to the server on every sync so the web UI can flag outdated agents.
28
28
  // Keep in lockstep with packages/agent/package.json "version" AND
29
29
  // src/lib/agentVersion.ts LATEST_AGENT_VERSION.
30
- const AGENT_VERSION = '0.6.12'
30
+ const AGENT_VERSION = '0.6.13'
31
31
 
32
32
  const AGENT_DIR = join(homedir(), '.gitdone-agent')
33
33
  const CONFIG_PATH = join(AGENT_DIR, 'config.json')
@@ -242,6 +242,17 @@ const GIT_MAX_BUFFER = 64 * 1024 * 1024
242
242
  // bloat the snapshot payload.
243
243
  const UNTRACKED_DIFF_MAX_BYTES = 1024 * 1024
244
244
 
245
+ // Synthesising a per-file diff spawns a synchronous `git diff --no-index` per
246
+ // untracked file. Individually cheap, but a repo with a huge untracked/scratch
247
+ // tree (e.g. extracted game assets — thousands of files) would spawn thousands
248
+ // of blocking git processes and freeze the single-threaded agent for MINUTES on
249
+ // every snapshot, starving the 30s sync heartbeat so the machine flips offline.
250
+ // Bound the synthesis two ways — a file-count cap and a wall-clock budget — so
251
+ // no single repo can ever monopolise the event loop. Anything past either keeps
252
+ // the "no diff" placeholder, identical to the size-cap path above.
253
+ const UNTRACKED_DIFF_MAX_FILES = 200
254
+ const UNTRACKED_DIFF_TIME_BUDGET_MS = 3000
255
+
245
256
  function git(cmd, cwd) {
246
257
  try {
247
258
  return execSync(cmd, { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: GIT_MAX_BUFFER }).trim()
@@ -520,12 +531,20 @@ function getSnapshot(repoPath) {
520
531
  // each from /dev/null so the console shows the whole new file — like GitHub
521
532
  // Desktop (gd-370). Skip anything over the size cap so a stray big/binary blob
522
533
  // can't bloat the snapshot; those keep the "no diff" placeholder.
523
- for (const [file, xy] of Object.entries(statuses)) {
524
- if (xy !== '??' || diffs[file]) continue
525
- try {
526
- const st = statSync(join(repoPath, file))
527
- if (!st.isFile() || st.size > UNTRACKED_DIFF_MAX_BYTES) continue
528
- } catch { continue }
534
+ const untracked = Object.keys(statuses).filter((f) => statuses[f] === '??' && !diffs[f])
535
+ const diffDeadline = Date.now() + UNTRACKED_DIFF_TIME_BUDGET_MS
536
+ let synthesised = 0
537
+ for (const file of untracked) {
538
+ // Stop before either bound so a scratch tree can't starve the heartbeat; the
539
+ // rest keep the "no diff" placeholder. Log once so it's visible why.
540
+ if (synthesised >= UNTRACKED_DIFF_MAX_FILES || Date.now() > diffDeadline) {
541
+ log(`⚠ untracked diff cap reached @ ${repoPath} (${synthesised}/${untracked.length} synthesised) — rest shown without preview`)
542
+ break
543
+ }
544
+ let st
545
+ try { st = statSync(join(repoPath, file)) } catch { continue }
546
+ if (!st.isFile() || st.size > UNTRACKED_DIFF_MAX_BYTES) continue
547
+ synthesised++
529
548
  const parsed = parseDiffByFile(gitDiffUntracked(file, repoPath))
530
549
  const val = parsed[file] ?? Object.values(parsed)[0]
531
550
  if (val) diffs[file] = val
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitdone-agent",
3
- "version": "0.6.12",
3
+ "version": "0.6.13",
4
4
  "description": "Local git agent for gitdone — watches a local repo and sends snapshots to gitdone.eu",
5
5
  "type": "module",
6
6
  "bin": {