diff --git a/src/agent-bridge.js b/src/agent-bridge.js index 5fb42be..967ca26 100644 --- a/src/agent-bridge.js +++ b/src/agent-bridge.js @@ -171,12 +171,19 @@ class AgentBridge { } if (session.active && session.process) { - session.process.kill('SIGTERM'); - session.killTimeout = setTimeout(() => { - if (session.active && session.process) { - session.process.kill('SIGKILL'); - } - }, 5000); + // On Windows, kill() without arguments terminates the process + // On Unix, we use SIGTERM followed by SIGKILL after timeout + const isWindows = process.platform === 'win32'; + if (isWindows) { + session.process.kill(); + } else { + session.process.kill('SIGTERM'); + session.killTimeout = setTimeout(() => { + if (session.active && session.process) { + session.process.kill('SIGKILL'); + } + }, 5000); + } } } catch (error) { console.warn(`Error stopping agent session ${sessionId}:`, error.message); diff --git a/src/claude-bridge.js b/src/claude-bridge.js index c7b4fee..4de4d3b 100644 --- a/src/claude-bridge.js +++ b/src/claude-bridge.js @@ -196,13 +196,19 @@ class ClaudeBridge { } if (session.active && session.process) { - session.process.kill('SIGTERM'); - - session.killTimeout = setTimeout(() => { - if (session.active && session.process) { - session.process.kill('SIGKILL'); - } - }, 5000); + // On Windows, kill() without arguments terminates the process + // On Unix, we use SIGTERM followed by SIGKILL after timeout + const isWindows = process.platform === 'win32'; + if (isWindows) { + session.process.kill(); + } else { + session.process.kill('SIGTERM'); + session.killTimeout = setTimeout(() => { + if (session.active && session.process) { + session.process.kill('SIGKILL'); + } + }, 5000); + } } } catch (error) { console.warn(`Error stopping session ${sessionId}:`, error.message); diff --git a/src/codex-bridge.js b/src/codex-bridge.js index 0877a28..71b85ce 100644 --- a/src/codex-bridge.js +++ b/src/codex-bridge.js @@ -177,12 +177,19 @@ class CodexBridge { } if (session.active && session.process) { - session.process.kill('SIGTERM'); - session.killTimeout = setTimeout(() => { - if (session.active && session.process) { - session.process.kill('SIGKILL'); - } - }, 5000); + // On Windows, kill() without arguments terminates the process + // On Unix, we use SIGTERM followed by SIGKILL after timeout + const isWindows = process.platform === 'win32'; + if (isWindows) { + session.process.kill(); + } else { + session.process.kill('SIGTERM'); + session.killTimeout = setTimeout(() => { + if (session.active && session.process) { + session.process.kill('SIGKILL'); + } + }, 5000); + } } } catch (error) { console.warn(`Error stopping codex session ${sessionId}:`, error.message); diff --git a/src/utils/session-store.js b/src/utils/session-store.js index 3e56554..898d265 100644 --- a/src/utils/session-store.js +++ b/src/utils/session-store.js @@ -7,23 +7,34 @@ class SessionStore { // Store sessions in user's home directory this.storageDir = path.join(os.homedir(), '.claude-code-web'); this.sessionsFile = path.join(this.storageDir, 'sessions.json'); - this.initializeStorage(); + this.initialized = false; + this.initPromise = this.initializeStorage(); } async initializeStorage() { try { // Create storage directory if it doesn't exist await fs.mkdir(this.storageDir, { recursive: true }); + this.initialized = true; } catch (error) { console.error('Failed to create storage directory:', error); } } + async ensureInitialized() { + if (!this.initialized) { + await this.initPromise; + } + } + async saveSessions(sessions) { try { + // Ensure initialization is complete + await this.ensureInitialized(); + // Ensure storage directory exists await fs.mkdir(this.storageDir, { recursive: true }); - + // Convert Map to array for JSON serialization const sessionsArray = Array.from(sessions.entries()).map(([id, session]) => ({ id, @@ -53,13 +64,22 @@ class SessionStore { sessions: sessionsArray }; - // Write to a temporary file first, then rename (atomic operation) + const jsonData = JSON.stringify(data, null, 2); + + // Try atomic write (temp file + rename) first const tempFile = `${this.sessionsFile}.tmp`; - await fs.writeFile(tempFile, JSON.stringify(data, null, 2)); - // Ensure directory still exists before rename (handles race conditions) - await fs.mkdir(this.storageDir, { recursive: true }); - await fs.rename(tempFile, this.sessionsFile); - + try { + await fs.writeFile(tempFile, jsonData); + await fs.rename(tempFile, this.sessionsFile); + } catch (renameError) { + // Fallback for Windows: direct write if rename fails + // This can happen due to file locks or cross-device moves + try { + await fs.unlink(tempFile); + } catch (e) { /* ignore cleanup errors */ } + await fs.writeFile(this.sessionsFile, jsonData); + } + return true; } catch (error) { console.error('Failed to save sessions:', error.message); @@ -69,6 +89,9 @@ class SessionStore { async loadSessions() { try { + // Ensure initialization is complete + await this.ensureInitialized(); + // Check if sessions file exists await fs.access(this.sessionsFile);