mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 20:58:14 -04:00
fix(providers): bind saved Requesty profile keys to the canonical endpoint
Requesty's dedicated key was registered in the gateway descriptor, the request-time credential gate and the .env allowlist, but not in the saved-profile credential lifecycle that LLMTR and API Route use. So: - a keyed `requesty` profile retargeted to a non-Requesty URL installed and persisted its saved key as OPENAI_API_KEY for that URL; - an ambient REQUESTY_API_KEY overrode a canonical profile's saved key, both on profile application and on relaunch; - tmux teammates did not receive REQUESTY_API_KEY. Withhold the saved key for retargeted Requesty profiles in profile application and startup persistence, manage REQUESTY_API_KEY like the other profile dedicated keys (keyless canonical profiles keep the ambient key; relaunch prefers the selected profile's key and never carries it to a noncanonical URL), and forward it to teammates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
|||||||
isCanonicalApismartInferenceBaseUrl,
|
isCanonicalApismartInferenceBaseUrl,
|
||||||
isCanonicalConcentrateInferenceBaseUrl,
|
isCanonicalConcentrateInferenceBaseUrl,
|
||||||
isCanonicalLlmtrInferenceBaseUrl,
|
isCanonicalLlmtrInferenceBaseUrl,
|
||||||
|
isCanonicalRequestyInferenceBaseUrl,
|
||||||
isCanonicalMergeGatewayInferenceBaseUrl,
|
isCanonicalMergeGatewayInferenceBaseUrl,
|
||||||
isGmiCloudBaseUrl,
|
isGmiCloudBaseUrl,
|
||||||
isCanonicalCommandcodeInferenceBaseUrl,
|
isCanonicalCommandcodeInferenceBaseUrl,
|
||||||
@@ -176,6 +177,7 @@ const PROFILE_ENV_KEYS = [
|
|||||||
'FIREWORKS_API_KEY',
|
'FIREWORKS_API_KEY',
|
||||||
'LONGCAT_API_KEY',
|
'LONGCAT_API_KEY',
|
||||||
'LLMTR_API_KEY',
|
'LLMTR_API_KEY',
|
||||||
|
'REQUESTY_API_KEY',
|
||||||
'MERGE_GATEWAY_API_KEY',
|
'MERGE_GATEWAY_API_KEY',
|
||||||
'CMD_API_KEY',
|
'CMD_API_KEY',
|
||||||
'COMMANDCODE_API_KEY',
|
'COMMANDCODE_API_KEY',
|
||||||
@@ -285,6 +287,7 @@ export type ProfileEnv = {
|
|||||||
FIREWORKS_API_KEY?: string
|
FIREWORKS_API_KEY?: string
|
||||||
LONGCAT_API_KEY?: string
|
LONGCAT_API_KEY?: string
|
||||||
LLMTR_API_KEY?: string
|
LLMTR_API_KEY?: string
|
||||||
|
REQUESTY_API_KEY?: string
|
||||||
MERGE_GATEWAY_API_KEY?: string
|
MERGE_GATEWAY_API_KEY?: string
|
||||||
CMD_API_KEY?: string
|
CMD_API_KEY?: string
|
||||||
COMMANDCODE_API_KEY?: string
|
COMMANDCODE_API_KEY?: string
|
||||||
@@ -2457,6 +2460,7 @@ export async function buildLaunchEnv(options: {
|
|||||||
'APISMART_API_KEY',
|
'APISMART_API_KEY',
|
||||||
'CONCENTRATE_API_KEY',
|
'CONCENTRATE_API_KEY',
|
||||||
'LLMTR_API_KEY',
|
'LLMTR_API_KEY',
|
||||||
|
'REQUESTY_API_KEY',
|
||||||
'MERGE_GATEWAY_API_KEY',
|
'MERGE_GATEWAY_API_KEY',
|
||||||
'CMD_API_KEY',
|
'CMD_API_KEY',
|
||||||
'COMMANDCODE_API_KEY',
|
'COMMANDCODE_API_KEY',
|
||||||
@@ -2507,6 +2511,9 @@ export async function buildLaunchEnv(options: {
|
|||||||
if (dedicatedKey === 'LLMTR_API_KEY' && effectiveOpenAIRouteId !== 'llmtr') {
|
if (dedicatedKey === 'LLMTR_API_KEY' && effectiveOpenAIRouteId !== 'llmtr') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (dedicatedKey === 'REQUESTY_API_KEY' && effectiveOpenAIRouteId !== 'requesty') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (dedicatedKey === 'MERGE_GATEWAY_API_KEY' && effectiveOpenAIRouteId !== 'merge-gateway') {
|
if (dedicatedKey === 'MERGE_GATEWAY_API_KEY' && effectiveOpenAIRouteId !== 'merge-gateway') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -2547,6 +2554,10 @@ export async function buildLaunchEnv(options: {
|
|||||||
dedicatedKey === 'LLMTR_API_KEY' &&
|
dedicatedKey === 'LLMTR_API_KEY' &&
|
||||||
!!dedicatedBaseUrl &&
|
!!dedicatedBaseUrl &&
|
||||||
!isCanonicalLlmtrInferenceBaseUrl(dedicatedBaseUrl)
|
!isCanonicalLlmtrInferenceBaseUrl(dedicatedBaseUrl)
|
||||||
|
const withholdAmbientRequestyKey =
|
||||||
|
dedicatedKey === 'REQUESTY_API_KEY' &&
|
||||||
|
!!dedicatedBaseUrl &&
|
||||||
|
!isCanonicalRequestyInferenceBaseUrl(dedicatedBaseUrl)
|
||||||
const withholdAmbientMergeGatewayKey =
|
const withholdAmbientMergeGatewayKey =
|
||||||
dedicatedKey === 'MERGE_GATEWAY_API_KEY' &&
|
dedicatedKey === 'MERGE_GATEWAY_API_KEY' &&
|
||||||
!!dedicatedBaseUrl &&
|
!!dedicatedBaseUrl &&
|
||||||
@@ -2560,6 +2571,7 @@ export async function buildLaunchEnv(options: {
|
|||||||
withholdAmbientApismartKey ||
|
withholdAmbientApismartKey ||
|
||||||
withholdAmbientConcentrateKey ||
|
withholdAmbientConcentrateKey ||
|
||||||
withholdAmbientLlmtrKey ||
|
withholdAmbientLlmtrKey ||
|
||||||
|
withholdAmbientRequestyKey ||
|
||||||
withholdAmbientMergeGatewayKey ||
|
withholdAmbientMergeGatewayKey ||
|
||||||
withholdAmbientCommandcodeKey
|
withholdAmbientCommandcodeKey
|
||||||
// Unlike the generic proxy-compatible routes above, Concentrate's
|
// Unlike the generic proxy-compatible routes above, Concentrate's
|
||||||
@@ -2570,6 +2582,7 @@ export async function buildLaunchEnv(options: {
|
|||||||
if (
|
if (
|
||||||
withholdAmbientConcentrateKey ||
|
withholdAmbientConcentrateKey ||
|
||||||
withholdAmbientLlmtrKey ||
|
withholdAmbientLlmtrKey ||
|
||||||
|
withholdAmbientRequestyKey ||
|
||||||
withholdAmbientMergeGatewayKey ||
|
withholdAmbientMergeGatewayKey ||
|
||||||
withholdAmbientCommandcodeKey
|
withholdAmbientCommandcodeKey
|
||||||
) {
|
) {
|
||||||
@@ -2618,6 +2631,18 @@ export async function buildLaunchEnv(options: {
|
|||||||
? sanitizeApiKey(persistedOpenAICredential.value)
|
? sanitizeApiKey(persistedOpenAICredential.value)
|
||||||
: undefined)
|
: undefined)
|
||||||
: undefined
|
: undefined
|
||||||
|
// Same ownership rule for a selected canonical Requesty profile, including
|
||||||
|
// startup files that only persisted the saved key as OPENAI_API_KEY.
|
||||||
|
const persistedRequestyProfileKey =
|
||||||
|
dedicatedKey === 'REQUESTY_API_KEY' &&
|
||||||
|
effectiveOpenAIRouteId === 'requesty' &&
|
||||||
|
!!dedicatedBaseUrl &&
|
||||||
|
isCanonicalRequestyInferenceBaseUrl(dedicatedBaseUrl)
|
||||||
|
? sanitizeApiKey(persistedEnv.REQUESTY_API_KEY) ||
|
||||||
|
(persistedOpenAICredential?.kind === 'usable'
|
||||||
|
? sanitizeApiKey(persistedOpenAICredential.value)
|
||||||
|
: undefined)
|
||||||
|
: undefined
|
||||||
const persistedMergeGatewayProfileKey =
|
const persistedMergeGatewayProfileKey =
|
||||||
dedicatedKey === 'MERGE_GATEWAY_API_KEY' &&
|
dedicatedKey === 'MERGE_GATEWAY_API_KEY' &&
|
||||||
effectiveOpenAIRouteId === 'merge-gateway' &&
|
effectiveOpenAIRouteId === 'merge-gateway' &&
|
||||||
@@ -2690,6 +2715,7 @@ export async function buildLaunchEnv(options: {
|
|||||||
? sanitizeApiKey(persistedEnv[dedicatedKey])
|
? sanitizeApiKey(persistedEnv[dedicatedKey])
|
||||||
: backfillDedicatedFromOpenAI ||
|
: backfillDedicatedFromOpenAI ||
|
||||||
persistedLlmtrProfileKey ||
|
persistedLlmtrProfileKey ||
|
||||||
|
persistedRequestyProfileKey ||
|
||||||
persistedMergeGatewayProfileKey ||
|
persistedMergeGatewayProfileKey ||
|
||||||
persistedCommandcodeProfileKey ||
|
persistedCommandcodeProfileKey ||
|
||||||
persistedOrcarouterProfileKey ||
|
persistedOrcarouterProfileKey ||
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ const RESTORED_KEYS = [
|
|||||||
'API_ROUTE_API_KEY',
|
'API_ROUTE_API_KEY',
|
||||||
'API_ROUTE_MODEL',
|
'API_ROUTE_MODEL',
|
||||||
'LLMTR_API_KEY',
|
'LLMTR_API_KEY',
|
||||||
|
'REQUESTY_API_KEY',
|
||||||
'CMD_API_KEY',
|
'CMD_API_KEY',
|
||||||
'COMMANDCODE_API_KEY',
|
'COMMANDCODE_API_KEY',
|
||||||
'COMMAND_CODE_API_KEY',
|
'COMMAND_CODE_API_KEY',
|
||||||
@@ -353,6 +354,17 @@ function buildLlmtrProfile(overrides: Partial<ProviderProfile> = {}): ProviderPr
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildRequestyProfile(overrides: Partial<ProviderProfile> = {}): ProviderProfile {
|
||||||
|
return buildProfile({
|
||||||
|
provider: 'requesty',
|
||||||
|
name: 'Requesty',
|
||||||
|
baseUrl: 'https://router.requesty.ai/v1',
|
||||||
|
model: 'openai/gpt-5-mini',
|
||||||
|
apiKey: 'requesty-test-key',
|
||||||
|
...overrides,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function buildCommandcodeProfile(overrides: Partial<ProviderProfile> = {}): ProviderProfile {
|
function buildCommandcodeProfile(overrides: Partial<ProviderProfile> = {}): ProviderProfile {
|
||||||
return buildProfile({
|
return buildProfile({
|
||||||
provider: 'commandcode',
|
provider: 'commandcode',
|
||||||
@@ -2596,6 +2608,54 @@ describe('applyProviderProfileToProcessEnv', () => {
|
|||||||
expect(process.env.LLMTR_API_KEY).toBeUndefined()
|
expect(process.env.LLMTR_API_KEY).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('retargeted Requesty profile withholds its saved credential', async () => {
|
||||||
|
const { applyProviderProfileToProcessEnv } =
|
||||||
|
await importFreshProviderProfileModules()
|
||||||
|
|
||||||
|
applyProviderProfileToProcessEnv(
|
||||||
|
buildRequestyProfile({ baseUrl: 'https://example.test/v1' }),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(process.env.OPENAI_BASE_URL).toBe('https://example.test/v1')
|
||||||
|
expect(process.env.OPENAI_API_KEY).toBeUndefined()
|
||||||
|
expect(process.env.REQUESTY_API_KEY).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Requesty profile clears an ambient dedicated key so its saved key wins', async () => {
|
||||||
|
const { applyProviderProfileToProcessEnv } =
|
||||||
|
await importFreshProviderProfileModules()
|
||||||
|
process.env.REQUESTY_API_KEY = 'ambient-old'
|
||||||
|
|
||||||
|
applyProviderProfileToProcessEnv(
|
||||||
|
buildRequestyProfile({ apiKey: 'selected-new' }),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(process.env.OPENAI_API_KEY).toBe('selected-new')
|
||||||
|
expect(
|
||||||
|
resolveRouteCredentialValue({
|
||||||
|
routeId: 'requesty',
|
||||||
|
baseUrl: process.env.OPENAI_BASE_URL,
|
||||||
|
processEnv: process.env,
|
||||||
|
}),
|
||||||
|
).toBe('selected-new')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('keyless canonical Requesty profile keeps its ambient dedicated key', async () => {
|
||||||
|
const { applyProviderProfileToProcessEnv } =
|
||||||
|
await importFreshProviderProfileModules()
|
||||||
|
process.env.REQUESTY_API_KEY = 'ambient-requesty-key'
|
||||||
|
|
||||||
|
applyProviderProfileToProcessEnv(buildRequestyProfile({ apiKey: undefined }))
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolveRouteCredentialValue({
|
||||||
|
routeId: 'requesty',
|
||||||
|
baseUrl: process.env.OPENAI_BASE_URL,
|
||||||
|
processEnv: process.env,
|
||||||
|
}),
|
||||||
|
).toBe('ambient-requesty-key')
|
||||||
|
})
|
||||||
|
|
||||||
test('query-bearing LLMTR profiles retain generic proxy capabilities', async () => {
|
test('query-bearing LLMTR profiles retain generic proxy capabilities', async () => {
|
||||||
const { addProviderProfile, applyProviderProfileToProcessEnv } =
|
const { addProviderProfile, applyProviderProfileToProcessEnv } =
|
||||||
await importFreshProviderProfileModules()
|
await importFreshProviderProfileModules()
|
||||||
@@ -5228,6 +5288,83 @@ describe('setActiveProviderProfile', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Requesty profiles keep saved-key ownership across restart and withhold it when retargeted', async () => {
|
||||||
|
const tempDir = mkdtempSync(join(tmpdir(), 'openclaude-provider-'))
|
||||||
|
const configDir = mkdtempSync(join(tmpdir(), 'openclaude-provider-config-'))
|
||||||
|
process.chdir(tempDir)
|
||||||
|
process.env.CLAUDE_CONFIG_DIR = configDir
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { setActiveProviderProfile } =
|
||||||
|
await importFreshProviderProfileModules()
|
||||||
|
const canonical = buildRequestyProfile({ id: 'requesty_profile' })
|
||||||
|
const keyless = buildRequestyProfile({
|
||||||
|
id: 'requesty_keyless',
|
||||||
|
apiKey: undefined,
|
||||||
|
})
|
||||||
|
const retargeted = buildRequestyProfile({
|
||||||
|
id: 'requesty_proxy',
|
||||||
|
baseUrl: 'https://example.test/v1',
|
||||||
|
})
|
||||||
|
saveMockGlobalConfig(current => ({
|
||||||
|
...current,
|
||||||
|
providerProfiles: [canonical, keyless, retargeted],
|
||||||
|
}))
|
||||||
|
const readPersisted = () =>
|
||||||
|
JSON.parse(
|
||||||
|
readFileSync(join(configDir, '.openclaude-profile.json'), 'utf8'),
|
||||||
|
)
|
||||||
|
const { buildStartupEnvFromProfile } = await import(
|
||||||
|
`./providerProfile.js?ts=${Date.now()}-${Math.random()}`
|
||||||
|
)
|
||||||
|
const ambient = { REQUESTY_API_KEY: 'ambient-unrelated-key' }
|
||||||
|
|
||||||
|
setActiveProviderProfile('requesty_profile', { configDir })
|
||||||
|
const persisted = readPersisted()
|
||||||
|
expect(persisted.env).toMatchObject({
|
||||||
|
OPENAI_BASE_URL: 'https://router.requesty.ai/v1',
|
||||||
|
OPENAI_API_KEY: 'requesty-test-key',
|
||||||
|
REQUESTY_API_KEY: 'requesty-test-key',
|
||||||
|
})
|
||||||
|
const startupEnv = await buildStartupEnvFromProfile({
|
||||||
|
persisted,
|
||||||
|
processEnv: ambient,
|
||||||
|
})
|
||||||
|
expect(startupEnv.REQUESTY_API_KEY).toBe('requesty-test-key')
|
||||||
|
delete persisted.env.REQUESTY_API_KEY
|
||||||
|
const migratedStartupEnv = await buildStartupEnvFromProfile({
|
||||||
|
persisted,
|
||||||
|
processEnv: ambient,
|
||||||
|
})
|
||||||
|
expect(migratedStartupEnv.REQUESTY_API_KEY).toBe('requesty-test-key')
|
||||||
|
|
||||||
|
setActiveProviderProfile('requesty_keyless', { configDir })
|
||||||
|
const keylessStartupEnv = await buildStartupEnvFromProfile({
|
||||||
|
persisted: readPersisted(),
|
||||||
|
processEnv: ambient,
|
||||||
|
})
|
||||||
|
expect(keylessStartupEnv.REQUESTY_API_KEY).toBe('ambient-unrelated-key')
|
||||||
|
|
||||||
|
setActiveProviderProfile('requesty_proxy', { configDir })
|
||||||
|
expect(process.env.OPENAI_API_KEY).toBeUndefined()
|
||||||
|
expect(process.env.REQUESTY_API_KEY).toBeUndefined()
|
||||||
|
const retargetedPersisted = readPersisted()
|
||||||
|
expect(retargetedPersisted.env.OPENAI_API_KEY).toBeUndefined()
|
||||||
|
expect(retargetedPersisted.env.REQUESTY_API_KEY).toBeUndefined()
|
||||||
|
const retargetedStartupEnv = await buildStartupEnvFromProfile({
|
||||||
|
persisted: retargetedPersisted,
|
||||||
|
processEnv: ambient,
|
||||||
|
})
|
||||||
|
expect(retargetedStartupEnv.OPENAI_BASE_URL).toBe('https://example.test/v1')
|
||||||
|
expect(retargetedStartupEnv.OPENAI_API_KEY).toBeUndefined()
|
||||||
|
expect(retargetedStartupEnv.REQUESTY_API_KEY).toBeUndefined()
|
||||||
|
} finally {
|
||||||
|
process.chdir(originalCwd)
|
||||||
|
rmSync(tempDir, { recursive: true, force: true })
|
||||||
|
rmSync(configDir, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test('keyed canonical LLMTR profiles persist their saved dedicated credential across restart', async () => {
|
test('keyed canonical LLMTR profiles persist their saved dedicated credential across restart', async () => {
|
||||||
const tempDir = mkdtempSync(join(tmpdir(), 'openclaude-provider-'))
|
const tempDir = mkdtempSync(join(tmpdir(), 'openclaude-provider-'))
|
||||||
const configDir = mkdtempSync(join(tmpdir(), 'openclaude-provider-config-'))
|
const configDir = mkdtempSync(join(tmpdir(), 'openclaude-provider-config-'))
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import {
|
|||||||
isCanonicalLlmtrInferenceBaseUrl,
|
isCanonicalLlmtrInferenceBaseUrl,
|
||||||
isCanonicalMergeGatewayInferenceBaseUrl,
|
isCanonicalMergeGatewayInferenceBaseUrl,
|
||||||
isCanonicalCommandcodeInferenceBaseUrl,
|
isCanonicalCommandcodeInferenceBaseUrl,
|
||||||
|
isCanonicalRequestyInferenceBaseUrl,
|
||||||
isFireworksBaseUrl,
|
isFireworksBaseUrl,
|
||||||
isGmiCloudBaseUrl,
|
isGmiCloudBaseUrl,
|
||||||
isLongcatBaseUrl,
|
isLongcatBaseUrl,
|
||||||
@@ -248,6 +249,17 @@ function isLlmtrProfile(profile: ProviderProfile): boolean {
|
|||||||
return !baseUrl || isCanonicalLlmtrInferenceBaseUrl(baseUrl)
|
return !baseUrl || isCanonicalLlmtrInferenceBaseUrl(baseUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRequestyProfile(profile: ProviderProfile): boolean {
|
||||||
|
const { route } = resolveProfileCompatibility(profile.provider)
|
||||||
|
if (route.routeId !== 'requesty') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const baseUrl = profile.baseUrl?.trim()
|
||||||
|
// A missing base URL resolves to the Requesty default, which is canonical.
|
||||||
|
// Any other URL is a retargeted endpoint and must not receive the saved key.
|
||||||
|
return !baseUrl || isCanonicalRequestyInferenceBaseUrl(baseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
function isMergeGatewayProfile(profile: ProviderProfile): boolean {
|
function isMergeGatewayProfile(profile: ProviderProfile): boolean {
|
||||||
const { route, compatibilityMode } = resolveProfileCompatibility(profile.provider)
|
const { route, compatibilityMode } = resolveProfileCompatibility(profile.provider)
|
||||||
if (compatibilityMode !== 'openai') return false
|
if (compatibilityMode !== 'openai') return false
|
||||||
@@ -1369,6 +1381,8 @@ export function applyProviderProfileToProcessEnv(
|
|||||||
route.routeId === 'api-route' && !isApiRouteProfile(profile)
|
route.routeId === 'api-route' && !isApiRouteProfile(profile)
|
||||||
const withholdRetargetedLlmtrCredential =
|
const withholdRetargetedLlmtrCredential =
|
||||||
route.routeId === 'llmtr' && !isLlmtrProfile(profile)
|
route.routeId === 'llmtr' && !isLlmtrProfile(profile)
|
||||||
|
const withholdRetargetedRequestyCredential =
|
||||||
|
route.routeId === 'requesty' && !isRequestyProfile(profile)
|
||||||
const withholdRetargetedMergeGatewayCredential =
|
const withholdRetargetedMergeGatewayCredential =
|
||||||
route.routeId === 'merge-gateway' && !isMergeGatewayProfile(profile)
|
route.routeId === 'merge-gateway' && !isMergeGatewayProfile(profile)
|
||||||
const withholdRetargetedCommandcodeCredential =
|
const withholdRetargetedCommandcodeCredential =
|
||||||
@@ -1382,6 +1396,7 @@ export function applyProviderProfileToProcessEnv(
|
|||||||
!withholdRetargetedConcentrateCredential &&
|
!withholdRetargetedConcentrateCredential &&
|
||||||
!withholdRetargetedApiRouteCredential &&
|
!withholdRetargetedApiRouteCredential &&
|
||||||
!withholdRetargetedLlmtrCredential &&
|
!withholdRetargetedLlmtrCredential &&
|
||||||
|
!withholdRetargetedRequestyCredential &&
|
||||||
!withholdRetargetedMergeGatewayCredential &&
|
!withholdRetargetedMergeGatewayCredential &&
|
||||||
!withholdRetargetedCommandcodeCredential &&
|
!withholdRetargetedCommandcodeCredential &&
|
||||||
!withholdRetargetedGmiCloudCredential
|
!withholdRetargetedGmiCloudCredential
|
||||||
@@ -1571,6 +1586,15 @@ 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
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isCommandcodeProfile(profile)) {
|
if (isCommandcodeProfile(profile)) {
|
||||||
openAIProfileEnv.CLAUDE_CODE_PROVIDER_ROUTE_ID = 'commandcode'
|
openAIProfileEnv.CLAUDE_CODE_PROVIDER_ROUTE_ID = 'commandcode'
|
||||||
if (!profile.apiKey) {
|
if (!profile.apiKey) {
|
||||||
@@ -1908,6 +1932,8 @@ function buildOpenAICompatibleStartupEnv(
|
|||||||
activeProfileRouteId === 'api-route' && !isApiRouteProfile(activeProfile)
|
activeProfileRouteId === 'api-route' && !isApiRouteProfile(activeProfile)
|
||||||
const withholdRetargetedLlmtrCredential =
|
const withholdRetargetedLlmtrCredential =
|
||||||
activeProfileRouteId === 'llmtr' && !isLlmtrProfile(activeProfile)
|
activeProfileRouteId === 'llmtr' && !isLlmtrProfile(activeProfile)
|
||||||
|
const withholdRetargetedRequestyCredential =
|
||||||
|
activeProfileRouteId === 'requesty' && !isRequestyProfile(activeProfile)
|
||||||
const withholdRetargetedMergeGatewayCredential =
|
const withholdRetargetedMergeGatewayCredential =
|
||||||
activeProfileRouteId === 'merge-gateway' && !isMergeGatewayProfile(activeProfile)
|
activeProfileRouteId === 'merge-gateway' && !isMergeGatewayProfile(activeProfile)
|
||||||
const withholdRetargetedCommandcodeCredential =
|
const withholdRetargetedCommandcodeCredential =
|
||||||
@@ -1919,6 +1945,7 @@ function buildOpenAICompatibleStartupEnv(
|
|||||||
const isConcentrateProfileFlag = isConcentrateProfile(activeProfile)
|
const isConcentrateProfileFlag = isConcentrateProfile(activeProfile)
|
||||||
const isApiRouteProfileFlag = isApiRouteProfile(activeProfile)
|
const isApiRouteProfileFlag = isApiRouteProfile(activeProfile)
|
||||||
const isLlmtrProfileFlag = isLlmtrProfile(activeProfile)
|
const isLlmtrProfileFlag = isLlmtrProfile(activeProfile)
|
||||||
|
const isRequestyProfileFlag = isRequestyProfile(activeProfile)
|
||||||
const isMergeGatewayProfileFlag = isMergeGatewayProfile(activeProfile)
|
const isMergeGatewayProfileFlag = isMergeGatewayProfile(activeProfile)
|
||||||
const isCommandcodeProfileFlag = isCommandcodeProfile(activeProfile)
|
const isCommandcodeProfileFlag = isCommandcodeProfile(activeProfile)
|
||||||
|
|
||||||
@@ -1928,6 +1955,7 @@ function buildOpenAICompatibleStartupEnv(
|
|||||||
!withholdRetargetedConcentrateCredential &&
|
!withholdRetargetedConcentrateCredential &&
|
||||||
!withholdRetargetedApiRouteCredential &&
|
!withholdRetargetedApiRouteCredential &&
|
||||||
!withholdRetargetedLlmtrCredential &&
|
!withholdRetargetedLlmtrCredential &&
|
||||||
|
!withholdRetargetedRequestyCredential &&
|
||||||
!withholdRetargetedMergeGatewayCredential &&
|
!withholdRetargetedMergeGatewayCredential &&
|
||||||
!withholdRetargetedCommandcodeCredential
|
!withholdRetargetedCommandcodeCredential
|
||||||
) {
|
) {
|
||||||
@@ -1993,6 +2021,9 @@ function buildOpenAICompatibleStartupEnv(
|
|||||||
if (isLlmtrProfileFlag) {
|
if (isLlmtrProfileFlag) {
|
||||||
strictEnv.LLMTR_API_KEY = activeProfile.apiKey
|
strictEnv.LLMTR_API_KEY = activeProfile.apiKey
|
||||||
}
|
}
|
||||||
|
if (isRequestyProfileFlag) {
|
||||||
|
strictEnv.REQUESTY_API_KEY = activeProfile.apiKey
|
||||||
|
}
|
||||||
if (isMergeGatewayProfileFlag) {
|
if (isMergeGatewayProfileFlag) {
|
||||||
strictEnv.MERGE_GATEWAY_API_KEY = activeProfile.apiKey
|
strictEnv.MERGE_GATEWAY_API_KEY = activeProfile.apiKey
|
||||||
}
|
}
|
||||||
@@ -2078,6 +2109,7 @@ function buildOpenAICompatibleStartupEnv(
|
|||||||
!withholdRetargetedConcentrateCredential &&
|
!withholdRetargetedConcentrateCredential &&
|
||||||
!withholdRetargetedApiRouteCredential &&
|
!withholdRetargetedApiRouteCredential &&
|
||||||
!withholdRetargetedLlmtrCredential &&
|
!withholdRetargetedLlmtrCredential &&
|
||||||
|
!withholdRetargetedRequestyCredential &&
|
||||||
!withholdRetargetedMergeGatewayCredential &&
|
!withholdRetargetedMergeGatewayCredential &&
|
||||||
!withholdRetargetedCommandcodeCredential
|
!withholdRetargetedCommandcodeCredential
|
||||||
) {
|
) {
|
||||||
@@ -2113,6 +2145,9 @@ function buildOpenAICompatibleStartupEnv(
|
|||||||
if (isLlmtrProfileFlag) {
|
if (isLlmtrProfileFlag) {
|
||||||
env.LLMTR_API_KEY = activeProfile.apiKey
|
env.LLMTR_API_KEY = activeProfile.apiKey
|
||||||
}
|
}
|
||||||
|
if (isRequestyProfileFlag) {
|
||||||
|
env.REQUESTY_API_KEY = activeProfile.apiKey
|
||||||
|
}
|
||||||
if (isMergeGatewayProfileFlag) {
|
if (isMergeGatewayProfileFlag) {
|
||||||
env.MERGE_GATEWAY_API_KEY = activeProfile.apiKey
|
env.MERGE_GATEWAY_API_KEY = activeProfile.apiKey
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,16 @@ test('buildInheritedEnvVars forwards an LLMTR credential without inventing route
|
|||||||
expect(envVars).not.toContain('OPENAI_BASE_URL=')
|
expect(envVars).not.toContain('OPENAI_BASE_URL=')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('buildInheritedEnvVars forwards a Requesty credential without inventing route state', () => {
|
||||||
|
process.env.REQUESTY_API_KEY = 'requesty-key'
|
||||||
|
|
||||||
|
const envVars = buildInheritedEnvVars()
|
||||||
|
|
||||||
|
expect(envVars).toContain('REQUESTY_API_KEY=requesty-key')
|
||||||
|
expect(envVars).not.toContain('CLAUDE_CODE_USE_OPENAI=1')
|
||||||
|
expect(envVars).not.toContain('OPENAI_BASE_URL=')
|
||||||
|
})
|
||||||
|
|
||||||
test('buildInheritedEnvVars forwards a GMI Cloud credential without inventing route state', () => {
|
test('buildInheritedEnvVars forwards a GMI Cloud credential without inventing route state', () => {
|
||||||
process.env.GMI_API_KEY = 'gmi-key'
|
process.env.GMI_API_KEY = 'gmi-key'
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ const TEAMMATE_ENV_VARS = [
|
|||||||
'VENICE_API_KEY',
|
'VENICE_API_KEY',
|
||||||
'MIMO_API_KEY',
|
'MIMO_API_KEY',
|
||||||
'LLMTR_API_KEY',
|
'LLMTR_API_KEY',
|
||||||
|
'REQUESTY_API_KEY',
|
||||||
'MERGE_GATEWAY_API_KEY',
|
'MERGE_GATEWAY_API_KEY',
|
||||||
'CMD_API_KEY',
|
'CMD_API_KEY',
|
||||||
'COMMANDCODE_API_KEY',
|
'COMMANDCODE_API_KEY',
|
||||||
|
|||||||
Reference in New Issue
Block a user