|
|
|
@@ -1,5 +1,5 @@
|
|
|
|
|
import { Resource } from "@opencode-ai/console-resource"
|
|
|
|
|
import { and, Database, eq, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js"
|
|
|
|
|
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"
|
|
|
|
@@ -34,7 +34,6 @@ export async function proxyInference(
|
|
|
|
|
: undefined)
|
|
|
|
|
if (!path) return undefined
|
|
|
|
|
|
|
|
|
|
const go = url.pathname.startsWith("/zen/go/")
|
|
|
|
|
const key = url.pathname.endsWith("/messages")
|
|
|
|
|
? request.headers.get("x-api-key")
|
|
|
|
|
: path.startsWith("/google/")
|
|
|
|
@@ -42,44 +41,23 @@ export async function proxyInference(
|
|
|
|
|
: request.headers.get("authorization")?.split(" ")[1]
|
|
|
|
|
if (!key || key === "public") return undefined
|
|
|
|
|
|
|
|
|
|
// Routing only; the destination owns authentication and revocation after cutover.
|
|
|
|
|
const workspace = await Database.use((tx) =>
|
|
|
|
|
tx
|
|
|
|
|
.select({
|
|
|
|
|
id: WorkspaceTable.id,
|
|
|
|
|
migratedAt: WorkspaceTable.migrated_at,
|
|
|
|
|
provider: ProviderTable.provider,
|
|
|
|
|
})
|
|
|
|
|
.from(KeyTable)
|
|
|
|
|
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
|
|
|
|
.leftJoin(
|
|
|
|
|
ProviderTable,
|
|
|
|
|
!go && generation?.provider
|
|
|
|
|
? and(
|
|
|
|
|
eq(ProviderTable.workspaceID, KeyTable.workspaceID),
|
|
|
|
|
eq(ProviderTable.provider, generation.provider),
|
|
|
|
|
isNull(ProviderTable.timeDeleted),
|
|
|
|
|
sql`length(${ProviderTable.credentials}) > 0`,
|
|
|
|
|
)
|
|
|
|
|
: sql`false`,
|
|
|
|
|
)
|
|
|
|
|
.where(eq(KeyTable.key, key))
|
|
|
|
|
.limit(1)
|
|
|
|
|
.then((rows) => rows[0]),
|
|
|
|
|
)
|
|
|
|
|
if (!workspace?.migratedAt) return undefined
|
|
|
|
|
const model = workspace.provider ? generation?.model : undefined
|
|
|
|
|
if (workspace.provider && !model) throw new Error("Legacy BYOK model mapping is unavailable")
|
|
|
|
|
// 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
|
|
|
|
|
? `/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
|
|
|
|
|
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 = ""
|
|
|
|
@@ -109,6 +87,30 @@ export async function proxyInference(
|
|
|
|
|
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." } },
|
|
|
|
|