From f77b99be3cac3aa10359201207e992d54f16c509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Sat, 23 May 2026 18:26:03 +0200 Subject: [PATCH] feat(den): add provider credential contract base Add the LLM provider credential kind/opencode auth storage contract, migration, and passive credential redaction/flags needed by follow-up provider credential and worker sync PRs. --- .../den-api/src/routes/org/llm-providers.ts | 33 ++++++++++++++++--- .../0019_llm_provider_opencode_oauth.sql | 3 ++ ee/packages/den-db/drizzle/meta/_journal.json | 7 ++++ .../src/schema/sharables/llm-providers.ts | 4 +++ 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 ee/packages/den-db/drizzle/0019_llm_provider_opencode_oauth.sql diff --git a/ee/apps/den-api/src/routes/org/llm-providers.ts b/ee/apps/den-api/src/routes/org/llm-providers.ts index 5cc66da166..994b43565e 100644 --- a/ee/apps/den-api/src/routes/org/llm-providers.ts +++ b/ee/apps/den-api/src/routes/org/llm-providers.ts @@ -145,6 +145,24 @@ function isOrganizationAdmin(payload: { currentMember: { isOwner: boolean; role: return payload.currentMember.isOwner || memberHasRole(payload.currentMember.role, "admin") } +export function getCredentialFlags(provider: Pick) { + const hasApiKey = Boolean(provider.apiKey && provider.apiKey.trim().length > 0) + const hasOpencodeAuth = Boolean(provider.opencodeAuth && provider.opencodeAuth.trim().length > 0) + return { + hasApiKey, + hasOpencodeAuth, + hasCredential: provider.credentialKind === "opencode_oauth" ? hasOpencodeAuth : hasApiKey, + } +} + +export function redactLlmProviderCredentials(provider: T): Omit & { apiKey: undefined; opencodeAuth: undefined } { + return { + ...provider, + apiKey: undefined, + opencodeAuth: undefined, + } +} + function canManageLlmProvider( payload: { currentMember: { id: MemberId; isOwner: boolean; role: string } }, provider: LlmProviderRow, @@ -464,7 +482,7 @@ async function loadLlmProviders(input: { return providers.map((provider) => ({ ...provider, - hasApiKey: Boolean(provider.apiKey && provider.apiKey.trim().length > 0), + ...getCredentialFlags(provider), models: (modelsByProviderId.get(provider.id) ?? []) .map((model) => ({ id: model.modelId, @@ -607,8 +625,7 @@ export function registerOrgLlmProviderRoutes ({ - ...provider, - apiKey: undefined, + ...redactLlmProviderCredentials(provider), canManage: canManageLlmProvider(payload, provider), })), }) @@ -678,6 +695,8 @@ export function registerOrgLlmProviderRoutes ({ id: model.modelId, @@ -781,10 +800,13 @@ export function registerOrgLlmProviderRoutes>() .notNull(), + credentialKind: mysqlEnum("credential_kind", ["api_key", "opencode_oauth"]) + .notNull() + .default("api_key"), apiKey: encryptedTextColumn("api_key"), + opencodeAuth: encryptedTextColumn("opencode_auth"), createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { fsp: 3 }) .notNull()