feat: add xAI as official provider (#865)

* feat: add xAI as official provider

- Add xAI preset to ProviderManager (alphabetical order)
- Add xAI provider detection via XAI_API_KEY
- Add xAI startup screen heuristic (x.ai base URL or grok model)
- Add xAI status display properties
- Add grok-4 and grok-3 context windows
- Add xAI model fallbacks across all tiers
- Fix JSDoc priority order in providerAutoDetect

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>

* fix(xai): persist relaunch classification for xAI profiles

Addresses reviewer feedback on feat/xai-official-provider:
- isProcessEnvAlignedWithProfile now validates XAI_API_KEY for x.ai
  base URLs, mirroring the Bankr pattern. Without this, relaunch
  skips re-applying the profile, XAI_API_KEY stays unset, and
  getAPIProvider() falls back to 'openai'.
- buildOpenAICompatibleStartupEnv now sets XAI_API_KEY when syncing
  active xAI profile to the legacy fallback file.
- Adds 'xai' to VALID_PROVIDERS and --provider xai CLI flag support.
- Adds xAI detection to providerDiscovery label heuristics.
- Adds 'xai' to legacy ProviderProfile type/isProviderProfile guard.
- Adds targeted tests for relaunch alignment, flag application, and
  discovery labeling.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@openclaude.dev>
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
This commit is contained in:
Kevin Codex
2026-04-26 21:26:44 +08:00
committed by GitHub
parent d45628c413
commit 2586a9cddb
18 changed files with 248 additions and 7 deletions

View File

@@ -23,7 +23,6 @@ import {
sanitizeApiKey,
sanitizeProviderConfigValue,
} from './providerSecrets.js'
import { getPrimaryModel } from './providerModels.js'
export {
maskSecretForDisplay,
@@ -79,6 +78,7 @@ const PROFILE_ENV_KEYS = [
'BANKR_BASE_URL',
'BNKR_API_KEY',
'BANKR_MODEL',
'XAI_API_KEY',
] as const
const SECRET_ENV_KEYS = [
@@ -91,9 +91,10 @@ const SECRET_ENV_KEYS = [
'MINIMAX_API_KEY',
'MISTRAL_API_KEY',
'BNKR_API_KEY',
'XAI_API_KEY',
] as const
export type ProviderProfile = 'openai' | 'ollama' | 'codex' | 'gemini' | 'atomic-chat' | 'nvidia-nim' | 'minimax' | 'mistral'
export type ProviderProfile = 'openai' | 'ollama' | 'codex' | 'gemini' | 'atomic-chat' | 'nvidia-nim' | 'minimax' | 'mistral' | 'xai'
export type ProfileEnv = {
OPENAI_BASE_URL?: string
@@ -123,6 +124,7 @@ export type ProfileEnv = {
BANKR_BASE_URL?: string
BNKR_API_KEY?: string
BANKR_MODEL?: string
XAI_API_KEY?: string
}
export type ProfileFile = {
@@ -180,7 +182,8 @@ export function isProviderProfile(value: unknown): value is ProviderProfile {
value === 'atomic-chat' ||
value === 'nvidia-nim' ||
value === 'minimax' ||
value === 'mistral'
value === 'mistral' ||
value === 'xai'
)
}