diff --git a/src/app/api/cli-tools/codex-settings/route.ts b/src/app/api/cli-tools/codex-settings/route.ts index 1d852de62e..6067f52326 100644 --- a/src/app/api/cli-tools/codex-settings/route.ts +++ b/src/app/api/cli-tools/codex-settings/route.ts @@ -12,6 +12,7 @@ import { createMultiBackup } from "@/shared/services/backupService"; import { saveCliToolLastConfigured, deleteCliToolLastConfigured } from "@/lib/db/cliToolState"; import { cliModelConfigSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; +import { getApiKeyById } from "@/lib/localDb"; const getCodexConfigPath = () => getCliConfigPaths("codex").config; const getCodexAuthPath = () => getCliConfigPaths("codex").auth; @@ -166,7 +167,8 @@ export async function POST(request: Request) { if (isValidationFailure(validation)) { return NextResponse.json({ error: validation.error }, { status: 400 }); } - const { baseUrl, apiKey, model } = validation.data; + const { baseUrl, model } = validation.data; + let { apiKey } = validation.data; if (!apiKey) { return NextResponse.json( { error: "baseUrl, apiKey and model are required" }, @@ -174,6 +176,21 @@ export async function POST(request: Request) { ); } + // (#549) Resolve real key from DB if keyId was provided. + // The dashboard sends masked key strings — resolving by ID guarantees + // we always write the full key value to the config file. + const keyId = typeof rawBody?.keyId === "string" ? rawBody.keyId.trim() : null; + if (keyId) { + try { + const keyRecord = await getApiKeyById(keyId); + if (keyRecord?.key) { + apiKey = keyRecord.key as string; + } + } catch { + // Non-critical: fall back to whatever value was in apiKey + } + } + const codexDir = getCodexDir(); const configPath = getCodexConfigPath(); const authPath = getCodexAuthPath(); diff --git a/src/app/api/cli-tools/droid-settings/route.ts b/src/app/api/cli-tools/droid-settings/route.ts index 8bbec50c17..3e3be5fdb4 100644 --- a/src/app/api/cli-tools/droid-settings/route.ts +++ b/src/app/api/cli-tools/droid-settings/route.ts @@ -12,6 +12,7 @@ import { createBackup } from "@/shared/services/backupService"; import { saveCliToolLastConfigured, deleteCliToolLastConfigured } from "@/lib/db/cliToolState"; import { cliModelConfigSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; +import { getApiKeyById } from "@/lib/localDb"; const getDroidSettingsPath = () => getCliPrimaryConfigPath("droid"); const getDroidDir = () => path.dirname(getDroidSettingsPath()); @@ -101,7 +102,21 @@ export async function POST(request: Request) { if (isValidationFailure(validation)) { return NextResponse.json({ error: validation.error }, { status: 400 }); } - const { baseUrl, apiKey, model } = validation.data; + const { baseUrl, model } = validation.data; + let { apiKey } = validation.data; + + // (#549) Resolve real key from DB if keyId was provided. + const keyId = typeof rawBody?.keyId === "string" ? rawBody.keyId.trim() : null; + if (keyId) { + try { + const keyRecord = await getApiKeyById(keyId); + if (keyRecord?.key) { + apiKey = keyRecord.key as string; + } + } catch { + // Non-critical: fall back to whatever value was in apiKey + } + } const droidDir = getDroidDir(); const settingsPath = getDroidSettingsPath(); diff --git a/src/app/api/cli-tools/kilo-settings/route.ts b/src/app/api/cli-tools/kilo-settings/route.ts index 983b96a75f..0974ee5455 100644 --- a/src/app/api/cli-tools/kilo-settings/route.ts +++ b/src/app/api/cli-tools/kilo-settings/route.ts @@ -9,6 +9,7 @@ import { createBackup } from "@/shared/services/backupService"; import { saveCliToolLastConfigured, deleteCliToolLastConfigured } from "@/lib/db/cliToolState"; import { cliModelConfigSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; +import { getApiKeyById } from "@/lib/localDb"; const KILO_DATA_DIR = path.join(os.homedir(), ".local", "share", "kilo"); const AUTH_PATH = path.join(KILO_DATA_DIR, "auth.json"); @@ -133,7 +134,21 @@ export async function POST(request) { if (isValidationFailure(validation)) { return NextResponse.json({ error: validation.error }, { status: 400 }); } - const { baseUrl, apiKey, model } = validation.data; + const { baseUrl, model } = validation.data; + let { apiKey } = validation.data; + + // (#549) Resolve real key from DB if keyId was provided. + const keyId = typeof rawBody?.keyId === "string" ? rawBody.keyId.trim() : null; + if (keyId) { + try { + const keyRecord = await getApiKeyById(keyId); + if (keyRecord?.key) { + apiKey = keyRecord.key as string; + } + } catch { + // Non-critical: fall back to whatever value was in apiKey + } + } // Ensure directories exist await fs.mkdir(KILO_DATA_DIR, { recursive: true });