shraga 0.1.49 → 0.1.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shraga",
3
- "version": "0.1.49",
3
+ "version": "0.1.50",
4
4
  "description": "The teammate you delegate coding to — a self-hostable, multi-user AI coding agent web UI (Claude Code, with a pluggable engine seam).",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs';
2
+ import { readFile, stat } from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import { hostname } from 'node:os';
4
5
  import { DATA_DIR } from './paths.ts';
@@ -116,10 +117,12 @@ export class DataSync {
116
117
  // Defer heavy sync I/O (reads all tracked files + execSync) to avoid blocking
117
118
  // WS connections and page loads during startup.
118
119
  setTimeout(() => {
119
- this.scanForConflictMarkers().catch(err => {
120
- console.warn(`${TAG} Post-init conflict scan failed:`, (err as Error).message);
121
- });
122
- this.runIntegrityAudit();
120
+ this.scanForConflictMarkers()
121
+ .catch(err => console.warn(`${TAG} Post-init conflict scan failed:`, (err as Error).message))
122
+ // execSync inside the audit blocks too — keep it off the scan's tick so the two
123
+ // never add up into one long freeze.
124
+ .then(() => new Promise<void>(r => setImmediate(r)))
125
+ .then(() => this.runIntegrityAudit());
123
126
  }, 60_000);
124
127
  }
125
128
 
@@ -442,13 +445,20 @@ export class DataSync {
442
445
  try {
443
446
  const tracked = (await this.git('ls-files')).split('\n').filter(Boolean);
444
447
  const conflicted: string[] = [];
445
- for (const f of tracked) {
446
- const abs = path.join(DATA_DIR, f);
448
+ // ASYNC + YIELDING on purpose. This walks every tracked file (1000+, GBs on a real
449
+ // deployment); doing it synchronously froze the event loop for ~45s — the server stopped
450
+ // answering /api/version entirely, which also made self-upgrade verification fail and
451
+ // auto-revert healthy versions. Deferring a synchronous freeze only moves it; it has to
452
+ // not block at all.
453
+ for (let i = 0; i < tracked.length; i++) {
454
+ const abs = path.join(DATA_DIR, tracked[i]);
447
455
  try {
448
- if (!existsSync(abs) || statSync(abs).isDirectory()) continue;
449
- const content = readFileSync(abs, 'utf-8');
450
- if (/^<{7} /m.test(content)) conflicted.push(f);
456
+ const info = await stat(abs).catch(() => null);
457
+ if (!info || info.isDirectory()) continue;
458
+ const content = await readFile(abs, 'utf-8');
459
+ if (/^<{7} /m.test(content)) conflicted.push(tracked[i]);
451
460
  } catch { /* skip unreadable */ }
461
+ if (i % 25 === 24) await new Promise<void>(r => setImmediate(r)); // let the server breathe
452
462
  }
453
463
  if (!conflicted.length) return;
454
464