diff --git a/src/bridge.ts b/src/bridge.ts index 2964d5c..390766a 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -5,6 +5,8 @@ import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"; import { createHash } from "node:crypto"; import { join, dirname } from "node:path"; import { spawn, type ChildProcess } from "node:child_process"; +import { mkdirSync } from "node:fs"; +import { homedir } from "node:os"; import type { ConnectorsConfig, ServerConfig } from "./config.js"; import { saveConfig } from "./config.js"; import { KeychainOAuthProvider, takePendingAuthUrl } from "./oauth.js"; @@ -190,10 +192,15 @@ export class ConnectorBridge { }; if (await ping()) return; // already running (this session or a prior one) if (this.companions.has(id)) return; // we already started it + let cwd: string | undefined; + if (comp.cwd) { + cwd = comp.cwd.replace(/^~(?=\/|$)/, homedir()).replace(/^\$HOME(?=\/|$)/, homedir()); + try { mkdirSync(cwd, { recursive: true }); } catch { /* best effort — spawn will surface a real failure */ } + } const child = spawn( resolveRuntimeCommand(comp.command), (comp.args ?? []).map(resolveBundledArg), - { env: { ...process.env, ...(comp.env ?? {}) } as Record, stdio: "ignore" }, + { env: { ...process.env, ...(comp.env ?? {}) } as Record, stdio: "ignore", cwd }, ); this.companions.set(id, child); child.on("exit", () => this.companions.delete(id)); diff --git a/src/config.ts b/src/config.ts index c8817c0..0e3f7d1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -20,6 +20,10 @@ export interface ServerConfig { args?: string[]; env?: Record; healthUrl?: string; + // Working directory for the sidecar (created if missing). The WhatsApp bridge writes its session + // store relative to cwd, so this must be writable (e.g. ~/.neuron/whatsapp) — never the read-only + // app bundle. "~" / "$HOME" at the start are expanded to the user's home. + cwd?: string; }; // http (Phase 3) url?: string;