From 541b6eda46eab5ee1f73a764738fc0d6d6dbc0b3 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Thu, 26 Mar 2026 02:04:02 +0100 Subject: [PATCH] fix(electrobun): sidecar runner path resolution for dev build output + debug logging --- ui-electrobun/src/bun/sidecar-manager.ts | 46 ++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/ui-electrobun/src/bun/sidecar-manager.ts b/ui-electrobun/src/bun/sidecar-manager.ts index 2e7ab92..cdbc7c0 100644 --- a/ui-electrobun/src/bun/sidecar-manager.ts +++ b/ui-electrobun/src/bun/sidecar-manager.ts @@ -3,9 +3,18 @@ import { join } from "path"; import { homedir } from "os"; -import { existsSync } from "fs"; +import { existsSync, appendFileSync, mkdirSync } from "fs"; import { parseMessage, type AgentMessage, type ProviderId } from "./message-adapter.ts"; +// Debug log to file (always on in dev, check AGOR_DEBUG for prod) +const DEBUG_LOG = join(homedir(), ".local", "share", "agor", "sidecar-debug.log"); +try { mkdirSync(join(homedir(), ".local", "share", "agor"), { recursive: true }); } catch {} +function dbg(msg: string) { + const line = `[${new Date().toISOString()}] ${msg}\n`; + try { appendFileSync(DEBUG_LOG, line); } catch {} + console.log(`[sidecar] ${msg}`); +} + // ── Types ──────────────────────────────────────────────────────────────────── export type SessionStatus = "running" | "idle" | "done" | "error"; @@ -111,8 +120,29 @@ function findClaudeCli(): string | undefined { // ── Runner resolution ──────────────────────────────────────────────────────── function resolveRunnerPath(provider: ProviderId): string { - const repoRoot = join(import.meta.dir, "..", "..", ".."); - return join(repoRoot, "sidecar", "dist", `${provider}-runner.mjs`); + // In dev mode, import.meta.dir is inside the Electrobun build output, + // not the source tree. Walk up until we find sidecar/dist/ or use env var. + const envRoot = process.env.AGOR_ROOT; + if (envRoot) return join(envRoot, "sidecar", "dist", `${provider}-runner.mjs`); + + // Try multiple candidate roots (dev build output vs source tree) + const candidates = [ + join(import.meta.dir, "..", "..", ".."), // source: src/bun/ → repo root + join(import.meta.dir, "..", "..", "..", "..", "..", ".."), // build: build/dev-linux-x64/App/... → repo root + ]; + + for (const root of candidates) { + const path = join(root, "sidecar", "dist", `${provider}-runner.mjs`); + if (existsSync(path)) { + dbg(`Runner found at: ${path} (root: ${root})`); + return path; + } + } + + // Fallback: try finding from cwd + const cwdPath = join(process.cwd(), "sidecar", "dist", `${provider}-runner.mjs`); + dbg(`Trying cwd fallback: ${cwdPath}`); + return cwdPath; } function findNodeRuntime(): string { @@ -175,12 +205,15 @@ export class SidecarManager { } const runnerPath = resolveRunnerPath(provider); + dbg(`startSession: id=${sessionId} provider=${provider} runner=${runnerPath}`); if (!existsSync(runnerPath)) { + dbg(`ERROR: runner not found at ${runnerPath}`); return { ok: false, error: `Runner not found: ${runnerPath}` }; } const controller = new AbortController(); const env = buildCleanEnv(options.extraEnv, options.claudeConfigDir); + dbg(`Spawning: ${this.nodeRuntime} ${runnerPath} cwd=${options.cwd || 'default'}`); const proc = Bun.spawn([this.nodeRuntime, runnerPath], { stdin: "pipe", @@ -218,6 +251,7 @@ export class SidecarManager { // Monitor process exit proc.exited.then((exitCode) => { + dbg(`Process exited: session=${sessionId} code=${exitCode}`); const s = this.sessions.get(sessionId); if (s) { s.state.status = exitCode === 0 ? "done" : "error"; @@ -248,8 +282,10 @@ export class SidecarManager { queryMsg.worktreeName = options.worktreeName; } + dbg(`Sending query: ${JSON.stringify(queryMsg).slice(0, 200)}...`); this.writeToProcess(sessionId, queryMsg); + dbg(`Session ${sessionId} started successfully`); return { ok: true }; } @@ -419,6 +455,7 @@ export class SidecarManager { const text = decoder.decode(chunk, { stream: true }); for (const line of text.split("\n")) { if (line.trim()) { + dbg(`STDERR [${sessionId}]: ${line.trim()}`); console.log(`[sidecar:${sessionId}] ${line.trim()}`); } } @@ -429,16 +466,19 @@ export class SidecarManager { } private handleNdjsonLine(sessionId: string, session: ActiveSession, line: string): void { + dbg(`NDJSON [${sessionId}]: ${line.slice(0, 200)}`); let raw: Record; try { raw = JSON.parse(line); } catch { + dbg(`Invalid JSON from ${sessionId}: ${line.slice(0, 100)}`); console.warn(`[sidecar] Invalid JSON from ${sessionId}: ${line.slice(0, 100)}`); return; } // Handle sidecar-level events (not forwarded to message adapter) const type = raw.type; + dbg(`Event type: ${type} for ${sessionId}`); if (type === "ready" || type === "pong") return; if (type === "agent_started") {