myrlin-workbook 0.9.17 → 0.9.18

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/package.json +1 -1
  2. package/src/supervisor.js +48 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myrlin-workbook",
3
- "version": "0.9.17",
3
+ "version": "0.9.18",
4
4
  "description": "Browser-based project manager for Claude Code sessions - session discovery, multi-terminal, cost tracking, docs, and kanban board",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/supervisor.js CHANGED
@@ -33,25 +33,57 @@ if (process.argv.includes('--daemon')) {
33
33
  const logDir = path.join(__dirname, '..', 'logs');
34
34
  if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
35
35
  const logFile = path.join(logDir, 'server.log');
36
- const out = fs.openSync(logFile, 'a');
37
- const err = fs.openSync(logFile, 'a');
36
+ const pidFile = path.join(logDir, 'server.pid');
38
37
 
39
38
  // Strip --daemon from args so the child runs in foreground (supervised) mode
40
39
  const childArgs = process.argv.slice(2).filter(a => a !== '--daemon');
41
-
42
- const child = spawn(process.execPath, [__filename, ...childArgs], {
43
- stdio: ['ignore', out, err],
44
- detached: true,
45
- env: { ...process.env },
46
- });
47
-
48
- // Write PID file so other tools can find/stop the server
49
- const pidFile = path.join(logDir, 'server.pid');
50
- fs.writeFileSync(pidFile, String(child.pid), 'utf8');
51
-
52
- child.unref();
53
- console.log(`[supervisor] Daemonized server (PID ${child.pid}), logs at ${logFile}`);
54
- process.exit(0);
40
+ const nodeExe = process.execPath;
41
+ const scriptArgs = [__filename, ...childArgs].map(a => `"${a}"`).join(' ');
42
+
43
+ if (process.platform === 'win32') {
44
+ // On Windows, Node's detached:true still inherits the console session's
45
+ // Job Object. When the parent shell (Git Bash, cmd, Claude Code) exits,
46
+ // Windows kills the entire job group. Use cmd.exe /c start to create a
47
+ // process in a completely new console session, then redirect its output.
48
+ const { execSync } = require('child_process');
49
+ const cmd = `cmd.exe /c start /b "" "${nodeExe}" --max-old-space-size=1024 ${scriptArgs} >> "${logFile}" 2>&1`;
50
+ execSync(cmd, { stdio: 'ignore', windowsHide: true });
51
+
52
+ // The PID isn't directly available from start /b. Write a marker so we
53
+ // can find it via tasklist. Wait briefly for the process to appear.
54
+ setTimeout(() => {
55
+ try {
56
+ const { execSync: es } = require('child_process');
57
+ const psCmd = `powershell.exe -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*supervisor.js*' -and $_.CommandLine -notlike '*--daemon*' } | Select-Object -ExpandProperty ProcessId"`;
58
+ const out = es(psCmd, { encoding: 'utf8', timeout: 10000 });
59
+ const pids = out.trim().split('\n').map(l => l.trim()).filter(Boolean);
60
+ if (pids.length > 0) {
61
+ const pid = pids[pids.length - 1];
62
+ fs.writeFileSync(pidFile, pid, 'utf8');
63
+ console.log(`[supervisor] Daemonized server (PID ${pid}), logs at ${logFile}`);
64
+ } else {
65
+ console.log(`[supervisor] Daemonized server, logs at ${logFile}`);
66
+ }
67
+ } catch (_) {
68
+ console.log(`[supervisor] Daemonized server, logs at ${logFile}`);
69
+ }
70
+ process.exit(0);
71
+ }, 2000);
72
+ } else {
73
+ // Unix: standard detach with file descriptors
74
+ const out = fs.openSync(logFile, 'a');
75
+ const err = fs.openSync(logFile, 'a');
76
+ const child = spawn(nodeExe, ['--max-old-space-size=1024', __filename, ...childArgs], {
77
+ stdio: ['ignore', out, err],
78
+ detached: true,
79
+ env: { ...process.env },
80
+ });
81
+ fs.writeFileSync(pidFile, String(child.pid), 'utf8');
82
+ child.unref();
83
+ console.log(`[supervisor] Daemonized server (PID ${child.pid}), logs at ${logFile}`);
84
+ process.exit(0);
85
+ }
86
+ return; // Guard: don't fall through to supervisor logic while waiting
55
87
  }
56
88
 
57
89
  // ─── EPIPE Protection ────────────────────────────────────