169 lines
4.7 KiB
JavaScript
169 lines
4.7 KiB
JavaScript
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 };
|
|
}
|