import crypto from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; import { resolveStateDir } from "../config/paths.js"; import { writeJsonAtomic } from "../infra/json-files.js"; export type NodeHostGatewayConfig = { host?: string; port?: number; tls?: boolean; tlsFingerprint?: string; }; export type NodeHostConfig = { version: 1; nodeId: string; token?: string; displayName?: string; gateway?: NodeHostGatewayConfig; }; const NODE_HOST_FILE = "node.json"; export function resolveNodeHostConfigPath(): string { return path.join(resolveStateDir(), NODE_HOST_FILE); } function normalizeConfig(config: Partial | null): NodeHostConfig { const base: NodeHostConfig = { version: 1, nodeId: "", token: config?.token, displayName: config?.displayName, gateway: config?.gateway, }; if (config?.version === 1 && typeof config.nodeId === "string") { base.nodeId = config.nodeId.trim(); } if (!base.nodeId) { base.nodeId = crypto.randomUUID(); } return base; } export async function loadNodeHostConfig(): Promise { const filePath = resolveNodeHostConfigPath(); try { const raw = await fs.readFile(filePath, "utf8"); const parsed = JSON.parse(raw) as Partial; return normalizeConfig(parsed); } catch { return null; } } export async function saveNodeHostConfig(config: NodeHostConfig): Promise { const filePath = resolveNodeHostConfigPath(); await writeJsonAtomic(filePath, config, { mode: 0o600 }); } export async function ensureNodeHostConfig(): Promise { const existing = await loadNodeHostConfig(); const normalized = normalizeConfig(existing); await saveNodeHostConfig(normalized); return normalized; }