import { readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs"; import { homedir } from "node:os"; import { join, dirname } from "node:path"; export const CONFIG_PATH = join(homedir(), ".neuron", "connectors.json"); const SANDBOX = join(homedir(), "neuron-connectd-sandbox"); // Phase-1 default: one zero-auth filesystem server scoped to the sandbox dir. // Once ~/.neuron/connectors.json exists, it wins. function defaultConfig() { return { servers: { filesystem: { transport: "stdio", enabled: true, command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", SANDBOX], }, }, }; } export function loadConfig() { try { const raw = readFileSync(CONFIG_PATH, "utf8"); const parsed = JSON.parse(raw); if (!parsed.servers || typeof parsed.servers !== "object") { return defaultConfig(); } return parsed; } catch { // No config file yet — run the Phase-1 default so the bridge is useful out of the box. return defaultConfig(); } } // Atomic write of connectors.json (temp + rename), 0600. The bridge owns all writes so the // soul stays simple and El never has to manipulate JSON. UI edits flow: UI → soul route → bridge. export function saveConfig(config) { mkdirSync(dirname(CONFIG_PATH), { recursive: true }); const tmp = `${CONFIG_PATH}.tmp`; writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 0o600 }); renameSync(tmp, CONFIG_PATH); } export const PORT = Number(process.env.NEURON_CONNECTD_PORT ?? 7771); export const HOST = "127.0.0.1"; // loopback only — never bind 0.0.0.0