M1/F1: Kimi Code OAuth device-code flow

Replace the xAI OAuth stack with the Kimi device authorization grant:
- kimi_oauth.rs wire layer (device_authorization + token poll + refresh
  against kigi_env::oauth_host(); client_id per PRD; retryable statuses
  429/5xx with backoff; expired_token restarts authorization)
- X-Msh-Device-{Name,Model,Id} headers; device_id minted uuid4-hex at
  ~/.kigi/device_id (0600)
- Storage: system keyring service `kigi`, entry `oauth/kimi-code`
  (macOS/Windows native backends), atomic-file fallback under ~/.kigi;
  official client's keyring/~/.kimi never touched
- Refresh manager: 60s tick, threshold max(300, expires_in*0.5),
  401-tombstone keyed by rejected refresh token with 300s cooldown and
  rotation auto-clear, cross-process lock with sibling-adoption
  triple-check, sleep/wake forced refresh
- Deleted xAI machinery: enterprise OIDC (PKCE/JWKS/teams), devbox login,
  external auth provider, JWT tier gating + subscription paywall stack,
  X-XAI-Token-Auth marker headers, ZDR gates, /user enrichment
- kigi login / TUI /login both drive the device flow; login-host display
  now derives from kigi_env::oauth_host()
- 264 auth unit/wiremock tests; live contract probe of
  auth.kimi.com/api/oauth/device_authorization matches the wire shapes

Gates: check/clippy --all-targets clean, fmt, deny ok, kigi-shell lib
5131 tests green.
This commit is contained in:
2026-07-17 07:37:29 -04:00
parent d6c20fc13f
commit 021b82443d
117 changed files with 4052 additions and 19900 deletions
+1 -5
View File
@@ -326,7 +326,7 @@ pub async fn connect_via_leader(
// agent's disk-rotated token under the file lock (`try_adopt_disk_token`).
let auth_manager = std::sync::Arc::new(kigi_shell::auth::AuthManager::new(
&kigi_shell::util::kigi_home::kigi_home(),
agent_config.grok_com_config.clone(),
agent_config.kimi_code_config.clone(),
));
Ok(AcpConnection {
@@ -897,11 +897,7 @@ mod tests {
// Realistic enterprise user: no cached session token, default `grok.com`
// login (no enterprise OIDC).
has_cached_token: false,
has_enterprise_oidc: false,
enterprise_oidc_issuer: None,
login_label: None,
has_auth_provider_command: false,
preferred_method: None,
});
let (needs, label, method_id, mode) = startup_auth_metadata(&built.methods);
+2 -2
View File
@@ -40,9 +40,9 @@ pub async fn spawn_grok_shell(
) -> Result<SpawnedAgent> {
let auth_manager = std::sync::Arc::new(AuthManager::new(
&kigi_home(),
agent_config.grok_com_config.clone(),
agent_config.kimi_code_config.clone(),
));
auth_manager.configure_refresher(agent_config.grok_com_config.auth_provider_command.clone());
auth_manager.configure_refresher();
// Pause token refreshes across system sleep so an OIDC refresh can't
// straddle a suspend (which can revoke the refresh token and force
// re-login). No-op where the OS listener is unavailable.