import os from "node:os"; import path from "node:path"; export type CliProfileParseResult = | { ok: true; profile: string | null; argv: string[] } | { ok: false; error: string }; function takeValue( raw: string, next: string | undefined, ): { value: string | null; consumedNext: boolean; } { if (raw.includes("=")) { const [, value] = raw.split("=", 2); const trimmed = (value ?? "").trim(); return { value: trimmed || null, consumedNext: false }; } const trimmed = (next ?? "").trim(); return { value: trimmed || null, consumedNext: Boolean(next) }; } function isValidProfileName(value: string): boolean { if (!value) return false; // Keep it path-safe + shell-friendly. return /^[a-z0-9][a-z0-9_-]{0,63}$/i.test(value); } export function parseCliProfileArgs(argv: string[]): CliProfileParseResult { if (argv.length < 2) return { ok: true, profile: null, argv }; const out: string[] = argv.slice(0, 2); let profile: string | null = null; let sawDev = false; let sawCommand = false; const args = argv.slice(2); for (let i = 0; i < args.length; i += 1) { const arg = args[i]; if (arg === undefined) continue; if (sawCommand) { out.push(arg); continue; } if (arg === "--dev") { if (profile && profile !== "dev") { return { ok: false, error: "Cannot combine --dev with --profile" }; } sawDev = true; profile = "dev"; continue; } if (arg === "--profile" || arg.startsWith("--profile=")) { if (sawDev) { return { ok: false, error: "Cannot combine --dev with --profile" }; } const next = args[i + 1]; const { value, consumedNext } = takeValue(arg, next); if (consumedNext) i += 1; if (!value) return { ok: false, error: "--profile requires a value" }; if (!isValidProfileName(value)) { return { ok: false, error: 'Invalid --profile (use letters, numbers, "_", "-" only)', }; } profile = value; continue; } if (!arg.startsWith("-")) { sawCommand = true; out.push(arg); continue; } out.push(arg); } return { ok: true, profile, argv: out }; } function resolveProfileStateDir( profile: string, homedir: () => string, ): string { const suffix = profile.toLowerCase() === "default" ? "" : `-${profile}`; return path.join(homedir(), `.clawdbot${suffix}`); } export function applyCliProfileEnv(params: { profile: string; env?: Record; homedir?: () => string; }) { const env = params.env ?? (process.env as Record); const homedir = params.homedir ?? os.homedir; const profile = params.profile.trim(); if (!profile) return; // Convenience only: fill defaults, never override explicit env values. env.CLAWDBOT_PROFILE = profile; const stateDir = env.CLAWDBOT_STATE_DIR?.trim() || resolveProfileStateDir(profile, homedir); if (!env.CLAWDBOT_STATE_DIR?.trim()) env.CLAWDBOT_STATE_DIR = stateDir; if (!env.CLAWDBOT_CONFIG_PATH?.trim()) { env.CLAWDBOT_CONFIG_PATH = path.join(stateDir, "clawdbot.json"); } if (profile === "dev" && !env.CLAWDBOT_GATEWAY_PORT?.trim()) { env.CLAWDBOT_GATEWAY_PORT = "19001"; } }