From 43046ee649f82d9bf7cbdbe80ac206d984b11c64 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sun, 22 Mar 2026 11:31:34 -0300 Subject: [PATCH] fix(3.0.0-rc/batch1): resolve issues #521, #522, #525, #532, #489 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(login): redirect to /dashboard/onboarding when API returns needsSetup:true (#521) - Handle the case where user skips password setup and lands on login - Instead of showing a cryptic error, redirect to onboarding flow fix(api-manager): replace useless 'copy masked key' button with lock tooltip (#522) - Copying a masked key (sk-proj123****abcd) is misleading and useless - Show a lock icon on hover explaining key is only available at creation time - Add i18n key 'keyOnlyAvailableAtCreation' fix(opencode-go): use zen/v1 for API key validation, not zen/go/v1 (#532) - Added testKeyBaseUrl field to RegistryEntry interface - opencode-go: testKeyBaseUrl → zen/v1 (same key authenticates both tiers) - validation.ts: resolveBaseUrl for key testing now prefers testKeyBaseUrl fix(antigravity): return structured 422 error when projectId is missing (#489) - Instead of throwing (crash), executor returns an OpenAI-format error JSON - Client receives message with instruction to reconnect OAuth - Prevents opaque 500 errors in the proxy logs chore: close #525 (OmniRoute = 9router — same project, different name) docs: add Docker password reset comment on #513 with INITIAL_PASSWORD workaround --- open-sse/config/providerRegistry.ts | 4 ++++ open-sse/executors/antigravity.ts | 24 +++++++++++++++---- .../api-manager/ApiManagerPageClient.tsx | 13 ++++------ src/app/login/page.tsx | 5 ++++ src/i18n/messages/en.json | 1 + src/lib/providers/validation.ts | 7 +++++- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/open-sse/config/providerRegistry.ts b/open-sse/config/providerRegistry.ts index 7dc2152795..1df5ac72c0 100644 --- a/open-sse/config/providerRegistry.ts +++ b/open-sse/config/providerRegistry.ts @@ -47,6 +47,8 @@ export interface RegistryEntry { executor: string; baseUrl?: string; baseUrls?: string[]; + /** Override base URL used only for API key validation (e.g., opencode-go validates on zen/v1) */ + testKeyBaseUrl?: string; responsesBaseUrl?: string; urlSuffix?: string; urlBuilder?: (base: string, model: string, stream: boolean) => string; @@ -501,6 +503,8 @@ export const REGISTRY: Record = { format: "openai", executor: "opencode", baseUrl: "https://opencode.ai/zen/go/v1", + // (#532) Key validation must hit the main zen endpoint (same key works for both tiers) + testKeyBaseUrl: "https://opencode.ai/zen/v1", authType: "apikey", authHeader: "Authorization", authPrefix: "Bearer", diff --git a/open-sse/executors/antigravity.ts b/open-sse/executors/antigravity.ts index 2e18d1aa48..fd09aaad3f 100644 --- a/open-sse/executors/antigravity.ts +++ b/open-sse/executors/antigravity.ts @@ -44,12 +44,28 @@ export class AntigravityExecutor extends BaseExecutor { // stale/wrong client-side values causing 404/403 from Cloud Code endpoints. // Opt-in escape hatch: set OMNIROUTE_ALLOW_BODY_PROJECT_OVERRIDE=1. const projectId = - allowBodyProjectOverride && bodyProjectId ? bodyProjectId : credentialsProjectId || bodyProjectId; + allowBodyProjectOverride && bodyProjectId + ? bodyProjectId + : credentialsProjectId || bodyProjectId; if (!projectId) { - throw new Error( - "Missing Google projectId for Antigravity account. Please reconnect OAuth so OmniRoute can fetch your real Cloud Code project (loadCodeAssist)." - ); + // (#489) Return a structured error instead of throwing — gives the client a clear signal + // to show a "Reconnect OAuth" prompt rather than an opaque "Internal Server Error". + const errorMsg = + "Missing Google projectId for Antigravity account. Please reconnect OAuth in Providers → Antigravity so OmniRoute can fetch your Cloud Code project."; + const errorBody = { + error: { + message: errorMsg, + type: "oauth_missing_project_id", + code: "missing_project_id", + }, + }; + const resp = new Response(JSON.stringify(errorBody), { + status: 422, + headers: { "Content-Type": "application/json" }, + }); + // Returning a Response object signals the executor to stop and forward it + return resp as unknown as never; } // Fix contents for Claude models via Antigravity diff --git a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx index f7c88cc631..75e59468bd 100644 --- a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx +++ b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx @@ -523,15 +523,12 @@ export default function ApiManagerPageClient() {
{key.key} - + lock +
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 3b51b6f3b7..e17e0c53b3 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -69,6 +69,11 @@ export default function LoginPage() { router.refresh(); } else { const data = await res.json(); + // (#521) If no password is set, redirect to onboarding instead of showing an error + if (data.needsSetup) { + router.push("/dashboard/onboarding"); + return; + } setError(data.error || t("invalidPassword")); } } catch (err) { diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index a7e5f0cf57..295e794f90 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -281,6 +281,7 @@ "failedUpdatePermissionsRetry": "Failed to update permissions. Please try again.", "unknownProvider": "unknown", "copyMaskedKey": "Copy masked key", + "keyOnlyAvailableAtCreation": "Full key available only at creation time — copy it when you first create the key", "modelsCount": "{count, plural, one {# model} other {# models}}", "lastUsedOn": "Last: {date}", "editPermissions": "Edit permissions", diff --git a/src/lib/providers/validation.ts b/src/lib/providers/validation.ts index dd71ef91f8..ae71e4e857 100644 --- a/src/lib/providers/validation.ts +++ b/src/lib/providers/validation.ts @@ -610,7 +610,12 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi } const modelId = entry.models?.[0]?.id || null; - const baseUrl = resolveBaseUrl(entry, providerSpecificData); + // (#532) Use testKeyBaseUrl if defined — some providers validate keys on a different endpoint + // than where requests are sent (e.g. opencode-go validates on zen/v1, not zen/go/v1) + const validationEntry = entry.testKeyBaseUrl + ? { ...entry, baseUrl: entry.testKeyBaseUrl } + : entry; + const baseUrl = resolveBaseUrl(validationEntry, providerSpecificData); try { if (OPENAI_LIKE_FORMATS.has(entry.format)) {