import { Resource } from "@opencode-ai/console-resource" import { and, Database, eq, isNotNull, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js" import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js" import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" const paths: Record = { "POST /zen/v1/chat/completions": "/openai/v1/chat/completions", "POST /zen/v1/responses": "/openai/v1/responses", "POST /zen/v1/messages": "/anthropic/v1/messages", "POST /zen/go/v1/chat/completions": "/go/openai/v1/chat/completions", "POST /zen/go/v1/responses": "/go/openai/v1/responses", "POST /zen/go/v1/messages": "/go/anthropic/v1/messages", "GET /zen/v1/models": "/v1/models", "GET /zen/go/v1/models": "/go/v1/models", "GET /zen/go/v1/usage": "/go/v1/usage", } export async function proxyInference( request: Request, generation?: { provider?: "openai" | "anthropic" | "google" /** The provider's native model ID, not the public Zen alias. */ model?: string body: (model?: string) => ReadableStream }, ): Promise { const url = new URL(request.url) const path = paths[`${request.method} ${url.pathname}`] ?? (request.method === "POST" && /^\/zen\/v1\/models\/[^/]+:(?:generateContent|streamGenerateContent)$/.test(url.pathname) ? url.pathname.replace("/zen/v1/models/", "/google/v1beta/models/") : undefined) if (!path) return undefined const key = url.pathname.endsWith("/messages") ? request.headers.get("x-api-key") : path.startsWith("/google/") ? request.headers.get("x-goog-api-key") : request.headers.get("authorization")?.split(" ")[1] if (!key || key === "public") return undefined // New Console keys are never in the legacy key table; every legacy key is `sk-`. const legacy = !key.startsWith("oc_sk_") const workspace = legacy ? await migratedWorkspace(key, generation?.provider) : undefined if (legacy && !workspace) return undefined const model = workspace?.provider ? generation?.model : undefined if (workspace?.provider && !model) throw new Error("Legacy BYOK model mapping is unavailable") const destination = new URL(Resource.ConsoleMigration.inferenceUrl) // Imported connections must use this same workspace/provider-derived ID. const target = model && workspace ? `/custom/conn_${workspace.id.slice(4)}_${workspace.provider}${ path.startsWith("/google/") ? `/models/${encodeURIComponent(model)}${url.pathname.slice(url.pathname.lastIndexOf(":"))}` : url.pathname.slice("/zen/v1".length) }` : path destination.pathname = `${destination.pathname.replace(/\/$/, "")}${target}` destination.search = url.search destination.hash = "" // Model extraction has already read part of the body; forward its replay stream. const forwarded = new Request( destination, generation ? new Request(request, { method: request.method, body: generation.body(model) }) : request, ) // Migrated requests use ordinary destination authentication and accounting. for (const name of [ "x-zen", "x-zen-model", "x-zen-ip", "cf-access-client-id", "cf-access-client-secret", "host", "content-length", ]) forwarded.headers.delete(name) forwarded.headers.set("authorization", `Bearer ${key}`) const ip = request.headers.get("cf-connecting-ip") if (ip) forwarded.headers.set("x-real-ip", ip) const requestID = request.headers.get("x-opencode-request-id") ?? request.headers.get("x-opencode-request") if (requestID) forwarded.headers.set("x-opencode-request-id", requestID) return fetch(forwarded, { redirect: "manual" }) } // Routing only; the destination owns authentication and revocation after cutover. function migratedWorkspace(key: string, provider?: string) { return Database.use((tx) => tx .select({ id: WorkspaceTable.id, provider: ProviderTable.provider }) .from(KeyTable) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) .leftJoin( ProviderTable, provider ? and( eq(ProviderTable.workspaceID, KeyTable.workspaceID), eq(ProviderTable.provider, provider), isNull(ProviderTable.timeDeleted), sql`length(${ProviderTable.credentials}) > 0`, ) : sql`false`, ) .where(and(eq(KeyTable.key, key), isNotNull(WorkspaceTable.migrated_at))) .limit(1) .then((rows) => rows[0]), ) } export function inferenceUnavailable() { return Response.json( { error: { type: "api_error", message: "Inference routing is unavailable. Please retry later." } }, { status: 503, headers: { "Cache-Control": "no-store" } }, ) }