mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 20:58:14 -04:00
fix(providers): drop saved Requesty auth and headers on shell retarget
The Requesty retarget boundary only gated the generic OPENAI_API_KEY selection. Requesty supports custom auth headers, so a saved profile's credential can also ride in the OPENAI_AUTH_* trio or ANTHROPIC_CUSTOM_HEADERS, and a shell OPENAI_BASE_URL retarget still sent those to the new endpoint. Apply the existing Runcrate retarget handling to a Requesty retarget away from its saved endpoint, so every credential channel follows the same rule. A canonical regional switch and a saved non-canonical profile relaunched at its own URL keep their saved auth, matching profile apply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3871,3 +3871,75 @@ test('a shell URL retarget to Requesty does not adopt another profile key as the
|
|||||||
'shell-requesty-key',
|
'shell-requesty-key',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('a shell URL retarget drops a saved Requesty profile custom auth and headers', async () => {
|
||||||
|
// Requesty supports custom auth headers, so its saved credential can ride in
|
||||||
|
// the custom-auth trio or custom headers as well as the API key aliases.
|
||||||
|
const persisted = profile('openai', {
|
||||||
|
CLAUDE_CODE_PROVIDER_ROUTE_ID: 'requesty',
|
||||||
|
OPENAI_BASE_URL: 'https://router.requesty.ai/v1',
|
||||||
|
OPENAI_MODEL: 'openai/gpt-5-mini',
|
||||||
|
OPENAI_API_KEY: 'saved-requesty-key',
|
||||||
|
REQUESTY_API_KEY: 'saved-requesty-key',
|
||||||
|
OPENAI_AUTH_HEADER: 'X-Requesty-Auth',
|
||||||
|
OPENAI_AUTH_SCHEME: 'raw',
|
||||||
|
OPENAI_AUTH_HEADER_VALUE: 'saved-auth-secret',
|
||||||
|
ANTHROPIC_CUSTOM_HEADERS: 'X-Requesty-Auth: saved-header-secret',
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const shellBaseUrl of [
|
||||||
|
'https://example.test/v1',
|
||||||
|
'http://router.requesty.ai/v1',
|
||||||
|
]) {
|
||||||
|
const env = await buildStartupEnvFromProfile({
|
||||||
|
persisted,
|
||||||
|
processEnv: { OPENAI_BASE_URL: shellBaseUrl },
|
||||||
|
})
|
||||||
|
assert.equal(env.OPENAI_BASE_URL, shellBaseUrl)
|
||||||
|
assert.equal(env.OPENAI_API_KEY, undefined)
|
||||||
|
assert.equal(env.REQUESTY_API_KEY, undefined)
|
||||||
|
assert.equal(env.OPENAI_AUTH_HEADER, undefined)
|
||||||
|
assert.equal(env.OPENAI_AUTH_SCHEME, undefined)
|
||||||
|
assert.equal(env.OPENAI_AUTH_HEADER_VALUE, undefined)
|
||||||
|
assert.equal(env.ANTHROPIC_CUSTOM_HEADERS, undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit shell credentials still follow the retargeted endpoint.
|
||||||
|
const explicit = await buildStartupEnvFromProfile({
|
||||||
|
persisted,
|
||||||
|
processEnv: {
|
||||||
|
OPENAI_BASE_URL: 'https://example.test/v1',
|
||||||
|
OPENAI_API_KEY: 'shell-key',
|
||||||
|
OPENAI_AUTH_HEADER: 'X-Shell-Auth',
|
||||||
|
OPENAI_AUTH_SCHEME: 'bearer',
|
||||||
|
OPENAI_AUTH_HEADER_VALUE: 'shell-auth-secret',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.equal(explicit.OPENAI_API_KEY, 'shell-key')
|
||||||
|
assert.equal(explicit.OPENAI_AUTH_HEADER, 'X-Shell-Auth')
|
||||||
|
assert.equal(explicit.OPENAI_AUTH_SCHEME, 'bearer')
|
||||||
|
assert.equal(explicit.OPENAI_AUTH_HEADER_VALUE, 'shell-auth-secret')
|
||||||
|
|
||||||
|
// A canonical regional host is still Requesty: the saved credentials follow.
|
||||||
|
const regional = await buildStartupEnvFromProfile({
|
||||||
|
persisted,
|
||||||
|
processEnv: { OPENAI_BASE_URL: 'https://router.eu.requesty.ai/v1' },
|
||||||
|
})
|
||||||
|
assert.equal(regional.REQUESTY_API_KEY, 'saved-requesty-key')
|
||||||
|
assert.equal(regional.OPENAI_AUTH_HEADER_VALUE, 'saved-auth-secret')
|
||||||
|
assert.equal(regional.ANTHROPIC_CUSTOM_HEADERS, 'X-Requesty-Auth: saved-header-secret')
|
||||||
|
|
||||||
|
// A profile saved for its own non-canonical URL keeps the auth it was saved
|
||||||
|
// with when relaunched there, matching profile apply.
|
||||||
|
const ownProxy = await buildStartupEnvFromProfile({
|
||||||
|
persisted: profile('openai', {
|
||||||
|
...persisted.env,
|
||||||
|
OPENAI_BASE_URL: 'https://requesty-proxy.example/v1',
|
||||||
|
OPENAI_API_KEY: undefined,
|
||||||
|
REQUESTY_API_KEY: undefined,
|
||||||
|
}),
|
||||||
|
processEnv: {},
|
||||||
|
})
|
||||||
|
assert.equal(ownProxy.OPENAI_AUTH_HEADER_VALUE, 'saved-auth-secret')
|
||||||
|
assert.equal(ownProxy.ANTHROPIC_CUSTOM_HEADERS, 'X-Requesty-Auth: saved-header-secret')
|
||||||
|
})
|
||||||
|
|||||||
@@ -2275,6 +2275,18 @@ export async function buildLaunchEnv(options: {
|
|||||||
(persistedEnv.CLAUDE_CODE_PROVIDER_ROUTE_ID === 'runcrate' ||
|
(persistedEnv.CLAUDE_CODE_PROVIDER_ROUTE_ID === 'runcrate' ||
|
||||||
resolveRouteIdFromBaseUrl(persistedOpenAIBaseUrl) === 'runcrate') &&
|
resolveRouteIdFromBaseUrl(persistedOpenAIBaseUrl) === 'runcrate') &&
|
||||||
!isSameConfiguredEndpoint(env.OPENAI_BASE_URL, persistedOpenAIBaseUrl)
|
!isSameConfiguredEndpoint(env.OPENAI_BASE_URL, persistedOpenAIBaseUrl)
|
||||||
|
// Requesty supports custom auth headers, so its saved credential can also
|
||||||
|
// ride in the custom-auth trio or custom headers. Those belong to the
|
||||||
|
// endpoint the profile was saved for: a shell retarget away from it drops
|
||||||
|
// them, while a saved non-canonical profile relaunched at its own URL keeps
|
||||||
|
// them, matching profile apply.
|
||||||
|
const shellRetargetedRequestyProfile =
|
||||||
|
retargetedRequestyProfile &&
|
||||||
|
!isSameConfiguredEndpoint(env.OPENAI_BASE_URL, persistedOpenAIBaseUrl)
|
||||||
|
// Saved profiles whose credentials must not follow a shell URL retarget on
|
||||||
|
// any channel: generic key, custom-auth trio, or custom headers.
|
||||||
|
const retargetedCredentialBoundProfile =
|
||||||
|
retargetedRuncrateProfile || shellRetargetedRequestyProfile
|
||||||
// A profile-applied Runcrate key also rides in the generic alias, and a pane
|
// A profile-applied Runcrate key also rides in the generic alias, and a pane
|
||||||
// can inherit it. Only a shell credential without Runcrate's managed marker
|
// can inherit it. Only a shell credential without Runcrate's managed marker
|
||||||
// may follow a retarget.
|
// may follow a retarget.
|
||||||
@@ -2376,10 +2388,10 @@ export async function buildLaunchEnv(options: {
|
|||||||
isNoncanonicalMergeGatewayLaunch ||
|
isNoncanonicalMergeGatewayLaunch ||
|
||||||
isNoncanonicalCommandcodeLaunch ||
|
isNoncanonicalCommandcodeLaunch ||
|
||||||
isNoncanonicalApiRouteLaunch
|
isNoncanonicalApiRouteLaunch
|
||||||
if (isNoncanonicalDedicatedOpenAILaunch || retargetedRuncrateProfile) {
|
if (isNoncanonicalDedicatedOpenAILaunch || retargetedCredentialBoundProfile) {
|
||||||
delete env.OPENAI_API_KEY
|
delete env.OPENAI_API_KEY
|
||||||
delete env.OPENAI_API_KEYS
|
delete env.OPENAI_API_KEYS
|
||||||
if (retargetedRuncrateProfile) {
|
if (retargetedCredentialBoundProfile) {
|
||||||
// A retargeted launch keeps no identity for the endpoint it now points
|
// A retargeted launch keeps no identity for the endpoint it now points
|
||||||
// at, so only the explicit shell credential may follow it.
|
// at, so only the explicit shell credential may follow it.
|
||||||
const shellCredential = resolveOpenAICredentialEnvSelection(shellCredentialSource)
|
const shellCredential = resolveOpenAICredentialEnvSelection(shellCredentialSource)
|
||||||
@@ -2402,7 +2414,7 @@ export async function buildLaunchEnv(options: {
|
|||||||
env[persistedCredential.envVar] = persistedCredential.value
|
env[persistedCredential.envVar] = persistedCredential.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (retargetedRuncrateProfile) {
|
if (retargetedCredentialBoundProfile) {
|
||||||
// A retargeted launch keeps no saved custom-auth trio: it belongs to the
|
// A retargeted launch keeps no saved custom-auth trio: it belongs to the
|
||||||
// endpoint the profile was saved for. Provenance comes from the marker a
|
// endpoint the profile was saved for. Provenance comes from the marker a
|
||||||
// profile application wrote, not from the values themselves: the saved
|
// profile application wrote, not from the values themselves: the saved
|
||||||
@@ -2766,9 +2778,10 @@ export async function buildLaunchEnv(options: {
|
|||||||
// and is sent on every request. So an ambient value must be withheld from a
|
// and is sent on every request. So an ambient value must be withheld from a
|
||||||
// non-canonical aimlapi/apismart launch exactly like the API key and the
|
// non-canonical aimlapi/apismart launch exactly like the API key and the
|
||||||
// custom-auth trio; only headers the profile itself persisted are restored.
|
// custom-auth trio; only headers the profile itself persisted are restored.
|
||||||
// A retargeted Runcrate launch keeps no headers either: both ambient and
|
// A retargeted Runcrate or Requesty launch keeps no headers either: both
|
||||||
// persisted values belong to the endpoint the profile was saved for.
|
// ambient and persisted values belong to the endpoint the profile was saved
|
||||||
const customHeaders = retargetedRuncrateProfile
|
// for.
|
||||||
|
const customHeaders = retargetedCredentialBoundProfile
|
||||||
? undefined
|
? undefined
|
||||||
: isNoncanonicalDedicatedOpenAILaunch
|
: isNoncanonicalDedicatedOpenAILaunch
|
||||||
? persistedCustomHeaders
|
? persistedCustomHeaders
|
||||||
|
|||||||
Reference in New Issue
Block a user