import fs from "node:fs"; import path from "node:path"; import { listAgentIds, resolveAgentDir } from "../agents/agent-scope.js"; import type { OpenClawConfig } from "../config/config.js"; import { resolveUserPath } from "../utils.js"; import { listAuthProfileStorePaths as listAuthProfileStorePathsFromAuthStorePaths } from "./auth-store-paths.js"; import { parseEnvValue } from "./shared.js"; export function parseEnvAssignmentValue(raw: string): string { return parseEnvValue(raw); } export function listAuthProfileStorePaths(config: OpenClawConfig, stateDir: string): string[] { return listAuthProfileStorePathsFromAuthStorePaths(config, stateDir); } export function listLegacyAuthJsonPaths(stateDir: string): string[] { const out: string[] = []; const agentsRoot = path.join(resolveUserPath(stateDir), "agents"); if (!fs.existsSync(agentsRoot)) { return out; } for (const entry of fs.readdirSync(agentsRoot, { withFileTypes: true })) { if (!entry.isDirectory()) { continue; } const candidate = path.join(agentsRoot, entry.name, "agent", "auth.json"); if (fs.existsSync(candidate)) { out.push(candidate); } } return out; } function resolveActiveAgentDir(stateDir: string, env: NodeJS.ProcessEnv = process.env): string { const override = env.OPENCLAW_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim(); if (override) { return resolveUserPath(override); } return path.join(resolveUserPath(stateDir), "agents", "main", "agent"); } export function listAgentModelsJsonPaths( config: OpenClawConfig, stateDir: string, env: NodeJS.ProcessEnv = process.env, ): string[] { const resolvedStateDir = resolveUserPath(stateDir); const paths = new Set(); paths.add(path.join(resolvedStateDir, "agents", "main", "agent", "models.json")); paths.add(path.join(resolveActiveAgentDir(stateDir, env), "models.json")); const agentsRoot = path.join(resolvedStateDir, "agents"); if (fs.existsSync(agentsRoot)) { for (const entry of fs.readdirSync(agentsRoot, { withFileTypes: true })) { if (!entry.isDirectory()) { continue; } paths.add(path.join(agentsRoot, entry.name, "agent", "models.json")); } } for (const agentId of listAgentIds(config)) { if (agentId === "main") { paths.add(path.join(resolvedStateDir, "agents", "main", "agent", "models.json")); continue; } const agentDir = resolveAgentDir(config, agentId); paths.add(path.join(resolveUserPath(agentDir), "models.json")); } return [...paths]; } export type ReadJsonObjectOptions = { maxBytes?: number; requireRegularFile?: boolean; }; export function readJsonObjectIfExists(filePath: string): { value: Record | null; error?: string; }; export function readJsonObjectIfExists( filePath: string, options: ReadJsonObjectOptions, ): { value: Record | null; error?: string; }; export function readJsonObjectIfExists( filePath: string, options: ReadJsonObjectOptions = {}, ): { value: Record | null; error?: string; } { if (!fs.existsSync(filePath)) { return { value: null }; } try { const stats = fs.statSync(filePath); if (options.requireRegularFile && !stats.isFile()) { return { value: null, error: `Refusing to read non-regular file: ${filePath}`, }; } if ( typeof options.maxBytes === "number" && Number.isFinite(options.maxBytes) && options.maxBytes >= 0 && stats.size > options.maxBytes ) { return { value: null, error: `Refusing to read oversized JSON (${stats.size} bytes): ${filePath}`, }; } const raw = fs.readFileSync(filePath, "utf8"); const parsed = JSON.parse(raw) as unknown; if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { return { value: null }; } return { value: parsed as Record }; } catch (err) { return { value: null, error: err instanceof Error ? err.message : String(err), }; } }