feat(api): use official provider endpoints

This commit is contained in:
Patrick Wozniak
2026-08-18 10:56:06 +02:00
parent 0603291396
commit 84a802d91d
20 changed files with 199 additions and 4525 deletions
+45 -52
View File
@@ -1,12 +1,10 @@
/**
* Command Code provider for pi.
*
* Connects pi to Command Code's API (https://api.commandcode.ai/alpha/generate).
* The provider uses pi's legacy extension registration surface because the
* current pi host exposes `registerProvider(name, config)`, including OMP.
* Uses Command Code's documented Provider API:
* https://api.commandcode.ai/provider/v1
*/
import { AssistantMessageEventStream } from "@earendil-works/pi-ai"
import {
getAgentDir,
type ExtensionAPI,
@@ -15,10 +13,11 @@ import {
} from "@earendil-works/pi-coding-agent"
import { join } from "node:path"
import { COMMAND_CODE_CLI_VERSION, createStreamCommandCode, DEFAULT_API_BASE } from "./src/core.ts"
import { calculateCommandCodeCost } from "./src/cost.ts"
import { getConfiguredApiKey } from "./src/api-key.ts"
import {
baseUrlForModel,
DEFAULT_MODELS_URL,
DEFAULT_PROVIDER_API_BASE,
getModelsTimeoutMs,
inputModalitiesForModel,
loadCommandCodeModels,
@@ -28,73 +27,67 @@ import {
import { getApiKey as getOAuthApiKey, login, refreshToken } from "./src/oauth.ts"
import { MODEL_COSTS, ZERO_MODEL_COST } from "./src/pricing.ts"
import { createCommandCodeRuntime } from "./src/runtime.ts"
import { normalizeCommandCodeMessage } from "./src/overflow.ts"
function commandCodeHeaders(): Record<string, string> | undefined {
if (process.env.CMD_ZDR === "1" || process.env.COMMANDCODE_ZDR === "1") {
return { "x-cmd-zdr": "1" }
}
return undefined
}
function createProviderConfig(
models: readonly CommandCodeModel[],
apiBase: string,
streamCommandCode: ProviderConfig["streamSimple"],
): ProviderConfig {
const headers = commandCodeHeaders()
return {
name: "Command Code",
baseUrl: apiBase,
// Keep environment authentication dynamic. OAuth credentials are resolved
// by pi's oauth registration, while the custom stream retains its own
// request-time legacy-file fallback for older compatible hosts.
apiKey: "$COMMANDCODE_API_KEY",
authHeader: true,
api: "commandcode-custom",
streamSimple: streamCommandCode,
headers: {
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
"x-cli-environment": "production",
},
apiKey: getConfiguredApiKey() ?? "$COMMANDCODE_API_KEY",
api: "openai-completions",
headers,
oauth: {
name: "Command Code",
login,
refreshToken,
getApiKey: getOAuthApiKey,
},
models: models.map(createProviderModel),
models: models.map((model) => ({
id: model.id,
name: model.name,
api: model.api,
baseUrl: baseUrlForModel(apiBase, model.api),
reasoning: model.reasoning,
...(thinkingMetadataForModel(model.id) ?? {}),
input: [...inputModalitiesForModel(model.id)],
cost: MODEL_COSTS[model.id] ?? ZERO_MODEL_COST,
contextWindow: model.contextWindow,
maxTokens: model.maxTokens,
headers,
compat:
model.api === "openai-completions"
? {
supportsStore: false,
supportsDeveloperRole: false,
supportsReasoningEffort: true,
maxTokensField: "max_tokens",
}
: {
supportsEagerToolInputStreaming: false,
supportsLongCacheRetention: false,
supportsCacheControlOnTools: false,
supportsToolReferences: false,
},
})),
}
}
function createProviderModel(model: {
id: string
name: string
reasoning: boolean
contextWindow: number
maxTokens: number
}) {
return {
id: model.id,
name: model.name,
reasoning: model.reasoning,
...(thinkingMetadataForModel(model.id) ?? {}),
input: inputModalitiesForModel(model.id),
cost: MODEL_COSTS[model.id] ?? ZERO_MODEL_COST,
contextWindow: model.contextWindow,
maxTokens: model.maxTokens,
} as const
}
export default async function (pi: ExtensionAPI) {
const apiBase = process.env.COMMANDCODE_API_BASE ?? DEFAULT_API_BASE
const apiBase = process.env.COMMANDCODE_API_BASE ?? DEFAULT_PROVIDER_API_BASE
const modelsUrl = process.env.COMMANDCODE_MODELS_URL ?? DEFAULT_MODELS_URL
const modelsTimeoutMs = getModelsTimeoutMs()
const modelsCachePath =
process.env.COMMANDCODE_MODELS_CACHE ?? join(getAgentDir(), "commandcode-models.json")
const streamCommandCode = createStreamCommandCode({
createStream: () => new AssistantMessageEventStream(),
calculateCost: calculateCommandCodeCost,
apiBase,
})
pi.on("message_end", async (event, ctx) => {
if (event.message.role !== "assistant") return
const normalized = normalizeCommandCodeMessage(event.message, ctx.model?.provider)
return normalized ? { message: normalized.message } : undefined
})
const runtime = createCommandCodeRuntime<ProviderConfig, ExtensionCommandContext>(pi, {
endpoint: modelsUrl,
@@ -105,7 +98,7 @@ export default async function (pi: ExtensionAPI) {
cachePath: modelsCachePath,
timeoutMs: modelsTimeoutMs,
}),
createProviderConfig: (models) => createProviderConfig(models, apiBase, streamCommandCode),
createProviderConfig: (models) => createProviderConfig(models, apiBase),
})
await runtime.initialize()