/** * @file network-config.js * @description The dashboard's saved network settings — bind address(es), * allowed Host names and the dashboard token — in `/network.json` * (0600). `agenthub start --host/--allowed-hosts/--token` writes it; every * server start (a restart after an update, the autostart unit, the plugin * bootstrap) applies it with `applyNetworkConfig()`, so a wider bind no longer * falls back to loopback silently the next time something else starts the * server. An environment variable that is set always wins over the file. * @author Nguyễn Ngọc Trí Vĩ */ const fs = require("node:fs"); const path = require("node:path"); const crypto = require("node:crypto"); /** File key → the environment variable it stands in for. */ const KEYS = { host: "DASHBOARD_HOST", allowedHosts: "DASHBOARD_ALLOWED_HOSTS", token: "DASHBOARD_TOKEN", }; function configPath() { const { getDataDir } = require("./claude-home"); return path.join(getDataDir(), "network.json"); } /** The saved settings, `{}` when there are none (or the file is unreadable). */ function readNetworkConfig() { try { const c = JSON.parse(fs.readFileSync(configPath(), "utf8")); if (!c || typeof c !== "object" || Array.isArray(c)) return {}; const out = {}; for (const k of Object.keys(KEYS)) { if (typeof c[k] === "string" && c[k].trim()) out[k] = c[k].trim(); } return out; } catch { return {}; } } /** * Merge `patch` into the saved settings: a string sets a key, `null` removes * it. Written 0600 through a temp file. Returns the new settings. */ function writeNetworkConfig(patch) { const next = { ...readNetworkConfig() }; for (const [k, v] of Object.entries(patch)) { if (!(k in KEYS)) continue; if (v === null || v === "") delete next[k]; else next[k] = String(v).trim(); } const file = configPath(); fs.mkdirSync(path.dirname(file), { recursive: true }); const tmp = `${file}.${process.pid}.tmp`; fs.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 }); fs.chmodSync(tmp, 0o600); fs.renameSync(tmp, file); return next; } /** Put every saved setting whose environment variable is unset into `env`. */ function applyNetworkConfig(env = process.env) { const saved = readNetworkConfig(); for (const [k, name] of Object.entries(KEYS)) { if (saved[k] && !(typeof env[name] === "string" && env[name].trim())) env[name] = saved[k]; } return saved; } /** A fresh random token for `--token auto`. */ const newToken = () => crypto.randomBytes(24).toString("base64url"); module.exports = { configPath, readNetworkConfig, writeNetworkConfig, applyNetworkConfig, newToken, KEYS, };