feat(connectd): companion cwd (writable session dir for the WhatsApp bridge)
companion.cwd (created if missing; ~/$HOME expanded) sets the sidecar's working directory so the WhatsApp bridge writes its session store to a writable path (~/.neuron/whatsapp), never the read-only app bundle. Typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -5,6 +5,8 @@ import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { join, dirname } from "node:path";
|
import { join, dirname } from "node:path";
|
||||||
import { spawn, type ChildProcess } from "node:child_process";
|
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 type { ConnectorsConfig, ServerConfig } from "./config.js";
|
||||||
import { saveConfig } from "./config.js";
|
import { saveConfig } from "./config.js";
|
||||||
import { KeychainOAuthProvider, takePendingAuthUrl } from "./oauth.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 (await ping()) return; // already running (this session or a prior one)
|
||||||
if (this.companions.has(id)) return; // we already started it
|
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(
|
const child = spawn(
|
||||||
resolveRuntimeCommand(comp.command),
|
resolveRuntimeCommand(comp.command),
|
||||||
(comp.args ?? []).map(resolveBundledArg),
|
(comp.args ?? []).map(resolveBundledArg),
|
||||||
{ env: { ...process.env, ...(comp.env ?? {}) } as Record<string, string>, stdio: "ignore" },
|
{ env: { ...process.env, ...(comp.env ?? {}) } as Record<string, string>, stdio: "ignore", cwd },
|
||||||
);
|
);
|
||||||
this.companions.set(id, child);
|
this.companions.set(id, child);
|
||||||
child.on("exit", () => this.companions.delete(id));
|
child.on("exit", () => this.companions.delete(id));
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ export interface ServerConfig {
|
|||||||
args?: string[];
|
args?: string[];
|
||||||
env?: Record<string, string>;
|
env?: Record<string, string>;
|
||||||
healthUrl?: string;
|
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)
|
// http (Phase 3)
|
||||||
url?: string;
|
url?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user