chenbhao commited on
Commit
8083f17
·
1 Parent(s): 6f40914

fix: /login provider change

Browse files
src/components/OpenAILoginFlow.tsx CHANGED
@@ -115,7 +115,11 @@ export function OpenAILoginFlow({
115
  }
116
  }
117
 
118
- async function handleSubmit(value: string): Promise<void> {
 
 
 
 
119
  const trimmed = value.trim()
120
  if (!trimmed) {
121
  return
@@ -180,6 +184,7 @@ export function OpenAILoginFlow({
180
  onChangeCursorOffset={setCursorOffset}
181
  columns={72}
182
  mask="*"
 
183
  />
184
  </Box>
185
  {status ? <Text color="error">{status}</Text> : null}
 
115
  }
116
  }
117
 
118
+ async function handleSubmit(value?: string): Promise<void> {
119
+ if (!value) {
120
+ return
121
+ }
122
+
123
  const trimmed = value.trim()
124
  if (!trimmed) {
125
  return
 
184
  onChangeCursorOffset={setCursorOffset}
185
  columns={72}
186
  mask="*"
187
+ focus={true}
188
  />
189
  </Box>
190
  {status ? <Text color="error">{status}</Text> : null}
src/components/OpenRouterLoginFlow.tsx CHANGED
@@ -19,7 +19,11 @@ export function OpenRouterLoginFlow({
19
  const [inputValue, setInputValue] = useState('')
20
  const [cursorOffset, setCursorOffset] = useState(0)
21
 
22
- async function handleSubmit(value: string): Promise<void> {
 
 
 
 
23
  const trimmed = value.trim()
24
  if (!trimmed) {
25
  return
@@ -76,6 +80,7 @@ export function OpenRouterLoginFlow({
76
  onChangeCursorOffset={setCursorOffset}
77
  columns={72}
78
  mask="*"
 
79
  />
80
  </Box>
81
  {status ? <Text color="error">{status}</Text> : null}
 
19
  const [inputValue, setInputValue] = useState('')
20
  const [cursorOffset, setCursorOffset] = useState(0)
21
 
22
+ async function handleSubmit(value?: string): Promise<void> {
23
+ if (!value) {
24
+ return
25
+ }
26
+
27
  const trimmed = value.trim()
28
  if (!trimmed) {
29
  return
 
80
  onChangeCursorOffset={setCursorOffset}
81
  columns={72}
82
  mask="*"
83
+ focus={true}
84
  />
85
  </Box>
86
  {status ? <Text color="error">{status}</Text> : null}
src/services/api/errors.ts CHANGED
@@ -905,9 +905,10 @@ export function getAssistantMessageFromError(
905
  if (error instanceof APIError && error.status === 404) {
906
  const switchCmd = getIsNonInteractiveSession() ? '--model' : '/model'
907
  const fallbackSuggestion = get3PModelFallbackSuggestion(model)
 
908
  return createAssistantAPIErrorMessage({
909
  content: fallbackSuggestion
910
- ? `The model ${model} is not available on your ${getAPIProvider()} deployment. Try ${switchCmd} to switch to ${fallbackSuggestion}, or ask your admin to enable this model.`
911
  : `There's an issue with the selected model (${model}). It may not exist or you may not have access to it. Run ${switchCmd} to pick a different model.`,
912
  error: 'invalid_request',
913
  })
 
905
  if (error instanceof APIError && error.status === 404) {
906
  const switchCmd = getIsNonInteractiveSession() ? '--model' : '/model'
907
  const fallbackSuggestion = get3PModelFallbackSuggestion(model)
908
+ const provider = getAPIProvider()
909
  return createAssistantAPIErrorMessage({
910
  content: fallbackSuggestion
911
+ ? `The model ${model} is not available on your ${provider ?? 'configured'} deployment. Try ${switchCmd} to switch to ${fallbackSuggestion}, or ask your admin to enable this model.`
912
  : `There's an issue with the selected model (${model}). It may not exist or you may not have access to it. Run ${switchCmd} to pick a different model.`,
913
  error: 'invalid_request',
914
  })
src/utils/auth.ts CHANGED
@@ -388,11 +388,18 @@ export async function saveOpenAIApiKey(apiKey: string): Promise<void> {
388
  }
389
 
390
  export async function saveOpenRouterApiKey(apiKey: string): Promise<void> {
 
 
 
 
 
 
391
  if (!isValidApiKey(apiKey)) {
392
  throw new Error(
393
  'Invalid API key format. API key must contain only alphanumeric characters, dashes, and underscores.',
394
  )
395
  }
 
396
  saveGlobalConfig(current => ({
397
  ...current,
398
  authProvider: 'openrouter',
@@ -402,12 +409,16 @@ export async function saveOpenRouterApiKey(apiKey: string): Promise<void> {
402
  const { clearStoredProviderCache } = await import('./model/providers.js')
403
  clearStoredProviderCache()
404
  // Clear OpenRouter models cache so it will be refetched with new API key
405
- const { clearOpenRouterModelsCache } = await import('./model/openRouterModels.js')
406
- clearOpenRouterModelsCache()
 
 
 
 
 
407
  }
408
 
409
  export async function saveLocalModelConfig(baseUrl: string, modelName: string): Promise<void> {
410
- console.log('[saveLocalModelConfig] Saving local model config:', baseUrl, modelName)
411
  if (!baseUrl.trim()) {
412
  throw new Error('Base URL cannot be empty')
413
  }
@@ -426,14 +437,20 @@ export async function saveLocalModelConfig(baseUrl: string, modelName: string):
426
  localBaseUrl: baseUrl,
427
  localModelName: modelName,
428
  }
429
- console.log('[saveLocalModelConfig] New config to save:', newConfig)
430
  return newConfig
431
  })
432
 
433
  // Clear provider cache so it will be re-read on next access
434
  const { clearStoredProviderCache } = await import('./model/providers.js')
435
  clearStoredProviderCache()
436
- console.log('[saveLocalModelConfig] Config saved successfully')
 
 
 
 
 
 
 
437
  }
438
 
439
  export function getLocalModelName(): string | null {
 
388
  }
389
 
390
  export async function saveOpenRouterApiKey(apiKey: string): Promise<void> {
391
+ if (!apiKey || typeof apiKey !== 'string') {
392
+ throw new Error(
393
+ 'Invalid API key: API key must be a non-empty string.',
394
+ )
395
+ }
396
+
397
  if (!isValidApiKey(apiKey)) {
398
  throw new Error(
399
  'Invalid API key format. API key must contain only alphanumeric characters, dashes, and underscores.',
400
  )
401
  }
402
+
403
  saveGlobalConfig(current => ({
404
  ...current,
405
  authProvider: 'openrouter',
 
409
  const { clearStoredProviderCache } = await import('./model/providers.js')
410
  clearStoredProviderCache()
411
  // Clear OpenRouter models cache so it will be refetched with new API key
412
+ try {
413
+ const { clearOpenRouterModelsCache } = await import('./model/openRouterModels.js')
414
+ clearOpenRouterModelsCache()
415
+ } catch (error) {
416
+ // Ignore errors from clearing cache
417
+ console.error('Error clearing OpenRouter models cache:', error)
418
+ }
419
  }
420
 
421
  export async function saveLocalModelConfig(baseUrl: string, modelName: string): Promise<void> {
 
422
  if (!baseUrl.trim()) {
423
  throw new Error('Base URL cannot be empty')
424
  }
 
437
  localBaseUrl: baseUrl,
438
  localModelName: modelName,
439
  }
 
440
  return newConfig
441
  })
442
 
443
  // Clear provider cache so it will be re-read on next access
444
  const { clearStoredProviderCache } = await import('./model/providers.js')
445
  clearStoredProviderCache()
446
+
447
+ // Clear OpenRouter models cache so it will be refetched with new API key
448
+ try {
449
+ const { clearOpenRouterModelsCache } = await import('./model/openRouterModels.js')
450
+ clearOpenRouterModelsCache()
451
+ } catch (error) {
452
+ // Ignore errors from clearing cache
453
+ }
454
  }
455
 
456
  export function getLocalModelName(): string | null {
src/utils/model/model.ts CHANGED
@@ -179,7 +179,29 @@ export function getRuntimeMainLoopModel(params: {
179
  */
180
  export function getDefaultMainLoopModelSetting(): ModelName | ModelAlias {
181
  // Check if using Local provider
182
- if (getAPIProvider() === 'local') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  const localModelName = getLocalModelName()
184
  if (localModelName) {
185
  return localModelName
 
179
  */
180
  export function getDefaultMainLoopModelSetting(): ModelName | ModelAlias {
181
  // Check if using Local provider
182
+ // Need to check both getAPIProvider() and direct file read because
183
+ // getAPIProvider() might return null if cache was cleared
184
+ const apiProvider = getAPIProvider()
185
+ let isLocalProvider = apiProvider === 'local'
186
+
187
+ // If getAPIProvider() returns null, check the config file directly
188
+ if (!isLocalProvider && apiProvider === null) {
189
+ try {
190
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
191
+ const { readFileSync } = require('fs') as typeof import('fs')
192
+ const { getGlobalClaudeFile } = require('./env.js') as typeof import('./env.js')
193
+ const raw = readFileSync(getGlobalClaudeFile(), 'utf8')
194
+ const config = JSON.parse(raw) as {
195
+ authProvider?: string
196
+ localBaseUrl?: string
197
+ }
198
+ isLocalProvider = config.authProvider === 'local' && !!config.localBaseUrl
199
+ } catch {
200
+ // Ignore errors, fall through to default behavior
201
+ }
202
+ }
203
+
204
+ if (isLocalProvider) {
205
  const localModelName = getLocalModelName()
206
  if (localModelName) {
207
  return localModelName
src/utils/model/modelOptions.ts CHANGED
@@ -67,7 +67,40 @@ export function getDefaultOptionForUser(fastMode = false): ModelOption {
67
  // PAYG
68
  const provider = getAPIProvider()
69
  const isOpenAI = provider === 'openai'
 
70
  const is3P = provider !== 'firstParty'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  return {
72
  value: null,
73
  label: 'Default (recommended)',
@@ -333,6 +366,13 @@ function getOpusPlanOption(): ModelOption {
333
  // @[MODEL LAUNCH]: Update the model picker lists below to include/reorder options for the new model.
334
  // Each user tier (ant, Max/Team Premium, Pro/Team Standard/Enterprise, PAYG 1P, PAYG 3P) has its own list.
335
  function getModelOptionsBase(fastMode = false): ModelOption[] {
 
 
 
 
 
 
 
336
  if (process.env.USER_TYPE === 'ant') {
337
  // Build options from antModels config
338
  const antModelOptions: ModelOption[] = getAntModels().map(m => ({
 
67
  // PAYG
68
  const provider = getAPIProvider()
69
  const isOpenAI = provider === 'openai'
70
+ const isLocal = provider === 'local'
71
  const is3P = provider !== 'firstParty'
72
+
73
+ // Check if we should use local model
74
+ let localModelName: string | null = null
75
+ if (isLocal || provider === null) {
76
+ // Try to get local model name from file
77
+ try {
78
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
79
+ const { readFileSync } = require('fs') as typeof import('fs')
80
+ const { getGlobalClaudeFile } = require('../env.js') as typeof import('../env.js')
81
+ const raw = readFileSync(getGlobalClaudeFile(), 'utf8')
82
+ const config = JSON.parse(raw) as {
83
+ authProvider?: string
84
+ localBaseUrl?: string
85
+ localModelName?: string
86
+ }
87
+ if (config.authProvider === 'local' && config.localBaseUrl && config.localModelName) {
88
+ localModelName = config.localModelName
89
+ }
90
+ } catch {
91
+ // Ignore errors
92
+ }
93
+ }
94
+
95
+ if (localModelName) {
96
+ return {
97
+ value: null,
98
+ label: 'Default (recommended)',
99
+ description: `Use the default local model (currently ${localModelName})`,
100
+ descriptionForModel: `Default local model (currently ${localModelName})`,
101
+ }
102
+ }
103
+
104
  return {
105
  value: null,
106
  label: 'Default (recommended)',
 
366
  // @[MODEL LAUNCH]: Update the model picker lists below to include/reorder options for the new model.
367
  // Each user tier (ant, Max/Team Premium, Pro/Team Standard/Enterprise, PAYG 1P, PAYG 3P) has its own list.
368
  function getModelOptionsBase(fastMode = false): ModelOption[] {
369
+ const provider = getAPIProvider()
370
+
371
+ // If no provider is configured, only return Default option
372
+ if (!provider) {
373
+ return [getDefaultOptionForUser(fastMode)]
374
+ }
375
+
376
  if (process.env.USER_TYPE === 'ant') {
377
  // Build options from antModels config
378
  const antModelOptions: ModelOption[] = getAntModels().map(m => ({
src/utils/model/providers.ts CHANGED
@@ -164,7 +164,7 @@ export function getOpenAIBaseUrl(): string {
164
  return process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1'
165
  }
166
 
167
- export function getAPIProvider(): APIProvider {
168
  const explicitProvider = getExplicitProviderOverride()
169
  if (explicitProvider) {
170
  return explicitProvider
@@ -180,7 +180,7 @@ export function getAPIProvider(): APIProvider {
180
  ? 'openai'
181
  : isOpenRouterConfigured()
182
  ? 'openrouter'
183
- : getStoredProviderPreference() ?? 'firstParty'
184
  }
185
 
186
  export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {
 
164
  return process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1'
165
  }
166
 
167
+ export function getAPIProvider(): APIProvider | null {
168
  const explicitProvider = getExplicitProviderOverride()
169
  if (explicitProvider) {
170
  return explicitProvider
 
180
  ? 'openai'
181
  : isOpenRouterConfigured()
182
  ? 'openrouter'
183
+ : getStoredProviderPreference()
184
  }
185
 
186
  export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {
src/utils/swarm/teammateModel.ts CHANGED
@@ -6,5 +6,7 @@ import { getAPIProvider } from '../model/providers.js'
6
  // use Opus 4.6. Must be provider-aware so Bedrock/Vertex/Foundry customers get
7
  // the correct model ID.
8
  export function getHardcodedTeammateModelFallback(): string {
9
- return CLAUDE_OPUS_4_6_CONFIG[getAPIProvider()]
 
 
10
  }
 
6
  // use Opus 4.6. Must be provider-aware so Bedrock/Vertex/Foundry customers get
7
  // the correct model ID.
8
  export function getHardcodedTeammateModelFallback(): string {
9
+ const provider = getAPIProvider()
10
+ // Default to firstParty if no provider is configured
11
+ return CLAUDE_OPUS_4_6_CONFIG[provider ?? 'firstParty']
12
  }