From 2ab980cbf88f72c3fe2497514258d0d854fda0fc Mon Sep 17 00:00:00 2001 From: chandlerm923 <319688110+chandlerm923@users.noreply.github.com> Date: Sat, 22 Aug 2026 15:01:44 +0900 Subject: [PATCH] fix(core): register only credential-shaped env names for integrations models.dev lists provider env vars in setup order, so non-secret values (account id, host, base url) that precede the actual key get registered as the env auth method and can win connection resolution's first-set-wins pick. Filter registered names to *_API_KEY/*_TOKEN/*_PAT when any match, falling back to the full list otherwise. Fixes #44065 --- packages/core/src/plugin/models-dev.ts | 15 ++++++++++++++- .../core/test/plugin/fixtures/models-dev.json | 6 ++++++ packages/core/test/plugin/models-dev.test.ts | 13 +++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/core/src/plugin/models-dev.ts b/packages/core/src/plugin/models-dev.ts index 075a6ed093c6..17b1c03c2cb7 100644 --- a/packages/core/src/plugin/models-dev.ts +++ b/packages/core/src/plugin/models-dev.ts @@ -10,6 +10,19 @@ function released(date: string) { return Number.isFinite(time) ? time : 0 } +const CREDENTIAL_ENV_SUFFIXES = ["_API_KEY", "_TOKEN", "_PAT"] + +// models.dev lists env vars in setup order, so account/host/region/project names +// that configure a provider often precede the actual credential (e.g. Cloudflare's +// `[CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_KEY]`). Connection resolution picks the +// first set var, so an unfiltered list can hand a non-secret value out as the +// bearer key. Narrow to credential-shaped names when any exist; otherwise fall +// back to the full list rather than guess wrong for an unfamiliar naming scheme. +function credentialEnvNames(names: readonly string[]) { + const credentials = names.filter((name) => CREDENTIAL_ENV_SUFFIXES.some((suffix) => name.endsWith(suffix))) + return credentials.length > 0 ? credentials : [...names] +} + function cost(input: ModelsDev.Model["cost"]): ModelV2Info["cost"] { const base = { input: input?.input ?? 0, @@ -134,7 +147,7 @@ export const ModelsDevPlugin = define({ }) integrations.method.update({ integrationID, - method: { type: "env", names: [...item.env] }, + method: { type: "env", names: credentialEnvNames(item.env) }, }) } }), diff --git a/packages/core/test/plugin/fixtures/models-dev.json b/packages/core/test/plugin/fixtures/models-dev.json index fb8f2622be35..e307c35ed736 100644 --- a/packages/core/test/plugin/fixtures/models-dev.json +++ b/packages/core/test/plugin/fixtures/models-dev.json @@ -10,5 +10,11 @@ "name": "Local", "env": [], "models": {} + }, + "cloudy": { + "id": "cloudy", + "name": "Cloudy", + "env": ["CLOUDY_ACCOUNT_ID", "CLOUDY_API_KEY"], + "models": {} } } diff --git a/packages/core/test/plugin/models-dev.test.ts b/packages/core/test/plugin/models-dev.test.ts index 6c0f7e070296..4b3d15bf2f37 100644 --- a/packages/core/test/plugin/models-dev.test.ts +++ b/packages/core/test/plugin/models-dev.test.ts @@ -158,6 +158,19 @@ describe("ModelsDevPlugin", () => { ], connections: [], }), + new Integration.Info({ + id: Integration.ID.make("cloudy"), + name: "Cloudy", + methods: [ + { type: "key" }, + { + // CLOUDY_ACCOUNT_ID is not a credential; only the key-shaped name is registered. + type: "env", + names: ["CLOUDY_API_KEY"], + }, + ], + connections: [], + }), ]) }).pipe(Effect.provide(AppNodeBuilder.build(ModelsDev.node))), (previous) =>