M0: compilable skeleton — Kigi 0.1.0 fork surgery
Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.
Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
ptyctl, ptyctl-cli, third_party/ unchanged; proto package
xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
(templates re-encrypted)
Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
module & dc_log, heap-profile uploader, auth-diagnostics uploader,
session-analytics halves of feedback; local zero-egress observability
preserved in new kigi-log crate (unified log, --debug firehose,
subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
shell util
Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted
Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean
Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
(new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
fast-worktree); RSS measurement tests serialized via serial_test
Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
notices sustained; kigi-tools ported-code notices extended; README,
CONTRIBUTING, SECURITY, AGENTS.md rewritten
Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
//! Cross-platform system **sleep/wake** (suspend/resume) notifications.
|
||||
//!
|
||||
//! The motivating use case: an OIDC token refresh that is *in flight when the
|
||||
//! laptop sleeps* can lose its rotated successor token (the server processes
|
||||
//! the request, rotates/revokes the old refresh token, and the response is
|
||||
//! lost across the suspend). On wake the client is holding a dead refresh
|
||||
//! token and the user is forced to re-login. See
|
||||
//! `kigi-shell`'s `AuthManager` sleep gate, which consumes these events to
|
||||
//! avoid *starting* a refresh just before sleep. An in-flight refresh is
|
||||
//! deliberately left to finish, never aborted (dropping it could discard a
|
||||
//! rotated-token response and cause the very revocation this guards against);
|
||||
//! instead, its [`PowerEvent::WillSleep`] handler may block briefly (bounded)
|
||||
//! to hold off the suspend until that in-flight refresh completes — see the
|
||||
//! callback contract below.
|
||||
//!
|
||||
//! This crate exposes a single tiny abstraction — [`SystemPowerListener`] —
|
||||
//! with per-OS implementations behind `#[cfg]` and a no-op fallback:
|
||||
//!
|
||||
//! | OS | Mechanism |
|
||||
//! |---------|----------------------------------------------------------------------|
|
||||
//! | macOS | IOKit `IORegisterForSystemPower` on a dedicated `CFRunLoop` thread |
|
||||
//! | Windows | `PowerRegisterSuspendResumeNotification` (`DEVICE_NOTIFY_CALLBACK`) |
|
||||
//! | Linux | logind D-Bus `PrepareForSleep` signal + a `delay` inhibitor lock |
|
||||
//! | other | no-op (returns `None` from [`SystemPowerListener::start`]) |
|
||||
//!
|
||||
//! The callback fires from a platform event thread/callback, so it must be
|
||||
//! `Send + Sync`. It should return promptly, but a [`PowerEvent::WillSleep`]
|
||||
//! handler *may* block for a short, bounded time to hold off sleep: the per-OS
|
||||
//! implementations acknowledge the transition only **after** the callback
|
||||
//! returns (macOS calls `IOAllowPowerChange`; Linux releases its `delay`
|
||||
//! inhibitor), so blocking there delays the suspend itself. Keep any such block
|
||||
//! within the OS budget — macOS allows ~30 s after `kIOMessageSystemWillSleep`;
|
||||
//! Linux logind's `InhibitDelayMaxSec` defaults to 5 s — or the OS proceeds to
|
||||
//! sleep anyway. `DidWake` handlers must stay cheap and non-blocking.
|
||||
|
||||
/// A system power transition.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PowerEvent {
|
||||
/// The system is about to sleep (lid close / suspend), or — on macOS — is
|
||||
/// *negotiating* an idle sleep (`kIOMessageCanSystemSleep`), which may
|
||||
/// follow within seconds. Best-effort: on macOS and Linux there is a short
|
||||
/// window to react before sleep proceeds, and the handler may block within
|
||||
/// it to hold off the suspend (see the crate-level callback contract); on
|
||||
/// Windows modern-standby it may not wait at all.
|
||||
///
|
||||
/// Because the idle-sleep negotiation can be vetoed (by any power client),
|
||||
/// a `WillSleep` is **not** a guarantee that sleep follows: it may be
|
||||
/// succeeded by a [`Self::DidWake`] without an intervening suspend.
|
||||
/// Handlers must therefore be idempotent and safe to "cancel" via
|
||||
/// `DidWake`.
|
||||
WillSleep,
|
||||
/// The system resumed from sleep, or a previously announced sleep was
|
||||
/// cancelled (macOS `kIOMessageSystemWillNotSleep` after a vetoed
|
||||
/// idle-sleep query). Both mean "not sleeping (anymore)".
|
||||
DidWake,
|
||||
}
|
||||
|
||||
/// Boxed user callback invoked on each [`PowerEvent`].
|
||||
pub type PowerCallback = Box<dyn Fn(PowerEvent) + Send + Sync + 'static>;
|
||||
|
||||
/// A coarse, synchronously-queryable system power state (see
|
||||
/// [`current_power_state`]).
|
||||
///
|
||||
/// The motivating distinction is **dark wake**: on macOS the system wakes
|
||||
/// briefly for background/maintenance work (Power Nap, network/disk
|
||||
/// maintenance) with the display off and no user present, then re-sleeps —
|
||||
/// frequently *without* delivering a [`PowerEvent`] at all (the legacy
|
||||
/// `IORegisterForSystemPower` notifications used by [`SystemPowerListener`] are
|
||||
/// blind to dark wakes). Code that starts irreversible network work — notably a
|
||||
/// one-time-use OIDC refresh-token exchange — should avoid doing so during a
|
||||
/// dark wake, because the machine may re-sleep mid-request and lose the
|
||||
/// response that carries the rotated token.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PowerState {
|
||||
/// Full / user wake: display (graphics) capability present — a user is (or
|
||||
/// can be) present. Safe to start irreversible work.
|
||||
FullWake,
|
||||
/// Dark wake: CPU (and usually network/disk) up for background or
|
||||
/// maintenance work, but display off and no user. The system may re-sleep
|
||||
/// at any moment with no warning.
|
||||
DarkWake,
|
||||
/// State could not be determined: an unsupported OS, or the platform query
|
||||
/// failed / returned a transitional sample. Callers should treat this as
|
||||
/// "no signal" and fall back to their existing behavior — never block on
|
||||
/// it.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Query the current system power state synchronously.
|
||||
///
|
||||
/// Cheap, non-blocking, and never panics. Returns [`PowerState::Unknown`] on
|
||||
/// platforms without a real implementation (currently everything except macOS)
|
||||
/// or when the platform query fails. Unlike [`SystemPowerListener`], this needs
|
||||
/// no running listener — on macOS it is a single connection-less IOKit call.
|
||||
pub fn current_power_state() -> PowerState {
|
||||
imp::current_power_state()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[path = "macos.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[path = "linux.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
mod imp {
|
||||
use super::PowerCallback;
|
||||
|
||||
pub(crate) struct Listener;
|
||||
|
||||
impl Listener {
|
||||
pub(crate) fn start(_callback: PowerCallback) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_power_state() -> super::PowerState {
|
||||
super::PowerState::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
/// A running system-power listener. On macOS/Windows, dropping it stops the
|
||||
/// listener and releases its OS resources. On Linux the worker parks on a
|
||||
/// blocking logind signal and cannot be cleanly interrupted, so it runs (with
|
||||
/// its D-Bus connection + sleep-delay inhibitor) until process exit — see the
|
||||
/// `linux` module. Intended as a process-lifetime singleton.
|
||||
pub struct SystemPowerListener {
|
||||
// Kept for its `Drop`; the field is read on platforms with a real impl.
|
||||
#[allow(dead_code)]
|
||||
inner: imp::Listener,
|
||||
}
|
||||
|
||||
impl SystemPowerListener {
|
||||
/// Start listening for system sleep/wake events.
|
||||
///
|
||||
/// Returns `None` when the platform mechanism is unavailable — an
|
||||
/// unsupported OS, a missing systemd-logind on Linux, or a registration
|
||||
/// failure. Callers should treat `None` as "no power notifications" and
|
||||
/// degrade gracefully (the dependent feature simply does not engage).
|
||||
///
|
||||
/// `callback` is invoked from a platform event thread, so it must be
|
||||
/// `Send + Sync`, cheap, and non-blocking.
|
||||
pub fn start<F>(callback: F) -> Option<Self>
|
||||
where
|
||||
F: Fn(PowerEvent) + Send + Sync + 'static,
|
||||
{
|
||||
imp::Listener::start(Box::new(callback)).map(|inner| Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn power_event_is_copy_eq() {
|
||||
let e = PowerEvent::WillSleep;
|
||||
let copied = e; // Copy
|
||||
assert_eq!(e, copied);
|
||||
assert_ne!(PowerEvent::WillSleep, PowerEvent::DidWake);
|
||||
}
|
||||
|
||||
/// `start` + `drop` must be clean on every platform: no panic and no hang
|
||||
/// (the latter exercises the macOS run-loop teardown). On Linux/Windows CI
|
||||
/// `start` may return `None` (no system bus / unsupported) — also fine.
|
||||
#[test]
|
||||
fn start_and_drop_is_clean() {
|
||||
// Bind and let it drop at end of scope rather than calling `drop()`:
|
||||
// on platforms where the listener owns no `Drop` type (e.g. Linux,
|
||||
// whose worker is detached and runs until process exit) an explicit
|
||||
// `drop()` trips `clippy::drop_non_drop`.
|
||||
let _listener = SystemPowerListener::start(|_event| {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user