import fs from "node:fs"; import path from "node:path"; import os from "node:os"; import type { AgenticROSConfig } from "@agenticros/core"; import { parseConfig, withResolvedSkillRefs, applyCachedSkillRefs } from "@agenticros/core"; /** Same as Claude Code MCP: optional `robot.namespace` override from the environment. */ function applyMcpEnvOverrides(config: AgenticROSConfig): AgenticROSConfig { const ns = process.env.AGENTICROS_ROBOT_NAMESPACE?.trim(); if (!ns) return config; return { ...config, robot: { ...config.robot, namespace: ns, }, }; } function getConfigPath(): string { const env = process.env.AGENTICROS_CONFIG_PATH; if (env && env.trim().length > 0) { return path.resolve(env); } return path.join(os.homedir(), ".agenticros", "config.json"); } function tryOpenClawConfig(): Record | null { const openclawEnv = process.env.OPENCLAW_CONFIG; const openclawPath = openclawEnv && openclawEnv.trim().length > 0 ? path.resolve(openclawEnv) : path.join(os.homedir(), ".openclaw", "openclaw.json"); try { const raw = fs.readFileSync(openclawPath, "utf8"); const parsed = JSON.parse(raw) as Record; const entries = parsed?.plugins && typeof (parsed.plugins as Record).entries === "object" ? (parsed.plugins as Record).entries as Record : null; const agenticros = entries?.agenticros && typeof entries.agenticros === "object" ? entries.agenticros as Record : null; const config = agenticros?.config; if (config !== null && typeof config === "object") { return config as Record; } } catch { // ignore } return null; } function loadConfigSync(): AgenticROSConfig { const primaryPath = getConfigPath(); try { const raw = fs.readFileSync(primaryPath, "utf8"); const parsed = JSON.parse(raw) as unknown; if (parsed !== null && typeof parsed === "object") { const cfg = parseConfig(parsed as Record); if (process.stderr && typeof process.stderr.write === "function") { process.stderr.write(`[AgenticROS] Config from ${primaryPath}\n`); } return applyMcpEnvOverrides(applyCachedSkillRefs(cfg)); } } catch (err) { const nodeErr = err as NodeJS.ErrnoException; if (nodeErr.code === "ENOENT") { const openclawConfig = tryOpenClawConfig(); if (openclawConfig) { return applyMcpEnvOverrides(applyCachedSkillRefs(parseConfig(openclawConfig))); } throw new Error( `AgenticROS config not found at ${primaryPath}. Create it or set AGENTICROS_CONFIG_PATH. ` + "See README for an example config.", ); } throw err; } return applyMcpEnvOverrides(applyCachedSkillRefs(parseConfig({}))); } export function loadConfig(): AgenticROSConfig { return loadConfigSync(); } export async function loadConfigAsync(): Promise { const base = loadConfigSync(); if (!(base.skillRefs?.length)) return base; const { config, errors } = await withResolvedSkillRefs(base, { onLog: (msg) => { if (process.stderr && typeof process.stderr.write === "function") { process.stderr.write(`[AgenticROS] ${msg}\n`); } }, }); for (const e of errors) { if (process.stderr && typeof process.stderr.write === "function") { process.stderr.write(`[AgenticROS] skillRef ${e.ref}: ${e.error}\n`); } } return applyMcpEnvOverrides(config); }