shraga 0.1.84 → 0.1.85

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.84",
3
+ "version": "0.1.85",
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",
@@ -20,7 +20,12 @@ export function reclaimStalePort(port: number, opts: { entrypoint?: string; cwd?
20
20
  const cwd = opts.cwd ?? process.cwd();
21
21
  const grace = opts.termGraceMs ?? 5000;
22
22
 
23
- const holders = listeners(port).filter((pid) => pid !== process.pid && isStaleSelf(pid, entrypoint, cwd));
23
+ // Any state, not just LISTEN: an orphan that closed its listener inside a drain it never finished
24
+ // still OWNS the socket, and looking only for LISTEN made it invisible while it blocked every
25
+ // replacement (feedox 2026-09-06: ppid-1 orphan, listener closed, port held for six minutes and
26
+ // counting). `isStaleSelf` is what keeps this narrow — a bystander is never a candidate.
27
+ const holders = [...new Set([...listeners(port), ...portUsers(port)])]
28
+ .filter((pid) => pid !== process.pid && isStaleSelf(pid, entrypoint, cwd));
24
29
  if (!holders.length) return false;
25
30
 
26
31
  for (const pid of holders) {
@@ -32,11 +37,17 @@ export function reclaimStalePort(port: number, opts: { entrypoint?: string; cwd?
32
37
  waitGone(pid, 2000);
33
38
  }
34
39
  }
35
- const free = listeners(port).filter((pid) => pid !== process.pid).length === 0;
40
+ const free = [...new Set([...listeners(port), ...portUsers(port)])].filter((pid) => pid !== process.pid).length === 0;
36
41
  console.warn(`[server] port ${port} ${free ? 'reclaimed' : 'STILL held after reclaim'}`);
37
42
  return free;
38
43
  }
39
44
 
45
+ /** Every process holding the port in ANY socket state (a half-closed orphan included). */
46
+ function portUsers(port: number): number[] {
47
+ return sh('lsof', ['-ti', `tcp:${port}`])
48
+ .split('\n').map((l) => Number(l.trim())).filter((n) => Number.isInteger(n) && n > 0);
49
+ }
50
+
40
51
  function listeners(port: number): number[] {
41
52
  return sh('lsof', ['-ti', `tcp:${port}`, '-sTCP:LISTEN'])
42
53
  .split('\n').map((l) => Number(l.trim())).filter((n) => Number.isInteger(n) && n > 0);