UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

236 lines (233 loc) 9.12 kB
import fs from "node:fs"; import os from "node:os"; import path from "node:path"; //#region src/infra/home-dir.ts function normalize(value) { const trimmed = value?.trim(); return trimmed ? trimmed : void 0; } function resolveEffectiveHomeDir(env = process.env, homedir = os.homedir) { const raw = resolveRawHomeDir(env, homedir); return raw ? path.resolve(raw) : void 0; } function resolveRawHomeDir(env, homedir) { const explicitHome = normalize(env.OPENCLAW_HOME); if (explicitHome) { if (explicitHome === "~" || explicitHome.startsWith("~/") || explicitHome.startsWith("~\\")) { const fallbackHome = normalize(env.HOME) ?? normalize(env.USERPROFILE) ?? normalizeSafe(homedir); if (fallbackHome) return explicitHome.replace(/^~(?=$|[\\/])/, fallbackHome); return; } return explicitHome; } const envHome = normalize(env.HOME); if (envHome) return envHome; const userProfile = normalize(env.USERPROFILE); if (userProfile) return userProfile; return normalizeSafe(homedir); } function normalizeSafe(homedir) { try { return normalize(homedir()); } catch { return; } } function resolveRequiredHomeDir(env = process.env, homedir = os.homedir) { return resolveEffectiveHomeDir(env, homedir) ?? path.resolve(process.cwd()); } function expandHomePrefix(input, opts) { if (!input.startsWith("~")) return input; const home = normalize(opts?.home) ?? resolveEffectiveHomeDir(opts?.env ?? process.env, opts?.homedir ?? os.homedir); if (!home) return input; return input.replace(/^~(?=$|[\\/])/, home); } //#endregion //#region src/config/paths.ts /** * Nix mode detection: When OPENCLAW_NIX_MODE=1, the gateway is running under Nix. * In this mode: * - No auto-install flows should be attempted * - Missing dependencies should produce actionable Nix-specific error messages * - Config is managed externally (read-only from Nix perspective) */ function resolveIsNixMode(env = process.env) { return env.OPENCLAW_NIX_MODE === "1"; } const isNixMode = resolveIsNixMode(); const LEGACY_STATE_DIRNAMES = [ ".clawdbot", ".moldbot", ".moltbot" ]; const NEW_STATE_DIRNAME = ".openclaw"; const CONFIG_FILENAME = "openclaw.json"; const LEGACY_CONFIG_FILENAMES = [ "clawdbot.json", "moldbot.json", "moltbot.json" ]; function resolveDefaultHomeDir() { return resolveRequiredHomeDir(process.env, os.homedir); } /** Build a homedir thunk that respects OPENCLAW_HOME for the given env. */ function envHomedir(env) { return () => resolveRequiredHomeDir(env, os.homedir); } function legacyStateDirs(homedir = resolveDefaultHomeDir) { return LEGACY_STATE_DIRNAMES.map((dir) => path.join(homedir(), dir)); } function newStateDir(homedir = resolveDefaultHomeDir) { return path.join(homedir(), NEW_STATE_DIRNAME); } function resolveLegacyStateDirs(homedir = resolveDefaultHomeDir) { return legacyStateDirs(homedir); } function resolveNewStateDir(homedir = resolveDefaultHomeDir) { return newStateDir(homedir); } /** * State directory for mutable data (sessions, logs, caches). * Can be overridden via OPENCLAW_STATE_DIR. * Default: ~/.openclaw */ function resolveStateDir(env = process.env, homedir = envHomedir(env)) { const effectiveHomedir = () => resolveRequiredHomeDir(env, homedir); const override = env.OPENCLAW_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim(); if (override) return resolveUserPath(override, env, effectiveHomedir); const newDir = newStateDir(effectiveHomedir); const legacyDirs = legacyStateDirs(effectiveHomedir); if (fs.existsSync(newDir)) return newDir; const existingLegacy = legacyDirs.find((dir) => { try { return fs.existsSync(dir); } catch { return false; } }); if (existingLegacy) return existingLegacy; return newDir; } function resolveUserPath(input, env = process.env, homedir = envHomedir(env)) { const trimmed = input.trim(); if (!trimmed) return trimmed; if (trimmed.startsWith("~")) { const expanded = expandHomePrefix(trimmed, { home: resolveRequiredHomeDir(env, homedir), env, homedir }); return path.resolve(expanded); } return path.resolve(trimmed); } const STATE_DIR = resolveStateDir(); /** * Config file path (JSON5). * Can be overridden via OPENCLAW_CONFIG_PATH. * Default: ~/.openclaw/openclaw.json (or $OPENCLAW_STATE_DIR/openclaw.json) */ function resolveCanonicalConfigPath(env = process.env, stateDir = resolveStateDir(env, envHomedir(env))) { const override = env.OPENCLAW_CONFIG_PATH?.trim() || env.CLAWDBOT_CONFIG_PATH?.trim(); if (override) return resolveUserPath(override, env, envHomedir(env)); return path.join(stateDir, CONFIG_FILENAME); } /** * Resolve the active config path by preferring existing config candidates * before falling back to the canonical path. */ function resolveConfigPathCandidate(env = process.env, homedir = envHomedir(env)) { const existing = resolveDefaultConfigCandidates(env, homedir).find((candidate) => { try { return fs.existsSync(candidate); } catch { return false; } }); if (existing) return existing; return resolveCanonicalConfigPath(env, resolveStateDir(env, homedir)); } /** * Active config path (prefers existing config files). */ function resolveConfigPath(env = process.env, stateDir = resolveStateDir(env, envHomedir(env)), homedir = envHomedir(env)) { const override = env.OPENCLAW_CONFIG_PATH?.trim(); if (override) return resolveUserPath(override, env, homedir); const stateOverride = env.OPENCLAW_STATE_DIR?.trim(); const existing = [path.join(stateDir, CONFIG_FILENAME), ...LEGACY_CONFIG_FILENAMES.map((name) => path.join(stateDir, name))].find((candidate) => { try { return fs.existsSync(candidate); } catch { return false; } }); if (existing) return existing; if (stateOverride) return path.join(stateDir, CONFIG_FILENAME); const defaultStateDir = resolveStateDir(env, homedir); if (path.resolve(stateDir) === path.resolve(defaultStateDir)) return resolveConfigPathCandidate(env, homedir); return path.join(stateDir, CONFIG_FILENAME); } const CONFIG_PATH = resolveConfigPathCandidate(); /** * Resolve default config path candidates across default locations. * Order: explicit config path → state-dir-derived paths → new default. */ function resolveDefaultConfigCandidates(env = process.env, homedir = envHomedir(env)) { const effectiveHomedir = () => resolveRequiredHomeDir(env, homedir); const explicit = env.OPENCLAW_CONFIG_PATH?.trim() || env.CLAWDBOT_CONFIG_PATH?.trim(); if (explicit) return [resolveUserPath(explicit, env, effectiveHomedir)]; const candidates = []; const openclawStateDir = env.OPENCLAW_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim(); if (openclawStateDir) { const resolved = resolveUserPath(openclawStateDir, env, effectiveHomedir); candidates.push(path.join(resolved, CONFIG_FILENAME)); candidates.push(...LEGACY_CONFIG_FILENAMES.map((name) => path.join(resolved, name))); } const defaultDirs = [newStateDir(effectiveHomedir), ...legacyStateDirs(effectiveHomedir)]; for (const dir of defaultDirs) { candidates.push(path.join(dir, CONFIG_FILENAME)); candidates.push(...LEGACY_CONFIG_FILENAMES.map((name) => path.join(dir, name))); } return candidates; } const DEFAULT_GATEWAY_PORT = 18789; /** * Gateway lock directory (ephemeral). * Default: os.tmpdir()/openclaw-<uid> (uid suffix when available). */ function resolveGatewayLockDir(tmpdir = os.tmpdir) { const base = tmpdir(); const uid = typeof process.getuid === "function" ? process.getuid() : void 0; const suffix = uid != null ? `openclaw-${uid}` : "openclaw"; return path.join(base, suffix); } const OAUTH_FILENAME = "oauth.json"; /** * OAuth credentials storage directory. * * Precedence: * - `OPENCLAW_OAUTH_DIR` (explicit override) * - `$*_STATE_DIR/credentials` (canonical server/default) */ function resolveOAuthDir(env = process.env, stateDir = resolveStateDir(env, envHomedir(env))) { const override = env.OPENCLAW_OAUTH_DIR?.trim(); if (override) return resolveUserPath(override, env, envHomedir(env)); return path.join(stateDir, "credentials"); } function resolveOAuthPath(env = process.env, stateDir = resolveStateDir(env, envHomedir(env))) { return path.join(resolveOAuthDir(env, stateDir), OAUTH_FILENAME); } function resolveGatewayPort(cfg, env = process.env) { const envRaw = env.OPENCLAW_GATEWAY_PORT?.trim() || env.CLAWDBOT_GATEWAY_PORT?.trim(); if (envRaw) { const parsed = Number.parseInt(envRaw, 10); if (Number.isFinite(parsed) && parsed > 0) return parsed; } const configPort = cfg?.gateway?.port; if (typeof configPort === "number" && Number.isFinite(configPort)) { if (configPort > 0) return configPort; } return DEFAULT_GATEWAY_PORT; } //#endregion export { expandHomePrefix as _, resolveCanonicalConfigPath as a, resolveDefaultConfigCandidates as c, resolveIsNixMode as d, resolveLegacyStateDirs as f, resolveStateDir as g, resolveOAuthPath as h, isNixMode as i, resolveGatewayLockDir as l, resolveOAuthDir as m, DEFAULT_GATEWAY_PORT as n, resolveConfigPath as o, resolveNewStateDir as p, STATE_DIR as r, resolveConfigPathCandidate as s, CONFIG_PATH as t, resolveGatewayPort as u, resolveEffectiveHomeDir as v, resolveRequiredHomeDir as y };