fix(requesty): mirror a saved Requesty key on in-process profile apply

A saved Requesty profile reaches the env through two producers. The
startup env (buildOpenAICompatibleStartupEnv) writes the key as both
OPENAI_API_KEY and REQUESTY_API_KEY and stamps the requesty route id.
The in-process apply (applyProviderProfileToProcessEnv) wrote only
OPENAI_API_KEY. Provider-flag switch-away recognizes a copied Requesty
key only when the two match, so after applying a keyed profile and then
selecting another provider, the Requesty secret stayed in
OPENAI_API_KEY and was sent to the new endpoint. The in-process apply
now writes the same mirror and route id as startup, like the ApiSmart,
Concentrate, API Route and Command Code profiles in that block.

The README, .env.example and website note now list the US and AP
regional hosts beside EU. Those four URLs are the only ones that
receive REQUESTY_API_KEY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jatmn
2026-11-04 19:53:37 -08:00
co-authored by Claude Opus 5.5
parent 9b56ef2ebc
commit 1ce28be535
5 changed files with 38 additions and 7 deletions
+3 -1
View File
@@ -237,8 +237,10 @@ ANTHROPIC_API_KEY=sk-ant-your-key-here
# For Requesty, prefer its dedicated key. Raw env setup must also set
# CLAUDE_CODE_USE_OPENAI=1 (above), OPENAI_BASE_URL and OPENAI_MODEL.
# OPENAI_API_KEY remains supported.
# For EU processing, replace the OPENAI_BASE_URL below with
# For regional processing, replace the OPENAI_BASE_URL below with one of:
# https://router.eu.requesty.ai/v1
# https://router.us.requesty.ai/v1
# https://router.ap.requesty.ai/v1
# REQUESTY_API_KEY=your-requesty-key-here
# OPENAI_BASE_URL=https://router.requesty.ai/v1
# OPENAI_MODEL=openai/gpt-5-mini
+1 -1
View File
@@ -381,7 +381,7 @@ Advanced and source-build guides:
| OrcaRouter | `/provider`, or `ORCAROUTER_API_KEY` with `--provider orcarouter` | OpenAI-compatible model routing gateway at `https://api.orcarouter.ai/v1`, defaults to `openai/gpt-5.5`, with `orcarouter/auto` smart routing and authenticated model discovery. Existing environment setups must replace `OPENAI_API_KEY` with `ORCAROUTER_API_KEY`, even when a custom authentication header supplies the request credential. Custom headers remain supported. |
| Runcrate | `/provider` or `RUNCRATE_API_KEY` with `--provider runcrate` | OpenAI-compatible aggregating gateway at `https://api.runcrate.ai/v1`, defaults to `deepseek-ai/DeepSeek-V4.1-Flash`, with curated open models (DeepSeek, GLM, Qwen, GPT-OSS). Existing environment setups must replace `OPENAI_API_KEY` with `RUNCRATE_API_KEY`. Custom headers remain supported. |
| Hicap | `/provider` or OpenAI-compatible env vars | Uses `api-key` auth, discovers models from unauthenticated `/models`, and supports Responses mode for `gpt-` models |
| Requesty | `/provider` or OpenAI-compatible env vars | OpenAI-compatible gateway at `https://router.requesty.ai/v1` (EU: `https://router.eu.requesty.ai/v1`); `/provider` and `--provider requesty` default to `openai/gpt-5-mini`, while raw env setup must set `CLAUDE_CODE_USE_OPENAI=1`, `OPENAI_BASE_URL`, and `OPENAI_MODEL`; accepts `REQUESTY_API_KEY` or `OPENAI_API_KEY` after the route is selected and discovers chat models from the public `/models` catalog |
| Requesty | `/provider` or OpenAI-compatible env vars | OpenAI-compatible gateway at `https://router.requesty.ai/v1` (regional: `https://router.eu.requesty.ai/v1`, `https://router.us.requesty.ai/v1`, `https://router.ap.requesty.ai/v1`); `/provider` and `--provider requesty` default to `openai/gpt-5-mini`, while raw env setup must set `CLAUDE_CODE_USE_OPENAI=1`, `OPENAI_BASE_URL`, and `OPENAI_MODEL`; accepts `REQUESTY_API_KEY` or `OPENAI_API_KEY` after the route is selected and discovers chat models from the public `/models` catalog |
| Fireworks AI | `/provider` or env vars | First-class provider with 276 curated models (DeepSeek, Qwen, Llama, Gemma, and more); uses `FIREWORKS_API_KEY` |
| LongCat | `/provider` or env vars | Meituan LongCat OpenAI-compatible API at `https://api.longcat.chat/openai/v1`; uses `LONGCAT_API_KEY` and defaults to `LongCat-2.0` |
| ClinePass | `/provider` or env vars | AI model gateway with usage limits (5hr, weekly, monthly); uses `CLINE_API_KEY` at `https://api.cline.bot/api/v1` |
+20
View File
@@ -2673,6 +2673,26 @@ describe('applyProviderProfileToProcessEnv', () => {
).toBe('selected-new')
})
test('keyed Requesty profile apply matches startup so a provider switch drops its key', async () => {
const { applyProviderProfileToProcessEnv } =
await importFreshProviderProfileModules()
const { applyProviderFlag } = await import('./providerFlag.js')
for (const provider of ['openai', 'openrouter']) {
applyProviderProfileToProcessEnv(
buildRequestyProfile({ apiKey: 'requesty-secret' }),
)
expect(process.env.OPENAI_API_KEY).toBe('requesty-secret')
expect(process.env.REQUESTY_API_KEY).toBe('requesty-secret')
expect(process.env.CLAUDE_CODE_PROVIDER_ROUTE_ID).toBe('requesty')
expect(applyProviderFlag(provider, []).error).toBeUndefined()
expect(process.env.OPENAI_BASE_URL).not.toContain('requesty')
expect(process.env.OPENAI_API_KEY).toBeUndefined()
}
})
test('keyless canonical Requesty profile keeps its ambient dedicated key', async () => {
const { applyProviderProfileToProcessEnv } =
await importFreshProviderProfileModules()
+13 -4
View File
@@ -1458,6 +1458,11 @@ export function applyProviderProfileToProcessEnv(
if (isApiRouteProfile(profile)) {
openAIProfileEnv.API_ROUTE_API_KEY = profile.apiKey
}
// Same mirror as buildOpenAICompatibleStartupEnv: provider-flag
// switch-away recognizes a copied Requesty key only by this pair.
if (isRequestyProfile(profile)) {
openAIProfileEnv.REQUESTY_API_KEY = profile.apiKey
}
if (isCommandcodeProfile(profile)) {
openAIProfileEnv.CMD_API_KEY = profile.apiKey
}
@@ -1592,10 +1597,14 @@ export function applyProviderProfileToProcessEnv(
// REQUESTY_API_KEY is profile-managed: installing this env clears the
// ambient value so a keyed profile's saved key wins. A keyless canonical
// profile adopts the ambient key here; a retargeted profile gets neither.
if (isRequestyProfile(profile) && !profile.apiKey) {
const ambientRequestyKey = sanitizeApiKey(process.env.REQUESTY_API_KEY)
if (ambientRequestyKey) {
openAIProfileEnv.REQUESTY_API_KEY = ambientRequestyKey
// Stamp the route identity the startup env also writes.
if (route.routeId === 'requesty') {
openAIProfileEnv.CLAUDE_CODE_PROVIDER_ROUTE_ID = 'requesty'
if (isRequestyProfile(profile) && !profile.apiKey) {
const ambientRequestyKey = sanitizeApiKey(process.env.REQUESTY_API_KEY)
if (ambientRequestyKey) {
openAIProfileEnv.REQUESTY_API_KEY = ambientRequestyKey
}
}
}
if (isCommandcodeProfile(profile)) {
+1 -1
View File
@@ -146,7 +146,7 @@ export const providers: Provider[] = [
group: 'gateways',
setup: '/provider or OpenAI-compatible env vars',
envVars: ['REQUESTY_API_KEY', 'OPENAI_API_KEY'],
notes: 'OpenAI-compatible gateway at https://router.requesty.ai/v1 (EU: https://router.eu.requesty.ai/v1); /provider and --provider requesty default to openai/gpt-5-mini. Uses public discovery of chat models.',
notes: 'OpenAI-compatible gateway at https://router.requesty.ai/v1 (regional: https://router.eu.requesty.ai/v1, https://router.us.requesty.ai/v1, https://router.ap.requesty.ai/v1); /provider and --provider requesty default to openai/gpt-5-mini. Uses public discovery of chat models.',
},
{
id: 'llmtr',