import { readFile } from "node:fs/promises"; import { homedir } from "node:os"; import { join } from "node:path"; import { ApiError } from "./errors.js"; /** CLI 設定:API 位址與 access token。 */ export interface FoxConfig { apiUrl: string; token: string | null; } const DEFAULT_API_URL = "http://localhost:3000"; /** * 讀取設定。優先順序(高 → 低): * 1. 環境變數 FOX_API_URL / FOX_API_TOKEN * 2. --api-url / --token 參數(由 caller 傳入) * 3. 本機設定檔 ~/.config/fox/cli.json({"apiUrl": "...", "token": "..."}) * * apiUrl 預設 http://localhost:3000;token 找不到時為 null, * 需要 token 的命令由 caller 檢查並報錯。 */ export async function loadConfig(flags: { apiUrl?: string; token?: string }): Promise { const configPath = join(homedir(), ".config", "fox", "cli.json"); let fileApiUrl: string | undefined; let fileToken: string | undefined; try { const raw = await readFile(configPath, "utf-8"); const parsed: unknown = JSON.parse(raw); if (parsed && typeof parsed === "object") { const obj = parsed as Record; if (typeof obj.apiUrl === "string") fileApiUrl = obj.apiUrl; if (typeof obj.token === "string") fileToken = obj.token; } } catch (err) { const code = (err as NodeJS.ErrnoException).code; if (code !== "ENOENT") { // 設定檔存在但壞掉:明確報錯,而不是靜默忽略。 throw new ApiError(`設定檔 ${configPath} 無法解析:${(err as Error).message}`); } } const envApiUrl = process.env.FOX_API_URL?.trim() || undefined; const envToken = process.env.FOX_API_TOKEN?.trim() || undefined; const apiUrl = flags.apiUrl ?? envApiUrl ?? fileApiUrl ?? DEFAULT_API_URL; const token = flags.token ?? envToken ?? fileToken ?? null; return { apiUrl: apiUrl.replace(/\/+$/, ""), token }; }