From eef29223183fb4dbd2ae470d8a9a1526fe72386f Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Thu, 3 Sep 2026 00:41:46 +0300 Subject: [PATCH] refactor(providers): finish canonical identifier audit --- packages/config-eslint/provider-identifiers.js | 14 +++++++++++--- .../config-eslint/provider-identifiers.test.js | 10 ++++++++++ .../src/__tests__/provider-identifiers.test.ts | 3 +++ packages/types/src/global-settings.ts | 3 ++- src/api/providers/vscode-lm.ts | 10 ++++++++-- src/core/config/importExport.ts | 3 ++- src/integrations/kimi-code/oauth.ts | 7 ++++--- src/integrations/openai-codex/oauth.ts | 7 ++++--- .../settings/ImageGenerationSettings.tsx | 2 +- webview-ui/src/oauth/urls.ts | 5 +++-- 10 files changed, 48 insertions(+), 16 deletions(-) diff --git a/packages/config-eslint/provider-identifiers.js b/packages/config-eslint/provider-identifiers.js index b1c2ebabb4..83968ad96e 100644 --- a/packages/config-eslint/provider-identifiers.js +++ b/packages/config-eslint/provider-identifiers.js @@ -80,7 +80,7 @@ export function createProviderIdentifierConfig({ providerIdentifiers, retiredPro return undefined } - function getProviderExpressionBranches(node) { + function getProviderExpressionChildren(node) { node = unwrapExpression(node) if (node?.type === "LogicalExpression") { @@ -95,6 +95,14 @@ export function createProviderIdentifierConfig({ providerIdentifiers, retiredPro return [node.right] } + if (node?.type === "CallExpression") { + return node.arguments + } + + if (node?.type === "ArrayExpression") { + return node.elements.filter((element) => element !== null) + } + return [] } @@ -119,8 +127,8 @@ export function createProviderIdentifierConfig({ providerIdentifiers, retiredPro }, create(context) { function reportIfRawProvider(node) { - for (const branch of getProviderExpressionBranches(node)) { - reportIfRawProvider(branch) + for (const child of getProviderExpressionChildren(node)) { + reportIfRawProvider(child) } const provider = getRawProvider(node) diff --git a/packages/config-eslint/provider-identifiers.test.js b/packages/config-eslint/provider-identifiers.test.js index 123b9b6a32..de03860b2a 100644 --- a/packages/config-eslint/provider-identifiers.test.js +++ b/packages/config-eslint/provider-identifiers.test.js @@ -200,6 +200,16 @@ ruleTester.run("no-raw-provider-identifiers provider-like values", rule, { }, ], }, + { + code: 'const schema = { imageGenerationProvider: z.enum(["openrouter"]) }', + errors: [ + { + messageId: "useCanonical", + data: { replacement: "providerIdentifiers.openrouter", value: "openrouter" }, + type: "Literal", + }, + ], + }, { code: 'const imageProvider = useGemini ? "gemini" : "openrouter"', errors: [ diff --git a/packages/types/src/__tests__/provider-identifiers.test.ts b/packages/types/src/__tests__/provider-identifiers.test.ts index 29e55b31cf..d9ba4fb799 100644 --- a/packages/types/src/__tests__/provider-identifiers.test.ts +++ b/packages/types/src/__tests__/provider-identifiers.test.ts @@ -21,6 +21,8 @@ import { retiredProviderNamesSchema, } from "../index.js" +// Raw values are intentional here: these fixtures protect the persisted provider identifier contract. +/* eslint-disable zoo/no-raw-provider-identifiers */ const expectedProviderIdentifiers = [ "openrouter", "vercel-ai-gateway", @@ -70,6 +72,7 @@ const expectedRetiredProviderIdentifiers = [ "io-intelligence", "roo", ] +/* eslint-enable zoo/no-raw-provider-identifiers */ describe("provider identifiers", () => { it("preserves active provider serialized values", () => { diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 95f246dbe7..692798d00d 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -14,6 +14,7 @@ import { telemetrySettingsSchema } from "./telemetry.js" import { toolNamesSchema } from "./tool.js" import { type Keys } from "./type-fu.js" import { languagesSchema } from "./vscode.js" +import { providerIdentifiers } from "./provider-identifiers.js" /** * Default delay in milliseconds after writes to allow diagnostics to detect potential problems. @@ -114,7 +115,7 @@ export const globalSettingsSchema = z.object({ dismissedUpsells: z.array(z.string()).optional(), // Image generation settings (experimental) - flattened for simplicity - imageGenerationProvider: z.enum(["openrouter"]).optional(), + imageGenerationProvider: z.enum([providerIdentifiers.openrouter]).optional(), openRouterImageApiKey: z.string().optional(), openRouterImageGenerationSelectedModel: z.string().optional(), diff --git a/src/api/providers/vscode-lm.ts b/src/api/providers/vscode-lm.ts index c657e6c0d6..62bcbf0c27 100644 --- a/src/api/providers/vscode-lm.ts +++ b/src/api/providers/vscode-lm.ts @@ -2,7 +2,13 @@ import { Anthropic } from "@anthropic-ai/sdk" import * as vscode from "vscode" import OpenAI from "openai" -import { type ModelInfo, openAiModelInfoSaneDefaults, vscodeLlmDefaultModelId, vscodeLlmModels } from "@roo-code/types" +import { + type ModelInfo, + openAiModelInfoSaneDefaults, + providerIdentifiers, + vscodeLlmDefaultModelId, + vscodeLlmModels, +} from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" import { SELECTOR_SEPARATOR, stringifyVsCodeLmModelSelector } from "../../shared/vsCodeSelectorUtils" @@ -555,7 +561,7 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan // Fallback when no client is available const fallbackId = this.options.vsCodeLmModelSelector ? stringifyVsCodeLmModelSelector(this.options.vsCodeLmModelSelector) - : "vscode-lm" + : providerIdentifiers.vscodeLm console.debug("Zoo Code : No client available, using fallback model info") diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts index 7b3b5aa231..3c213fedf4 100644 --- a/src/core/config/importExport.ts +++ b/src/core/config/importExport.ts @@ -10,6 +10,7 @@ import { globalSettingsSchema, providerSettingsWithIdSchema, isProviderName, + retiredProviderIdentifiers, type GlobalSettings, type ProviderSettingsWithId, } from "@roo-code/types" @@ -106,7 +107,7 @@ function sanitizeGlobalSettings(rawGlobalSettings: unknown): { let valueToValidate = rawValue - if (key === "imageGenerationProvider" && rawValue === "roo") { + if (key === "imageGenerationProvider" && rawValue === retiredProviderIdentifiers.roo) { warnings.push(`Setting "${path}" used unsupported value "roo" and was cleared during import.`) valueToValidate = undefined } diff --git a/src/integrations/kimi-code/oauth.ts b/src/integrations/kimi-code/oauth.ts index cd28df8b04..0eb9581512 100644 --- a/src/integrations/kimi-code/oauth.ts +++ b/src/integrations/kimi-code/oauth.ts @@ -1,5 +1,6 @@ import type { ExtensionContext } from "vscode" import { z } from "zod" +import { providerIdentifiers } from "@roo-code/types" export const KIMI_CODE_OAUTH_CONFIG = { authHost: "https://auth.kimi.com", @@ -16,7 +17,7 @@ const TOKEN_EXPIRY_BUFFER_MS = 60_000 const OAUTH_REQUEST_TIMEOUT_MS = 30_000 const credentialsSchema = z.object({ - type: z.literal("kimi-code"), + type: z.literal(providerIdentifiers.kimiCode), accessToken: z.string().min(1), refreshToken: z.string().min(1), expiresAt: z.number(), @@ -130,7 +131,7 @@ async function requestDeviceToken(deviceCode: string, signal?: AbortSignal): Pro const tokens = tokenResponseSchema.parse(await response.json()) if (!tokens.refresh_token) throw new Error("Kimi Code OAuth did not return a refresh token") return { - type: "kimi-code", + type: providerIdentifiers.kimiCode, accessToken: tokens.access_token, refreshToken: tokens.refresh_token, expiresAt: Date.now() + tokens.expires_in * 1000, @@ -147,7 +148,7 @@ export async function refreshKimiCodeAccessToken(credentials: KimiCodeCredential if (!response.ok) throw await readOAuthError(response) const tokens = tokenResponseSchema.parse(await response.json()) return { - type: "kimi-code", + type: providerIdentifiers.kimiCode, accessToken: tokens.access_token, refreshToken: tokens.refresh_token ?? credentials.refreshToken, expiresAt: Date.now() + tokens.expires_in * 1000, diff --git a/src/integrations/openai-codex/oauth.ts b/src/integrations/openai-codex/oauth.ts index d87199126c..e11ac4cb58 100644 --- a/src/integrations/openai-codex/oauth.ts +++ b/src/integrations/openai-codex/oauth.ts @@ -3,6 +3,7 @@ import * as http from "http" import { URL } from "url" import type { ExtensionContext } from "vscode" import { z } from "zod" +import { providerIdentifiers } from "@roo-code/types" /** * OpenAI Codex OAuth Configuration @@ -28,7 +29,7 @@ const OPENAI_CODEX_CREDENTIALS_KEY = "openai-codex-oauth-credentials" // Credentials schema const openAiCodexCredentialsSchema = z.object({ - type: z.literal("openai-codex"), + type: z.literal(providerIdentifiers.openaiCodex), access_token: z.string().min(1), refresh_token: z.string().min(1), // expires is in milliseconds since epoch @@ -264,7 +265,7 @@ export async function exchangeCodeForTokens(code: string, codeVerifier: string): }) return { - type: "openai-codex", + type: providerIdentifiers.openaiCodex, access_token: tokenResponse.access_token, refresh_token: tokenResponse.refresh_token, expires: expiresAt, @@ -316,7 +317,7 @@ export async function refreshAccessToken(credentials: OpenAiCodexCredentials): P }) return { - type: "openai-codex", + type: providerIdentifiers.openaiCodex, access_token: tokenResponse.access_token, refresh_token: tokenResponse.refresh_token ?? credentials.refresh_token, expires: expiresAt, diff --git a/webview-ui/src/components/settings/ImageGenerationSettings.tsx b/webview-ui/src/components/settings/ImageGenerationSettings.tsx index f322436a17..fd54054b70 100644 --- a/webview-ui/src/components/settings/ImageGenerationSettings.tsx +++ b/webview-ui/src/components/settings/ImageGenerationSettings.tsx @@ -115,7 +115,7 @@ export const ImageGenerationSettings = ({ value={currentProvider} onChange={(e: any) => handleProviderChange(e.target.value)} className="w-full"> - + OpenRouter diff --git a/webview-ui/src/oauth/urls.ts b/webview-ui/src/oauth/urls.ts index 8d5f7208f3..dca51a87b2 100644 --- a/webview-ui/src/oauth/urls.ts +++ b/webview-ui/src/oauth/urls.ts @@ -1,3 +1,4 @@ +import { providerIdentifiers } from "@roo-code/types" import { Package } from "@roo/package" export function getCallbackUrl(provider: string, uriScheme?: string) { @@ -5,11 +6,11 @@ export function getCallbackUrl(provider: string, uriScheme?: string) { } export function getOpenRouterAuthUrl(uriScheme?: string) { - return `https://openrouter.ai/auth?callback_url=${getCallbackUrl("openrouter", uriScheme)}` + return `https://openrouter.ai/auth?callback_url=${getCallbackUrl(providerIdentifiers.openrouter, uriScheme)}` } export function getRequestyAuthUrl(uriScheme?: string) { - return `https://app.requesty.ai/oauth/authorize?callback_url=${getCallbackUrl("requesty", uriScheme)}` + return `https://app.requesty.ai/oauth/authorize?callback_url=${getCallbackUrl(providerIdentifiers.requesty, uriScheme)}` } const ZOO_CODE_DEFAULT_BASE_URL = "https://www.zoocode.dev"