export const TOOLS = Object.freeze({ codex: { id: "codex", command: "codex", name: "Codex", vendor: "OpenAI", mono: "CX", modelFlag: "--model", secretNames: ["OPENAI_API_KEY"], }, claude: { id: "claude", command: "claude", name: "Claude Code", vendor: "Anthropic", mono: "CC", modelFlag: "--model", secretNames: ["ANTHROPIC_API_KEY"], }, codebuddy: { id: "codebuddy", command: "codebuddy", name: "CodeBuddy", vendor: "Tencent", mono: "CB", modelFlag: "--model", secretNames: ["CODEBUDDY_API_KEY"], }, kimi: { id: "kimi", command: "kimi", name: "Kimi CLI", vendor: "Moonshot AI", mono: "KM", modelFlag: "--model", secretNames: ["MOONSHOT_API_KEY"], }, opencode: { id: "opencode", command: "opencode", name: "OpenCode", vendor: "SST / Open source", mono: "OC", modelFlag: "--model", secretNames: [ "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DEEPSEEK_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY", ], }, qwen: { id: "qwen", command: "qwen", name: "Qwen Code", vendor: "Alibaba", mono: "QW", modelFlag: "--model", secretNames: ["DASHSCOPE_API_KEY"], }, dsh: { id: "dsh", command: "dsh", name: "DeepSeek Harness", vendor: "DeepSeek", mono: "DS", secretNames: ["DEEPSEEK_API_KEY"], profiles: ["headless"], }, ccswitch: { id: "ccswitch", command: "cc-switch", name: "CC Switch", vendor: "Community CLI", mono: "CS", secretNames: [], visible: false, integration: true, }, multica: { id: "multica", command: "multica-setup", name: "Multica Runtime", vendor: "Multica", mono: "MU", secretNames: [], visible: false, integration: true, }, }); export const SECRET_ENV_ALLOWLIST = new Set( Object.values(TOOLS).flatMap((tool) => tool.secretNames), ); export function toolById(id) { return typeof id === "string" ? TOOLS[id] : undefined; } export function buildCommand(tool, request = {}) { const args = []; const input = request.initialInput?.trim() || ""; if (tool.integration) { if (request.purpose !== "authorization") throw new Error("Integration tools only support setup sessions"); if (tool.id === "multica") { for (const [flag, value] of [["--server-url", request.serverUrl], ["--app-url", request.appUrl]]) { if (!value) continue; let url; try { url = new URL(value); } catch { throw new Error(`${flag} must be a valid URL`); } if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error(`${flag} must be an HTTP(S) URL without embedded credentials`); args.push(flag, url.toString().replace(/\/$/, "")); } } return { command: tool.command, args, interactive: true }; } if (request.purpose === "task" && !input) { throw new Error("A task message is required"); } if (request.purpose === "task") { switch (tool.id) { case "codex": args.push("exec", "--color", "never", "--skip-git-repo-check"); break; case "claude": case "codebuddy": args.push("--print", "--output-format", "text"); break; case "kimi": args.push("--prompt", input, "--output-format", "text"); break; case "opencode": args.push("run", "--format", "default"); break; case "qwen": args.push("--prompt", input, "--output-format", "text"); break; case "dsh": args.push("--profile", "headless", input); return { command: tool.command, args, interactive: false }; default: throw new Error("Unsupported CLI task mode"); } if (tool.modelFlag && typeof request.model === "string" && request.model.trim()) { args.push(tool.modelFlag, request.model.trim().slice(0, 128)); } if (!["kimi", "qwen"].includes(tool.id)) args.push(input); return { command: tool.command, args, interactive: false }; } if (tool.id === "dsh") { const profile = request.profile || "headless"; if (!tool.profiles.includes(profile)) { throw new Error("DeepSeek Harness profile must be headless or web"); } if (profile === "headless" && !request.initialInput?.trim()) { throw new Error("DeepSeek Harness requires a task before starting"); } args.push("--profile", profile); if (profile === "headless" && request.initialInput) args.push(request.initialInput); } else if (tool.modelFlag && typeof request.model === "string" && request.model.trim()) { args.push(tool.modelFlag, request.model.trim().slice(0, 128)); } return { command: tool.command, args, interactive: true }; }