From 00d407fea22fd745d1c8935bd89f3687b4b6b1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Thu, 19 Mar 2026 11:59:29 -0300 Subject: [PATCH] feat: migrate openai codex plan from legacy extension --- .../src/legacy-migration/legacy-types.ts | 18 ++++++ .../src/legacy-migration/migration-service.ts | 61 +++++++++++++++++-- .../src/legacy-migration/provider-mapping.ts | 39 ++++++++++-- 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/packages/kilo-vscode/src/legacy-migration/legacy-types.ts b/packages/kilo-vscode/src/legacy-migration/legacy-types.ts index 5bd11948a42..3f3af3018f9 100644 --- a/packages/kilo-vscode/src/legacy-migration/legacy-types.ts +++ b/packages/kilo-vscode/src/legacy-migration/legacy-types.ts @@ -171,6 +171,24 @@ export interface LegacyProviderSettings { // Synthetic syntheticApiKey?: string + // NanoGPT + nanoGptApiKey?: string + nanoGptModelId?: string + + // Poe + poeApiKey?: string + poeModelId?: string + + // AiHubMix + aihubmixApiKey?: string + aihubmixModelId?: string + aihubmixBaseUrl?: string + + // ZenMux + zenmuxApiKey?: string + zenmuxModelId?: string + zenmuxBaseUrl?: string + // Allow dynamic property access for provider-mapping lookups [key: string]: unknown } diff --git a/packages/kilo-vscode/src/legacy-migration/migration-service.ts b/packages/kilo-vscode/src/legacy-migration/migration-service.ts index 6ab8f576b49..20a58437fef 100644 --- a/packages/kilo-vscode/src/legacy-migration/migration-service.ts +++ b/packages/kilo-vscode/src/legacy-migration/migration-service.ts @@ -37,6 +37,7 @@ import type { // --------------------------------------------------------------------------- const SECRET_KEY = "roo_cline_config_api_config" +const CODEX_OAUTH_SECRET_KEY = "openai-codex-oauth-credentials" const MIGRATION_STATUS_KEY = "kilo.legacyMigrationStatus" type MigrationStatus = "completed" | "skipped" @@ -67,7 +68,11 @@ export async function detectLegacyData(context: vscode.ExtensionContext): Promis const customModes = await readLegacyCustomModes(context) const settings = readLegacySettings(context) - const providers = buildProviderList(profiles) + const oauthProviders = new Set() + const codexRaw = await context.secrets.get(CODEX_OAUTH_SECRET_KEY) + if (codexRaw) oauthProviders.add("openai-codex") + + const providers = buildProviderList(profiles, oauthProviders) const mcpServers = buildMcpServerList(mcpSettings) const modes = buildCustomModeList(customModes) const defaultModel = resolveDefaultModel(profiles) @@ -137,7 +142,7 @@ export async function migrate( continue } onProgress(profileName, "migrating") - const result = await migrateProvider(profileName, settings, client) + const result = await migrateProvider(context, profileName, settings, client) results.push(result) onProgress(profileName, result.status, result.message) } @@ -245,6 +250,7 @@ export async function migrate( */ export async function clearLegacyData(context: vscode.ExtensionContext): Promise { await context.secrets.delete(SECRET_KEY) + await context.secrets.delete(CODEX_OAUTH_SECRET_KEY) const legacyStateKeys = [ "kilo-code.allowedCommands", @@ -308,6 +314,7 @@ export async function clearLegacyData(context: vscode.ExtensionContext): Promise // --------------------------------------------------------------------------- async function migrateProvider( + context: vscode.ExtensionContext, profileName: string, settings: LegacyProviderSettings, client: KiloClient, @@ -336,6 +343,16 @@ async function migrateProvider( } } + // OAuth providers store credentials in a separate VS Code secret + if (mapping.oauthSecretKey) { + const creds = await readOAuthCredentials(context, mapping.oauthSecretKey) + if (!creds) { + return { item: profileName, category: "provider", status: "warning", message: "No OAuth credentials found" } + } + await client.auth.set({ providerID: mapping.id, auth: { type: "oauth" as const, ...creds } }) + return { item: profileName, category: "provider", status: "success" } + } + const apiKey = settings[mapping.key] as string | undefined if (!apiKey) { return { item: profileName, category: "provider", status: "warning", message: "No API key found in profile" } @@ -699,6 +716,35 @@ function convertCustomMode(mode: LegacyCustomMode): AgentConfig { } } +// --------------------------------------------------------------------------- +// Internal — OAuth credential helpers +// --------------------------------------------------------------------------- + +/** + * Reads OAuth credentials stored in a separate VS Code secret (e.g. openai-codex-oauth-credentials). + * Returns the fields needed by the CLI's Auth.Oauth type, or null if absent/malformed. + */ +async function readOAuthCredentials( + context: vscode.ExtensionContext, + secretKey: string, +): Promise<{ access: string; refresh: string; expires: number; accountId?: string } | null> { + const raw = await context.secrets.get(secretKey) + if (!raw) return null + const parsed = (() => { + try { + return JSON.parse(raw) as Record + } catch { + return null + } + })() + if (!parsed) return null + const access = parsed.access_token as string | undefined + const refresh = parsed.refresh_token as string | undefined + const expires = parsed.expires as number | undefined + if (!access || !refresh || expires === undefined) return null + return { access, refresh, expires, accountId: parsed.accountId as string | undefined } +} + // --------------------------------------------------------------------------- // Internal — reading legacy data from storage // --------------------------------------------------------------------------- @@ -921,7 +967,10 @@ function parseCustomModesYaml(text: string): LegacyCustomMode[] | null { // Internal — building display lists for the wizard // --------------------------------------------------------------------------- -function buildProviderList(profiles: LegacyProviderProfiles | null): MigrationProviderInfo[] { +function buildProviderList( + profiles: LegacyProviderProfiles | null, + oauthProviders: Set, +): MigrationProviderInfo[] { if (!profiles?.apiConfigs) return [] return Object.entries(profiles.apiConfigs).map(([profileName, settings]) => { @@ -932,7 +981,11 @@ function buildProviderList(profiles: LegacyProviderProfiles | null): MigrationPr const modelField = mapping?.modelField ?? "apiModelId" const model = settings[modelField] as string | undefined - const hasApiKey = mapping ? Boolean(settings[mapping.key]) : false + const hasApiKey = mapping?.oauthSecretKey + ? oauthProviders.has(provider) + : mapping + ? Boolean(settings[mapping.key]) + : false return { profileName, diff --git a/packages/kilo-vscode/src/legacy-migration/provider-mapping.ts b/packages/kilo-vscode/src/legacy-migration/provider-mapping.ts index 817cdeb9775..9038dbf4d56 100644 --- a/packages/kilo-vscode/src/legacy-migration/provider-mapping.ts +++ b/packages/kilo-vscode/src/legacy-migration/provider-mapping.ts @@ -18,6 +18,8 @@ export interface ProviderMapping { urlField?: string /** Field holding an organization/account ID (used for OAuth-style auth) */ organizationIdField?: string + /** VS Code secret key holding OAuth credentials stored separately from the provider profile */ + oauthSecretKey?: string } /** @@ -240,6 +242,38 @@ export const PROVIDER_MAP: Record = { key: "syntheticApiKey", name: "Synthetic", }, + "openai-codex": { + id: "openai", + key: "", + name: "OpenAI (ChatGPT Plus/Pro)", + oauthSecretKey: "openai-codex-oauth-credentials", + }, + "nano-gpt": { + id: "nano-gpt", + key: "nanoGptApiKey", + name: "NanoGPT", + modelField: "nanoGptModelId", + }, + poe: { + id: "poe", + key: "poeApiKey", + name: "Poe", + modelField: "poeModelId", + }, + aihubmix: { + id: "aihubmix", + key: "aihubmixApiKey", + name: "AiHubMix", + modelField: "aihubmixModelId", + urlField: "aihubmixBaseUrl", + }, + zenmux: { + id: "zenmux", + key: "zenmuxApiKey", + name: "ZenMux", + modelField: "zenmuxModelId", + urlField: "zenmuxBaseUrl", + }, } /** Providers that have no equivalent in the new CLI backend */ @@ -248,16 +282,11 @@ export const UNSUPPORTED_PROVIDERS = new Set([ "human-relay", "vscode-lm", "claude-code", - "openai-codex", "qwen-code", "virtual-quota-fallback", "glama", "roo", - "nano-gpt", - "poe", - "aihubmix", "apertis", - "zenmux", ]) /** Built-in default mode slugs that should not be migrated */