Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ Fourteen backends. Pin one, or let `auto` walk the chain in order.
| ------------ | ---------------------- |
| `auto` | chain |
| `exa` | `EXA_API_KEY` (or mcp) |
| `brave` | `BRAVE_API_KEY` |
Anthropic `oauth` · OpenAI · OpenAI Codex `oauth` · Google Gemini · Google Antigravity `oauth` · xAI · Mistral · Groq · Cerebras · Fireworks · Together · Hugging Face · NVIDIA · OpenRouter · Synthetic · Vercel AI Gateway · Cloudflare AI Gateway · Wafer Serverless · Command Code · Perplexity `oauth`
| `jina` | `JINA_API_KEY` |
| `kimi` | `MOONSHOT_API_KEY` |
| `zai` | `ZAI_API_KEY` |
Expand Down
7 changes: 7 additions & 0 deletions packages/ai/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## [Unreleased]
### Added

- Added the **Command Code** provider (`/login commandcode`), backed by Command Code's OpenAI-compatible Provider API (`https://api.commandcode.ai/provider/v1`). Authenticate by pasting a key (Command Code Studio → API Keys) or setting `COMMANDCODE_API_KEY`; models are discovered at runtime from `GET /provider/v1/models`. Follows the same registry shape as other OpenAI-compatible API-key providers (Cerebras, Moonshot, Together, vLLM).


## [16.1.16] - 2026-06-23

Expand Down Expand Up @@ -1357,6 +1361,9 @@
- Fixed `pi-ai login <provider>` crashing with `Unknown provider` for providers that only the `auth-storage` `login()` switch knew about (perplexity, alibaba-coding-plan, gitlab-duo, huggingface, opencode-zen/go, lm-studio, ollama, cerebras, fireworks, qianfan, synthetic, venice, litellm, moonshot, together, cloudflare/vercel ai gateways, vllm, qwen-portal, nvidia, xiaomi, and any custom OAuth provider). The CLI now delegates to `SqliteAuthCredentialStore.login()` instead of duplicating a smaller switch, so the auth-broker `omp auth-broker login <provider>` flow works for every registered OAuth provider.

## [15.1.4] - 2026-05-19
### Added

- Added the **Command Code** provider (`/login commandcode`), backed by Command Code's OpenAI-compatible Provider API (`https://api.commandcode.ai/provider/v1`). Models are discovered at runtime from `GET /provider/v1/models`; authenticate by pasting a key (Command Code Studio → API Keys) or setting `COMMANDCODE_API_KEY`. Follows the same shape as other OpenAI-compatible API-key providers (Cerebras, Moonshot, Together, vLLM).
Comment on lines +1364 to +1366

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the duplicate released changelog entry

This same Command Code note is already under [Unreleased], but this copy was inserted under the already-released [15.1.4] - 2026-05-19 section; the repo changelog rules say released sections are immutable. Leaving it here rewrites historical release notes and makes the provider appear to have shipped in an old release, so this duplicate should be removed from the released section.

Useful? React with 👍 / 👎.


### Changed

Expand Down
23 changes: 23 additions & 0 deletions packages/ai/src/registry/commandcode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** Command Code login flow (API key paste against the OpenAI-compatible Provider API). */
import { createApiKeyLogin } from "./api-key-login";
import type { OAuthLoginCallbacks } from "./oauth/types";
import type { ProviderDefinition } from "./types";

export const loginCommandCode = createApiKeyLogin({
providerLabel: "Command Code",
authUrl: "https://commandcode.ai/studio/api-keys",
instructions: "Create an API key in Command Code Studio (Provider plan) and copy it",
promptMessage: "Paste your Command Code API key",
placeholder: "sk-...",
validation: {
kind: "models-endpoint",
provider: "commandcode",
modelsUrl: "https://api.commandcode.ai/provider/v1/models",
},
});

export const commandCodeProvider = {
id: "commandcode",
name: "Command Code",
login: (cb: OAuthLoginCallbacks) => loginCommandCode(cb),
} as const satisfies ProviderDefinition;
2 changes: 2 additions & 0 deletions packages/ai/src/registry/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { anthropicProvider } from "./anthropic";
import { azureProvider } from "./azure";
import { cerebrasProvider } from "./cerebras";
import { cloudflareAiGatewayProvider } from "./cloudflare-ai-gateway";
import { commandCodeProvider } from "./commandcode";
import { cursorProvider } from "./cursor";
import { deepseekProvider } from "./deepseek";
import { devinProvider } from "./devin";
Expand Down Expand Up @@ -114,6 +115,7 @@ const ALL = [
waferServerlessProvider,
vercelAiGatewayProvider,
cloudflareAiGatewayProvider,
commandCodeProvider,
litellmProvider,
kiloProvider,
zenmuxProvider,
Expand Down
28 changes: 28 additions & 0 deletions packages/ai/test/auth-storage-api-key-login.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as os from "node:os";
import * as path from "node:path";

import { AuthStorage, SqliteAuthCredentialStore } from "@oh-my-pi/pi-ai/auth-storage";
import * as commandCodeModule from "@oh-my-pi/pi-ai/registry/commandcode";
import * as deepseekModule from "@oh-my-pi/pi-ai/registry/deepseek";
import * as kagiModule from "@oh-my-pi/pi-ai/registry/kagi";
import * as ollamaCloudModule from "@oh-my-pi/pi-ai/registry/ollama-cloud";
Expand Down Expand Up @@ -44,6 +45,7 @@ describe("AuthStorage api-key login upsert", () => {
let loginDeepSeekSpy: Mock<typeof deepseekModule.loginDeepSeek>;
let loginKagiSpy: Mock<typeof kagiModule.loginKagi>;
let loginOllamaCloudSpy: Mock<typeof ollamaCloudModule.loginOllamaCloud>;
let loginCommandCodeSpy: Mock<typeof commandCodeModule.loginCommandCode>;

beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pi-ai-auth-api-key-login-"));
Expand All @@ -53,6 +55,7 @@ describe("AuthStorage api-key login upsert", () => {
loginDeepSeekSpy = vi.spyOn(deepseekModule, "loginDeepSeek");
loginKagiSpy = vi.spyOn(kagiModule, "loginKagi");
loginOllamaCloudSpy = vi.spyOn(ollamaCloudModule, "loginOllamaCloud");
loginCommandCodeSpy = vi.spyOn(commandCodeModule, "loginCommandCode");
});

afterEach(async () => {
Expand Down Expand Up @@ -182,4 +185,29 @@ describe("AuthStorage api-key login upsert", () => {
expect(store.getApiKey("deepseek")).toBe("same-deepseek-key");
expect(await authStorage.getApiKey("deepseek", "session-deepseek-relogin")).toBe("same-deepseek-key");
});

it("stores a commandcode api-key credential via the login dispatch", async () => {
if (!store || !authStorage || !dbPath) throw new Error("test setup failed");

loginCommandCodeSpy.mockResolvedValueOnce("same-cc-key");

const controller = {
onAuth: () => {},
onPrompt: async () => "",
};

await authStorage.login("commandcode", controller);

expect(countCredentialRows(dbPath, "commandcode")).toBe(1);
const ccCredentials = store.listAuthCredentials("commandcode");
expect(ccCredentials).toHaveLength(1);
const [ccStored] = ccCredentials;
expect(ccStored?.credential.type).toBe("api_key");
if (!ccStored || ccStored.credential.type !== "api_key") {
throw new Error("expected stored api-key credential");
}
expect(ccStored.credential.key).toBe("same-cc-key");
expect(store.getApiKey("commandcode")).toBe("same-cc-key");
expect(await authStorage.getApiKey("commandcode", "session-cc-login")).toBe("same-cc-key");
});
});
4 changes: 4 additions & 0 deletions packages/catalog/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## [Unreleased]
### Added

- Added the **Command Code** provider to the catalog table: OpenAI-compatible (`https://api.commandcode.ai/provider/v1`), runtime-discovered via `GET /provider/v1/models` (not on models.dev), env var `COMMANDCODE_API_KEY`, default model `deepseek/deepseek-v4-pro`.


## [16.1.14] - 2026-06-22

Expand Down
8 changes: 8 additions & 0 deletions packages/catalog/src/provider-models/descriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
anthropicModelManagerOptions,
cerebrasModelManagerOptions,
cloudflareAiGatewayModelManagerOptions,
commandCodeModelManagerOptions,
deepseekModelManagerOptions,
firepassModelManagerOptions,
fireworksModelManagerOptions,
Expand Down Expand Up @@ -95,6 +96,13 @@ export const CATALOG_PROVIDERS = [
createModelManagerOptions: (config: ModelManagerConfig) => cloudflareAiGatewayModelManagerOptions(config),
catalogDiscovery: { label: "Cloudflare AI Gateway" },
},
{
id: "commandcode",
defaultModel: "deepseek/deepseek-v4-pro",
envVars: ["COMMANDCODE_API_KEY"],
createModelManagerOptions: (config: ModelManagerConfig) => commandCodeModelManagerOptions(config),
catalogDiscovery: { label: "Command Code", oauthProvider: "commandcode" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep runtime-only Command Code out of generation

This catalogDiscovery opt-in enrolls Command Code in packages/catalog/scripts/generate-models.ts's catalog fetch loop (isCatalogDescriptor && !DISCOVERY_ONLY_PROVIDERS). Because this provider is described as runtime-discovered and has no bundled models.json source, a maintainer who has COMMANDCODE_API_KEY set will bake the live /provider/v1/models response into generated models.json on the next regeneration instead of keeping it runtime-only. Either mark it discovery-only in the generator or remove this catalog-discovery opt-in.

Useful? React with 👍 / 👎.

},
{
id: "cursor",
defaultModel: "claude-4.6-opus-high",
Expand Down
40 changes: 40 additions & 0 deletions packages/catalog/src/provider-models/openai-compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2832,6 +2832,46 @@ export function vllmModelManagerOptions(config?: VllmModelManagerConfig): ModelM
};
}

// ---------------------------------------------------------------------------
// 22.5. Command Code
// ---------------------------------------------------------------------------

export interface CommandCodeModelManagerConfig {
apiKey?: string;
baseUrl?: string;
fetch?: FetchImpl;
}

/**
* Command Code Provider API — OpenAI-compatible (`/provider/v1/chat/completions`).
* Not listed on models.dev, so models come purely from runtime discovery against
* `GET /provider/v1/models` (same shape as vLLM / NanoGPT).
*/
export function commandCodeModelManagerOptions(
config?: CommandCodeModelManagerConfig,
): ModelManagerOptions<"openai-completions"> {
const apiKey = config?.apiKey;
const baseUrl = config?.baseUrl ?? "https://api.commandcode.ai/provider/v1";
const references = createBundledReferenceMap<"openai-completions">(
"commandcode" as Parameters<typeof getBundledModels>[0],
);
return {
providerId: "commandcode",
...(apiKey && {
fetchDynamicModels: () =>
fetchOpenAICompatibleModels({
api: "openai-completions",
provider: "commandcode",
baseUrl,
apiKey,
mapModel: (entry, defaults) =>
mapWithBundledReference(entry, defaults, references.get(defaults.id)),
fetch: config?.fetch,
}),
}),
};
}

// ---------------------------------------------------------------------------
// 23. NanoGPT
// ---------------------------------------------------------------------------
Expand Down
Loading