From b3372e46c460ed23b61c3110468469fd4b1b3ed9 Mon Sep 17 00:00:00 2001 From: Muhammad Nabil Muyassar Rahman <65392758+TapZe@users.noreply.github.com> Date: Tue, 9 Jun 2026 04:41:52 +0700 Subject: [PATCH] fix(command-code): revert chat endpoint to /alpha/generate and fix model sync discovery (#3432) Integrated into release/v3.8.17 --- docs/reference/PROVIDER_REFERENCE.md | 2 +- open-sse/config/providerRegistry.ts | 4 ++-- open-sse/executors/commandCode.ts | 4 ++-- src/app/api/providers/[id]/models/route.ts | 8 ++++++++ src/lib/providers/validation.ts | 4 ++-- src/shared/constants/providers.ts | 2 +- tests/unit/command-code-executor.test.ts | 10 +++++----- tests/unit/executor-command-code.test.ts | 2 +- tests/unit/provider-validation-specialty.test.ts | 4 ++-- tests/unit/responses-handler.test.ts | 2 +- 10 files changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/reference/PROVIDER_REFERENCE.md b/docs/reference/PROVIDER_REFERENCE.md index 2cab59dd4a..246e05ce37 100644 --- a/docs/reference/PROVIDER_REFERENCE.md +++ b/docs/reference/PROVIDER_REFERENCE.md @@ -113,7 +113,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each | `cloudflare-ai` | `cf` | Cloudflare Workers AI | API key | [link](https://developers.cloudflare.com/workers-ai) | Requires API Token AND Account ID (found at dash.cloudflare.com) | | `codestral` | `codestral` | Codestral | API key | [link](https://mistral.ai) | — | | `cohere` | `cohere` | Cohere | API key | [link](https://cohere.com) | Free Trial: 1,000 API calls/month for testing, no credit card required | -| `command-code` | `cmd` | Command Code | API key | [link](https://commandcode.ai/) | Use a Command Code API key. Requests are sent to Command Code's /provider/v1/chat/completions endpoint. | +| `command-code` | `cmd` | Command Code | API key | [link](https://commandcode.ai/) | Use a Command Code API key. Requests are sent to Command Code's /alpha/generate endpoint. | | `coze` | `coze` | Coze | API key | [link](https://coze.com) | Get API key at coze.com/open/api | | `crof` | `crof` | CrofAI | API key | [link](https://crof.ai) | — | | `databricks` | `databricks` | Databricks | API key, enterprise | [link](https://www.databricks.com) | — | diff --git a/open-sse/config/providerRegistry.ts b/open-sse/config/providerRegistry.ts index 6e76c4da8c..39bb5cb0b8 100644 --- a/open-sse/config/providerRegistry.ts +++ b/open-sse/config/providerRegistry.ts @@ -1626,8 +1626,8 @@ const _REGISTRY_EAGER: Record = { alias: "cmd", format: "openai", executor: "command-code", - baseUrl: "https://api.commandcode.ai/provider/v1", - chatPath: "/chat/completions", + baseUrl: "https://api.commandcode.ai", + chatPath: "/alpha/generate", modelsUrl: "https://api.commandcode.ai/provider/v1/models", authType: "apikey", authHeader: "Authorization", diff --git a/open-sse/executors/commandCode.ts b/open-sse/executors/commandCode.ts index f606ec3cbb..81c1e04611 100644 --- a/open-sse/executors/commandCode.ts +++ b/open-sse/executors/commandCode.ts @@ -509,8 +509,8 @@ export class CommandCodeExecutor extends BaseExecutor { } buildUrl() { - const baseUrl = (this.config.baseUrl || "https://api.commandcode.ai/provider/v1").replace(/\/$/, ""); - return `${baseUrl}${this.config.chatPath || "/chat/completions"}`; + const baseUrl = (this.config.baseUrl || "https://api.commandcode.ai").replace(/\/$/, ""); + return `${baseUrl}${this.config.chatPath || "/alpha/generate"}`; } async execute({ model, body, stream, credentials, signal, upstreamExtraHeaders }: ExecuteInput) { diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index f2d4bcb2dc..e3733dd5f1 100755 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -687,6 +687,14 @@ const PROVIDER_MODELS_CONFIG: Record = { authPrefix: "Bearer ", parseResponse: (data) => data.data || data.models || [], }, + "command-code": { + url: "https://api.commandcode.ai/provider/v1/models", + method: "GET", + headers: { "Content-Type": "application/json" }, + authHeader: "Authorization", + authPrefix: "Bearer ", + parseResponse: (data) => data.data || data.models || [], + }, "opencode-zen": { url: "https://opencode.ai/zen/v1/models", method: "GET", diff --git a/src/lib/providers/validation.ts b/src/lib/providers/validation.ts index fda1067bda..e2ac5d9b66 100644 --- a/src/lib/providers/validation.ts +++ b/src/lib/providers/validation.ts @@ -567,8 +567,8 @@ async function validateDirectChatProvider({ export async function validateCommandCodeProvider({ apiKey, providerSpecificData = {} }: any) { const entry = getRegistryEntry("command-code"); - const baseUrl = normalizeBaseUrl(entry?.baseUrl || "https://api.commandcode.ai/provider/v1"); - const chatPath = entry?.chatPath || "/chat/completions"; + const baseUrl = normalizeBaseUrl(entry?.baseUrl || "https://api.commandcode.ai"); + const chatPath = entry?.chatPath || "/alpha/generate"; const url = `${baseUrl}${chatPath.startsWith("/") ? chatPath : `/${chatPath}`}`; const validationModelId = providerSpecificData?.validationModelId || diff --git a/src/shared/constants/providers.ts b/src/shared/constants/providers.ts index f47a2dc866..67df2b560a 100644 --- a/src/shared/constants/providers.ts +++ b/src/shared/constants/providers.ts @@ -599,7 +599,7 @@ export const APIKEY_PROVIDERS = { textIcon: "CC", website: "https://commandcode.ai/", authHint: - "Use a Command Code API key. Requests are sent to Command Code's /provider/v1/chat/completions endpoint.", + "Use a Command Code API key. Requests are sent to Command Code's /alpha/generate endpoint.", apiHint: "Create or copy an API key from Command Code, then paste it here as a Bearer token.", }, openrouter: { diff --git a/tests/unit/command-code-executor.test.ts b/tests/unit/command-code-executor.test.ts index 758e5c7946..c866b8f459 100644 --- a/tests/unit/command-code-executor.test.ts +++ b/tests/unit/command-code-executor.test.ts @@ -74,8 +74,8 @@ test("Command Code provider catalog has pinned models and alias lookup", () => { assert.ok(entry); assert.equal(entry.alias, "cmd"); assert.equal(entry.executor, "command-code"); - assert.equal(entry.baseUrl, "https://api.commandcode.ai/provider/v1"); - assert.equal(entry.chatPath, "/chat/completions"); + assert.equal(entry.baseUrl, "https://api.commandcode.ai"); + assert.equal(entry.chatPath, "/alpha/generate"); assert.deepEqual( entry.models.map((model) => model.id), PINNED_COMMAND_CODE_MODELS @@ -89,7 +89,7 @@ test("getExecutor returns the specialized Command Code executor", () => { assert.ok(getExecutor("cmd") instanceof CommandCodeExecutor); }); -test("Command Code executor posts wrapped body and required headers to /provider/v1/chat/completions", async () => { +test("Command Code executor posts wrapped body and required headers to /alpha/generate", async () => { const calls: any[] = []; globalThis.fetch = async (url, init = {}) => { calls.push({ url: String(url), init }); @@ -112,9 +112,9 @@ test("Command Code executor posts wrapped body and required headers to /provider }, }); - assert.equal(url, "https://api.commandcode.ai/provider/v1/chat/completions"); + assert.equal(url, "https://api.commandcode.ai/alpha/generate"); assert.equal(calls.length, 1); - assert.equal(calls[0].url, "https://api.commandcode.ai/provider/v1/chat/completions"); + assert.equal(calls[0].url, "https://api.commandcode.ai/alpha/generate"); assert.equal(calls[0].init.method, "POST"); assert.equal(headers.Authorization, "Bearer cc_test_key"); assert.equal(headers["x-command-code-version"], "0.24.1"); diff --git a/tests/unit/executor-command-code.test.ts b/tests/unit/executor-command-code.test.ts index 47776f57b6..d4c2761d56 100644 --- a/tests/unit/executor-command-code.test.ts +++ b/tests/unit/executor-command-code.test.ts @@ -18,7 +18,7 @@ describe("CommandCodeExecutor", () => { const executor = new mod.CommandCodeExecutor(); const url = executor.buildUrl(); assert.ok(typeof url === "string"); - assert.ok(url.includes("/chat/completions") && url.includes("commandcode")); + assert.ok(url.includes("generate") && url.includes("commandcode")); }); it("execute throws when no API key", async () => { diff --git a/tests/unit/provider-validation-specialty.test.ts b/tests/unit/provider-validation-specialty.test.ts index b101f747a6..235326650d 100644 --- a/tests/unit/provider-validation-specialty.test.ts +++ b/tests/unit/provider-validation-specialty.test.ts @@ -88,7 +88,7 @@ test("specialty provider validators cover Deepgram, AssemblyAI, ElevenLabs and I test("validateCommandCodeProvider ignores caller baseUrl and chatPath overrides", async () => { globalThis.fetch = async (url, init = {}) => { - assert.equal(String(url), "https://api.commandcode.ai/provider/v1/chat/completions"); + assert.equal(String(url), "https://api.commandcode.ai/alpha/generate"); const headers = init.headers as Record; assert.equal(headers.Authorization, "Bearer cc-key"); const body = JSON.parse(String(init.body)); @@ -1956,7 +1956,7 @@ test("validateCommandCodeProvider sends Command Code probe URL, headers, and wra assert.deepEqual(result, { valid: true, error: null }); assert.equal(calls.length, 1); - assert.equal(calls[0].url, "https://api.commandcode.ai/provider/v1/chat/completions"); + assert.equal(calls[0].url, "https://api.commandcode.ai/alpha/generate"); assert.equal(calls[0].method, "POST"); assert.equal(calls[0].headers.Authorization, "Bearer cc_test_key"); assert.equal(calls[0].headers["Content-Type"], "application/json"); diff --git a/tests/unit/responses-handler.test.ts b/tests/unit/responses-handler.test.ts index 816bddc26c..cdcd6676da 100644 --- a/tests/unit/responses-handler.test.ts +++ b/tests/unit/responses-handler.test.ts @@ -251,7 +251,7 @@ test("handleResponsesCore transforms Command Code executor SSE through Responses }); assert.equal(result.success, true); - assert.equal(call.url, "https://api.commandcode.ai/provider/v1/chat/completions"); + assert.equal(call.url, "https://api.commandcode.ai/alpha/generate"); assert.equal(call.headers.Authorization, "Bearer cc_test_key"); assert.equal(call.headers["x-command-code-version"], "0.24.1"); assert.equal(call.body.params.model, "gpt-5.4-mini");