Compare commits
2 commits
c78e7f3dcc
...
c882e23445
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c882e23445 | ||
|
|
59e5b2ad77 |
3 changed files with 35 additions and 11 deletions
|
|
@ -24,6 +24,10 @@ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { homedir } from 'node:os';
|
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 ============
|
// ============ State ============
|
||||||
|
|
||||||
let client = null;
|
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
|
// Sauber beenden
|
||||||
process.on('SIGTERM', () => { process.exit(0); });
|
process.on('SIGTERM', () => { clearInterval(keepAlive); process.exit(0); });
|
||||||
process.on('SIGINT', () => { process.exit(0); });
|
process.on('SIGINT', () => { clearInterval(keepAlive); process.exit(0); });
|
||||||
|
|
||||||
// Bereit-Signal
|
// Bereit-Signal
|
||||||
sendEvent('ready', {
|
sendEvent('ready', {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ struct BridgeMessage {
|
||||||
|
|
||||||
/// Globaler State für die Bridge
|
/// Globaler State für die Bridge
|
||||||
pub struct ClaudeState {
|
pub struct ClaudeState {
|
||||||
|
pub bridge_process: Option<std::process::Child>,
|
||||||
pub bridge_stdin: Option<std::process::ChildStdin>,
|
pub bridge_stdin: Option<std::process::ChildStdin>,
|
||||||
pub request_counter: u64,
|
pub request_counter: u64,
|
||||||
pub agents: Vec<AgentStatus>,
|
pub agents: Vec<AgentStatus>,
|
||||||
|
|
@ -61,6 +62,7 @@ pub struct ClaudeState {
|
||||||
impl Default for ClaudeState {
|
impl Default for ClaudeState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
bridge_process: None,
|
||||||
bridge_stdin: None,
|
bridge_stdin: None,
|
||||||
request_counter: 0,
|
request_counter: 0,
|
||||||
agents: vec![],
|
agents: vec![],
|
||||||
|
|
@ -93,8 +95,16 @@ pub fn start_bridge(app: &AppHandle) -> Result<(), String> {
|
||||||
|
|
||||||
println!("🔌 Starte Claude Bridge: {:?}", script_path);
|
println!("🔌 Starte Claude Bridge: {:?}", script_path);
|
||||||
|
|
||||||
|
// Arbeitsverzeichnis = Projektroot (wo node_modules liegt)
|
||||||
|
let project_dir = script_path.parent()
|
||||||
|
.and_then(|p| p.parent())
|
||||||
|
.unwrap_or_else(|| std::path::Path::new("."));
|
||||||
|
|
||||||
|
println!("📂 Bridge Arbeitsverzeichnis: {:?}", project_dir);
|
||||||
|
|
||||||
let mut child = Command::new("node")
|
let mut child = Command::new("node")
|
||||||
.arg(&script_path)
|
.arg(&script_path)
|
||||||
|
.current_dir(project_dir)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
|
|
@ -103,14 +113,26 @@ pub fn start_bridge(app: &AppHandle) -> Result<(), String> {
|
||||||
|
|
||||||
let stdin = child.stdin.take().ok_or("Kein stdin verfügbar")?;
|
let stdin = child.stdin.take().ok_or("Kein stdin verfügbar")?;
|
||||||
let stdout = child.stdout.take().ok_or("Kein stdout 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::<Arc<Mutex<ClaudeState>>>();
|
let state = app.state::<Arc<Mutex<ClaudeState>>>();
|
||||||
{
|
{
|
||||||
let mut state = state.lock().unwrap();
|
let mut state = state.lock().unwrap();
|
||||||
|
state.bridge_process = Some(child);
|
||||||
state.bridge_stdin = Some(stdin);
|
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
|
// Stdout in separatem Thread lesen
|
||||||
let app_handle = app.clone();
|
let app_handle = app.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,8 @@
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import { tick } from 'svelte';
|
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 {
|
function renderMarkdown(text: string): string {
|
||||||
try {
|
try {
|
||||||
return marked.parse(text) as string;
|
return marked.parse(text) as string;
|
||||||
|
|
@ -19,7 +14,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-Scroll zum Ende
|
|
||||||
let messagesContainer: HTMLDivElement;
|
let messagesContainer: HTMLDivElement;
|
||||||
|
|
||||||
async function scrollToBottom() {
|
async function scrollToBottom() {
|
||||||
|
|
@ -29,7 +23,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bei neuen Nachrichten scrollen
|
|
||||||
$: if ($messages.length) scrollToBottom();
|
$: if ($messages.length) scrollToBottom();
|
||||||
|
|
||||||
async function sendMessage() {
|
async function sendMessage() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue