From b1068c7d2fd6e87aeeba4ecb619e8ea984dffe8d Mon Sep 17 00:00:00 2001 From: Sawyer Date: Fri, 18 Sep 2026 14:06:44 -0700 Subject: [PATCH 1/2] feat(hub): OAuth tokens refresh ahead of expiry and Settings offers sign-in again (CL-8592) --- apps/hub/package.json | 2 +- apps/hub/src/server.ts | 89 +++++++++++++++---- apps/web/src/settings/credentials-section.tsx | 73 ++++++++++++++- apps/web/src/settings/strings.ts | 3 + bun.lock | 8 +- 5 files changed, 151 insertions(+), 24 deletions(-) diff --git a/apps/hub/package.json b/apps/hub/package.json index 29cbfe5ba..a9640e7d3 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -20,7 +20,7 @@ "@corbits/error-sink": "workspace:*", "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", "@corbits/memory": "github:corbitsdev/corbits-memory#e74da20f148a302dff5400915fe504ee2395e913", - "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#e97d563b823506d72f83dead478dd9fa9dccfd4c", + "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#f818dcb6dc5e08319a12cd154ca63e5eed61a947", "@corbits/url-path": "workspace:*", "@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d", "@corbits/workflows": "workspace:*", diff --git a/apps/hub/src/server.ts b/apps/hub/src/server.ts index 35ef40602..c45d0eff1 100644 --- a/apps/hub/src/server.ts +++ b/apps/hub/src/server.ts @@ -34,6 +34,7 @@ import { createReconciliationScheduler, DEFAULT_SIDECAR_ALLOCATION_CONCURRENCY, pushCredentialReconcile, + pushSourceUpdates, WORKSPACE_BUILTINS_REGISTRY, type SidecarLookups, type SidecarProvisioner, @@ -57,9 +58,23 @@ import { mountMailbox, } from "@corbits/mailbox"; import { createMemory, loadMemoryConfig } from "@corbits/memory"; -import { mountOAuthLogin } from "@corbits/oauth-core/hub"; -import { CODEX_PROVIDER, codexOAuthConfig, exchangeCodexCode } from "@corbits/codex-provider"; -import { XAI_PROVIDER, xaiOAuthConfig, exchangeXaiCode } from "@corbits/xai-provider"; +import { + createOAuthTokenRefresher, + mountOAuthLogin, + type OAuthLoginProviders, +} from "@corbits/oauth-core/hub"; +import { + CODEX_PROVIDER, + codexOAuthConfig, + exchangeCodexCode, + refreshCodexTokens, +} from "@corbits/codex-provider"; +import { + XAI_PROVIDER, + xaiOAuthConfig, + exchangeXaiCode, + refreshXaiTokens, +} from "@corbits/xai-provider"; import { createCronTicker, createRunTriggerCronDeliver, mountCron } from "@corbits/cron"; import { createHubMailboxAuthorizeSender, @@ -570,6 +585,7 @@ export async function createHubServer({ }; let cronTicker: { start(): void; stop(): void } | undefined; + let oauthTokenRefresher: { start(): void; stop(): void } | undefined; { const cronApp = new Hono(); mountCron(cronApp, { @@ -644,30 +660,67 @@ export async function createHubServer({ grantStore, conditionRegistry: grantConditionRegistry, }); + const oauthProviders: OAuthLoginProviders = { + [CODEX_PROVIDER]: { + oauthConfig: codexOAuthConfig, + exchange: (code: string, verifier: string, now: number) => + exchangeCodexCode(code, verifier, now), + // The account id the refresher carries forward lives on the + // credential, so the prior tokens need only supply the secret. + refresh: (refreshSecret: string, now: number) => + refreshCodexTokens(refreshSecret, now, { access: "", refresh: refreshSecret }), + // The Codex backend rejects inference without this header value. + metadata: (tokens) => + "accountId" in tokens && typeof tokens.accountId === "string" + ? { accountId: tokens.accountId } + : {}, + }, + [XAI_PROVIDER]: { + oauthConfig: xaiOAuthConfig, + exchange: (code: string, verifier: string, now: number) => + exchangeXaiCode(code, verifier, now), + refresh: (refreshSecret: string, now: number) => refreshXaiTokens(refreshSecret, now), + }, + }; mountOAuthLogin(oauthLoginApi, { db, cipher: credentialCipher, requireGrant: requireGrant("credential:*", "create"), - providers: { - [CODEX_PROVIDER]: { - oauthConfig: codexOAuthConfig, - exchange: (code, verifier, now) => exchangeCodexCode(code, verifier, now), - // The Codex backend rejects inference without this header value. - metadata: (tokens) => - "accountId" in tokens && typeof tokens.accountId === "string" - ? { accountId: tokens.accountId } - : {}, - }, - [XAI_PROVIDER]: { - oauthConfig: xaiOAuthConfig, - exchange: (code, verifier, now) => exchangeXaiCode(code, verifier, now), - }, - }, + providers: oauthProviders, onError: (error, { provider }) => { reportError(error, { operation: "hub.oauth-login", extra: { provider } }); }, }); app.route(TENANT_PREFIX, oauthLoginApi); + + // Stock Interchange has no serving-time refresh hook, so a subscription + // token that lapses between inference calls would simply fail the next + // one; this renews it a little before expiry instead. + oauthTokenRefresher = createOAuthTokenRefresher({ + db, + cipher: credentialCipher, + providers: oauthProviders, + intervalMs: 60_000, + onRefreshed: ({ tenantId, credentialId }) => { + // The same push the stock credentials route fires after a secret + // rotation, so running sidecars get the new material. + void pushSourceUpdates(db, sidecarRouter, tenantId, credentialCipher).catch( + (error: unknown) => { + reportError(error, { + operation: "hub.oauth-refresh.push", + extra: { tenantId, credentialId }, + }); + }, + ); + }, + onError: (error, { provider, credentialId }) => { + reportError(error, { + operation: "hub.oauth-refresh", + extra: { provider: provider ?? "", credentialId: credentialId ?? "" }, + }); + }, + }); + oauthTokenRefresher.start(); } await installWebhooks({ diff --git a/apps/web/src/settings/credentials-section.tsx b/apps/web/src/settings/credentials-section.tsx index 8d78a616b..ca06d538d 100644 --- a/apps/web/src/settings/credentials-section.tsx +++ b/apps/web/src/settings/credentials-section.tsx @@ -1,5 +1,7 @@ // The stock credential routes are the only way a key gets stored in this -// repo — no connector registry, no OAuth flow, no per-provider card. +// repo — no connector registry, no per-provider card. A credential minted +// by signing in is renewed by signing in again, through the same hub-hosted +// login the onboarding step uses. import { Button, @@ -26,6 +28,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import { QueryView, toAPIQuery } from "@/lib/api-query"; +import { readProviderLogin, startProviderLogin } from "@/settings/inference"; import { tenantKeys } from "@/query-client"; import { createCredential, @@ -40,6 +43,15 @@ import { } from "./credentials-api"; import { SETTINGS_STRINGS } from "./strings"; +/** The registered OAuth provider a credential was minted by, as + * `@corbits/oauth-core` records it; absent on a pasted key. */ +function oauthProviderOf(credential: Credential): string | null { + const metadata: unknown = credential.metadata; + if (typeof metadata !== "object" || metadata === null) return null; + const value = (metadata as Record).oauthProvider; + return typeof value === "string" && value.length > 0 ? value : null; +} + type CredentialsData = { readonly credentials: readonly Credential[]; readonly providers: readonly Provider[]; @@ -49,6 +61,7 @@ export function CredentialsSection({ tenantId }: { readonly tenantId: string | n const queryClient = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); const [editing, setEditing] = useState(null); + const [loginId, setLoginId] = useState(null); // Credentials and their providers load together: creating one needs the // provider row for the typed name, so a split read would race the form. @@ -93,6 +106,41 @@ export function CredentialsSection({ tenantId }: { readonly tenantId: string | n }, }); + // Re-signing in files the tokens under the same credential name, so the + // hub replaces the material in place and every offering keeps pointing at it. + const signIn = useMutation({ + mutationFn: (credential: Credential) => { + const provider = oauthProviderOf(credential); + if (tenantId === null || provider === null) { + throw new Error("that credential was not created by signing in"); + } + return startProviderLogin(tenantId, { + provider, + providerId: credential.providerId, + credentialName: credential.name, + }); + }, + onSuccess: (started) => { + setLoginId(started.loginId); + window.open(started.authorizeUrl, "_blank", "noopener,noreferrer"); + }, + }); + + const login = useQuery({ + queryKey: tenantKeys.credentials(tenantId ?? "none").concat("oauth-login", loginId ?? ""), + enabled: tenantId !== null && loginId !== null, + queryFn: async () => { + if (tenantId === null || loginId === null) throw new Error("no sign-in in flight"); + const state = await readProviderLogin(tenantId, loginId); + if (state.status !== "pending") { + setLoginId(null); + reload(); + } + return state; + }, + refetchInterval: (query) => (query.state.data?.status === "pending" ? 2000 : false), + }); + const update = useMutation({ mutationFn: (input: { readonly credentialId: string; @@ -150,10 +198,17 @@ export function CredentialsSection({ tenantId }: { readonly tenantId: string | n {SETTINGS_STRINGS.credentialsDeleteError}

)} + {signIn.error === null || signIn.error === undefined ? null : ( +

+ {SETTINGS_STRINGS.credentialsSignInAgainError} +

+ )} del.mutate(credential)} + onSignIn={(credential) => signIn.mutate(credential)} /> void; readonly onDelete: (credential: Credential) => void; + readonly onSignIn: (credential: Credential) => void; }) { if (credentials.length === 0) { return ( @@ -211,6 +270,18 @@ function CredentialsTable({ {credential.type} {credential.status} + {credential.type === "oauth_token" && oauthProviderOf(credential) !== null ? ( + + ) : null} diff --git a/apps/web/src/settings/strings.ts b/apps/web/src/settings/strings.ts index 6aafb0bd9..cf60438c8 100644 --- a/apps/web/src/settings/strings.ts +++ b/apps/web/src/settings/strings.ts @@ -168,6 +168,9 @@ export const SETTINGS_STRINGS = { credentialsDeleteConfirm: "Delete this credential for good?", credentialsDeleteError: "Couldn't delete that credential — try again.", credentialsEditAction: "Edit", + credentialsSignInAgainAction: "Sign in again", + credentialsSignInAgainPending: "Waiting for sign-in…", + credentialsSignInAgainError: "Couldn't start that sign-in — try again.", credentialsEditDialogTitle: "Edit credential", credentialsEditDialogDescription: "Base URL and model apply to a local, Ollama-style credential; leave them blank otherwise.", diff --git a/bun.lock b/bun.lock index 84d5fb654..63f2f3d62 100644 --- a/bun.lock +++ b/bun.lock @@ -61,7 +61,7 @@ "@corbits/error-sink": "workspace:*", "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", "@corbits/memory": "github:corbitsdev/corbits-memory#e74da20f148a302dff5400915fe504ee2395e913", - "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#e97d563b823506d72f83dead478dd9fa9dccfd4c", + "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#f818dcb6dc5e08319a12cd154ca63e5eed61a947", "@corbits/url-path": "workspace:*", "@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d", "@corbits/workflows": "workspace:*", @@ -621,7 +621,7 @@ "@corbits/myra": ["@corbits/myra@workspace:agents/myra"], - "@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#e97d563", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-e97d563", "sha512-YbW3CXt/W+fw+0RHBm8hWXg9m0Gckt/iTDJJ/c60SYhOVOvPVaQ+HscRgq+UxMThtyer9xIBkkL8B8YgdEBfgQ=="], + "@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#f818dcb", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-f818dcb", "sha512-hfJdNa3GjNIV1VbzXPgSoBOf/RFRPj3q8RJTooRfWTra93GSfw3QRJ+dRHeRvxsAUbuiZkAazsMYlQaOm4N8rw=="], "@corbits/ollama-adapter": ["@corbits/ollama-adapter@github:corbitsdev/corbits-ollama-adapter#0387eb1", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-ollama-adapter-0387eb1", "sha512-O1dl5lSwWzNGJibQLlpYDFKJZWpstgHX519bl8oe1ALL/KHszT/tBDlpsD9jN3hmQD3jV2L1jNwvOwLOBLtpyw=="], @@ -1823,11 +1823,11 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@corbits/codex-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#1b7f9fb", { "dependencies": { "arktype": "2.2.3" } }, "corbitsdev-corbits-oauth-core-1b7f9fb", "sha512-PscyiDAl46umTM44NQPN1q5+PsGUV53Rxr4Wz/Ofz+k9LOOz6FBKamRFtWR8YWdKJt9I8sPOLOyEcMNNN2i+YQ=="], + "@corbits/codex-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#e97d563", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-e97d563", "sha512-YbW3CXt/W+fw+0RHBm8hWXg9m0Gckt/iTDJJ/c60SYhOVOvPVaQ+HscRgq+UxMThtyer9xIBkkL8B8YgdEBfgQ=="], "@corbits/mailbox/hono-openapi": ["hono-openapi@1.3.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.11.2", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-NLVeVkhKZ3drmQNEIPac8HX8Y54uf1hJAgIM/7MfDsaeVVmB+QILWQxx5x3R3NvRHgedcbEbOCGY2uR7WQYyMw=="], - "@corbits/xai-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#1b7f9fb", { "dependencies": { "arktype": "2.2.3" } }, "corbitsdev-corbits-oauth-core-1b7f9fb", "sha512-PscyiDAl46umTM44NQPN1q5+PsGUV53Rxr4Wz/Ofz+k9LOOz6FBKamRFtWR8YWdKJt9I8sPOLOyEcMNNN2i+YQ=="], + "@corbits/xai-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#e97d563", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-e97d563", "sha512-YbW3CXt/W+fw+0RHBm8hWXg9m0Gckt/iTDJJ/c60SYhOVOvPVaQ+HscRgq+UxMThtyer9xIBkkL8B8YgdEBfgQ=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], From f78a1c89e4c4a3c15cbf71d2051da1ea65863d91 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Fri, 18 Sep 2026 14:09:16 -0700 Subject: [PATCH 2/2] chore(hub): pin @corbits/oauth-core to the merged main sha (CL-8592) --- apps/hub/package.json | 2 +- bun.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/hub/package.json b/apps/hub/package.json index a9640e7d3..dabd4185f 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -20,7 +20,7 @@ "@corbits/error-sink": "workspace:*", "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", "@corbits/memory": "github:corbitsdev/corbits-memory#e74da20f148a302dff5400915fe504ee2395e913", - "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#f818dcb6dc5e08319a12cd154ca63e5eed61a947", + "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#f186ed0e14e8fc3bacb32efa1caabb02d9d5cafd", "@corbits/url-path": "workspace:*", "@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d", "@corbits/workflows": "workspace:*", diff --git a/bun.lock b/bun.lock index 63f2f3d62..8d07a254c 100644 --- a/bun.lock +++ b/bun.lock @@ -61,7 +61,7 @@ "@corbits/error-sink": "workspace:*", "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", "@corbits/memory": "github:corbitsdev/corbits-memory#e74da20f148a302dff5400915fe504ee2395e913", - "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#f818dcb6dc5e08319a12cd154ca63e5eed61a947", + "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core#f186ed0e14e8fc3bacb32efa1caabb02d9d5cafd", "@corbits/url-path": "workspace:*", "@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d", "@corbits/workflows": "workspace:*", @@ -621,7 +621,7 @@ "@corbits/myra": ["@corbits/myra@workspace:agents/myra"], - "@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#f818dcb", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-f818dcb", "sha512-hfJdNa3GjNIV1VbzXPgSoBOf/RFRPj3q8RJTooRfWTra93GSfw3QRJ+dRHeRvxsAUbuiZkAazsMYlQaOm4N8rw=="], + "@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#f186ed0", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-f186ed0", "sha512-wjKVTa26EoUqg8biyC4CumDi6y1hBck6xJTB/oEaTkc1lQ3Foum89KoXGHSvact+N1j9uwLGFEnOfgYp2FFi5Q=="], "@corbits/ollama-adapter": ["@corbits/ollama-adapter@github:corbitsdev/corbits-ollama-adapter#0387eb1", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-ollama-adapter-0387eb1", "sha512-O1dl5lSwWzNGJibQLlpYDFKJZWpstgHX519bl8oe1ALL/KHszT/tBDlpsD9jN3hmQD3jV2L1jNwvOwLOBLtpyw=="], @@ -1947,13 +1947,13 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - "@workbench/hub/@corbits/codex-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#e97d563", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-e97d563", "sha512-YbW3CXt/W+fw+0RHBm8hWXg9m0Gckt/iTDJJ/c60SYhOVOvPVaQ+HscRgq+UxMThtyer9xIBkkL8B8YgdEBfgQ=="], + "@workbench/hub/@corbits/codex-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#f186ed0", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-f186ed0", "sha512-wjKVTa26EoUqg8biyC4CumDi6y1hBck6xJTB/oEaTkc1lQ3Foum89KoXGHSvact+N1j9uwLGFEnOfgYp2FFi5Q=="], "@workbench/hub/@corbits/codex-provider/@corbits/openai-responses": ["@corbits/openai-responses@github:corbitsdev/corbits-openai-responses#7efe000", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-openai-responses-7efe000", "sha512-pH6C8T1WDJjqM+YbnQpofd7HgP5krEj48JCCQK16KI98a6kaz+gffpgdOdQpU3aNVZnFlyADXod1Z4tqUG/3ww=="], "@workbench/hub/@corbits/mailbox/hono-openapi": ["hono-openapi@1.3.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.11.2", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-NLVeVkhKZ3drmQNEIPac8HX8Y54uf1hJAgIM/7MfDsaeVVmB+QILWQxx5x3R3NvRHgedcbEbOCGY2uR7WQYyMw=="], - "@workbench/hub/@corbits/xai-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#e97d563", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-e97d563", "sha512-YbW3CXt/W+fw+0RHBm8hWXg9m0Gckt/iTDJJ/c60SYhOVOvPVaQ+HscRgq+UxMThtyer9xIBkkL8B8YgdEBfgQ=="], + "@workbench/hub/@corbits/xai-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#f186ed0", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-f186ed0", "sha512-wjKVTa26EoUqg8biyC4CumDi6y1hBck6xJTB/oEaTkc1lQ3Foum89KoXGHSvact+N1j9uwLGFEnOfgYp2FFi5Q=="], "@workbench/hub/@corbits/xai-provider/@corbits/openai-responses": ["@corbits/openai-responses@github:corbitsdev/corbits-openai-responses#7efe000", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-openai-responses-7efe000", "sha512-pH6C8T1WDJjqM+YbnQpofd7HgP5krEj48JCCQK16KI98a6kaz+gffpgdOdQpU3aNVZnFlyADXod1Z4tqUG/3ww=="],