From c882e234454d72033e378b2070369418dd631c23 Mon Sep 17 00:00:00 2001 From: Eddy Date: Mon, 13 Apr 2026 21:10:38 +0200 Subject: [PATCH] Fix: Bridge-Prozess im State speichern (verhindert Drop/Kill) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Child-Objekt wurde am Ende von start_bridge() gedroppt — das schloss die Pipes und beendete den Node-Prozess. Jetzt wird child im ClaudeState gespeichert und lebt solange die App läuft. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/claude-bridge.js | 13 +++++++++++-- src-tauri/src/claude.rs | 16 +++++++++++++++- src/lib/components/ChatPanel.svelte | 9 +-------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/scripts/claude-bridge.js b/scripts/claude-bridge.js index 8897241..ff1642c 100644 --- a/scripts/claude-bridge.js +++ b/scripts/claude-bridge.js @@ -24,6 +24,10 @@ import { readFileSync, writeFileSync, existsSync } from 'node:fs'; import { join } from 'node:path'; import { homedir } from 'node:os'; +// Prozess am Leben halten — MUSS vor allem anderen stehen +const keepAlive = setInterval(() => {}, 60000); +process.stdin.resume(); // stdin offen halten auch ohne readline + // ============ State ============ let client = null; @@ -275,9 +279,14 @@ rl.on('line', (line) => { } }); +// NICHT beenden wenn stdin schließt — wir warten auf weitere Befehle per stdin +rl.on('close', () => { + process.stderr.write('⚠️ stdin geschlossen — Bridge bleibt trotzdem aktiv\n'); +}); + // Sauber beenden -process.on('SIGTERM', () => { process.exit(0); }); -process.on('SIGINT', () => { process.exit(0); }); +process.on('SIGTERM', () => { clearInterval(keepAlive); process.exit(0); }); +process.on('SIGINT', () => { clearInterval(keepAlive); process.exit(0); }); // Bereit-Signal sendEvent('ready', { diff --git a/src-tauri/src/claude.rs b/src-tauri/src/claude.rs index cd7ad08..f3e3166 100644 --- a/src-tauri/src/claude.rs +++ b/src-tauri/src/claude.rs @@ -53,6 +53,7 @@ struct BridgeMessage { /// Globaler State für die Bridge pub struct ClaudeState { + pub bridge_process: Option, pub bridge_stdin: Option, pub request_counter: u64, pub agents: Vec, @@ -61,6 +62,7 @@ pub struct ClaudeState { impl Default for ClaudeState { fn default() -> Self { Self { + bridge_process: None, bridge_stdin: None, request_counter: 0, agents: vec![], @@ -111,14 +113,26 @@ pub fn start_bridge(app: &AppHandle) -> Result<(), String> { let stdin = child.stdin.take().ok_or("Kein stdin verfügbar")?; let stdout = child.stdout.take().ok_or("Kein stdout verfügbar")?; + let stderr = child.stderr.take(); - // State speichern + // State speichern — child MUSS am Leben bleiben! let state = app.state::>>(); { let mut state = state.lock().unwrap(); + state.bridge_process = Some(child); state.bridge_stdin = Some(stdin); } + // Stderr in separatem Thread lesen und loggen + if let Some(stderr) = stderr { + std::thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines().map_while(Result::ok) { + println!("🔌 Bridge stderr: {}", line); + } + }); + } + // Stdout in separatem Thread lesen let app_handle = app.clone(); std::thread::spawn(move || { diff --git a/src/lib/components/ChatPanel.svelte b/src/lib/components/ChatPanel.svelte index fed0c48..ea5c089 100644 --- a/src/lib/components/ChatPanel.svelte +++ b/src/lib/components/ChatPanel.svelte @@ -4,13 +4,8 @@ import { marked } from 'marked'; import { tick } from 'svelte'; - // marked konfigurieren: kein sanitize nötig (lokale App, kein User-Input von extern) - marked.setOptions({ - breaks: true, - gfm: true, - }); + marked.setOptions({ breaks: true, gfm: true }); - // Markdown zu HTML konvertieren function renderMarkdown(text: string): string { try { return marked.parse(text) as string; @@ -19,7 +14,6 @@ } } - // Auto-Scroll zum Ende let messagesContainer: HTMLDivElement; async function scrollToBottom() { @@ -29,7 +23,6 @@ } } - // Bei neuen Nachrichten scrollen $: if ($messages.length) scrollToBottom(); async function sendMessage() {