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:
@@ -111,10 +111,7 @@ async fn handle_session_rename(agent: &MvpAgent, args: &acp::ExtRequest) -> ExtR
|
||||
// Send a SessionSummaryGenerated notification so the TUI updates its title
|
||||
notify_session_title(agent, session_id, &req.title).await;
|
||||
|
||||
if agent.is_writeback_storage()
|
||||
&& let Some(auth) = agent.current_auth()
|
||||
&& !auth.is_zdr_team()
|
||||
{
|
||||
if agent.is_writeback_storage() && agent.current_auth().is_some() {
|
||||
use crate::remote::client::BackendClient;
|
||||
use crate::session::export::ExportedMetadata;
|
||||
|
||||
@@ -133,15 +130,7 @@ async fn handle_session_rename(agent: &MvpAgent, args: &acp::ExtRequest) -> ExtR
|
||||
// Hook 2: update session replica with summary (fire-and-forget)
|
||||
if let Some(client) = agent.session_registry_client() {
|
||||
let sid = req.session_id.to_string();
|
||||
let title = if agent
|
||||
.auth_manager
|
||||
.current_or_expired()
|
||||
.is_some_and(|a| a.is_zdr_team())
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(req.title.clone())
|
||||
};
|
||||
let title = Some(req.title.clone());
|
||||
tokio::spawn(async move {
|
||||
let update = crate::agent::session_registry_client::UpdateRequest {
|
||||
summary: title,
|
||||
@@ -248,8 +237,7 @@ async fn handle_session_delete(agent: &MvpAgent, args: &acp::ExtRequest) -> ExtR
|
||||
// For writeback storage (non-ZDR): remote delete is authoritative for
|
||||
// the cloud history and runs first; on failure no local bits are
|
||||
// touched so the pager does not remove the row or toast success.
|
||||
let needs_remote =
|
||||
agent.is_writeback_storage() && agent.current_auth().is_some_and(|a| !a.is_zdr_team());
|
||||
let needs_remote = agent.is_writeback_storage() && agent.current_auth().is_some();
|
||||
|
||||
// Shared delete: remote-first, then local disk + FTS eviction.
|
||||
// Mirrored by the `grok sessions delete <id>` CLI path.
|
||||
|
||||
Reference in New Issue
Block a user