diff --git a/src/frontends/tui/minimal.ts b/src/frontends/tui/minimal.ts index 4d34d44..4634f77 100644 --- a/src/frontends/tui/minimal.ts +++ b/src/frontends/tui/minimal.ts @@ -52,6 +52,7 @@ export class MinimalTui { private totalUsage: TokenUsage = { inputTokens: 0, outputTokens: 0 }; private currentHint = ''; private lastLine = ''; + private backendPids: Map = new Map(); constructor(private config: MinimalTuiConfig) {} @@ -322,24 +323,30 @@ export class MinimalTui { } private async stopBackend(provider: string): Promise { + const pid = this.backendPids.get(provider); + if (!pid) { + return; // No tracked PID, process not started by us + } + try { - const { spawn } = await import('child_process'); - let processName: string; - switch (provider) { - case 'ollama': - processName = 'ollama'; - break; - case 'llamacpp': - processName = 'llama-server'; - break; - default: - return; - } + process.kill(pid, 'SIGTERM'); + // Wait up to 2 seconds for graceful shutdown await new Promise((resolve) => { - spawn('pkill', [processName]).on('close', resolve); + const timeout = setTimeout(resolve, 2000); + const checkInterval = setInterval(() => { + try { + process.kill(pid, 0); // Check if process exists + } catch { + clearInterval(checkInterval); + clearTimeout(timeout); + resolve(); + } + }, 100); }); + this.backendPids.delete(provider); } catch (error) { - // Ignore errors stopping backends + // Process already dead or permission error + this.backendPids.delete(provider); } } @@ -348,18 +355,25 @@ export class MinimalTui { const { spawn } = await import('child_process'); const args: string[] = []; + let proc: ReturnType | undefined; + switch (provider) { case 'ollama': - spawn('ollama', ['serve'], { detached: true, stdio: 'ignore' }).unref(); + proc = spawn('ollama', ['serve'], { detached: true, stdio: 'ignore' }); break; case 'llamacpp': args.push('--model', config.model); args.push('--port', new URL(config.endpoint ?? 'http://localhost:8080').port || '8080'); args.push('--host', '0.0.0.0'); - spawn('llama-server', args, { detached: true, stdio: 'ignore' }).unref(); + proc = spawn('llama-server', args, { detached: true, stdio: 'ignore' }); break; } + if (proc && proc.pid) { + proc.unref(); + this.backendPids.set(provider, proc.pid); + } + // Wait briefly for the daemon to start await new Promise(resolve => setTimeout(resolve, 500)); } catch (error) {