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,17 @@
|
||||
[package]
|
||||
license = "Apache-2.0"
|
||||
name = "kigi-env"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Backend environment presets for the Grok CLI crate family: endpoint URL defaults and env-var test support."
|
||||
|
||||
[features]
|
||||
# single shared rlib per crate, so downstream Bazel test targets need the
|
||||
default-bazel = []
|
||||
|
||||
[dependencies]
|
||||
tracing = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,169 @@
|
||||
//! Backend endpoint defaults for the Kigi crate family.
|
||||
//!
|
||||
//! Kigi talks to exactly four first-party endpoints (PRD §8.2); everything
|
||||
//! else (MCP servers, GitHub release assets) is user- or release-configured.
|
||||
//! Each value resolves as an env-var override when set, else the compiled
|
||||
//! production default.
|
||||
|
||||
/// The complete set of first-party endpoints.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct KigiEndpoints {
|
||||
/// Kimi Code subscription inference API (OpenAI chat/completions compatible).
|
||||
pub coding_api_base_url: &'static str,
|
||||
/// OAuth device-flow host for Kimi Code subscription login.
|
||||
pub oauth_host: &'static str,
|
||||
/// GitHub Releases API endpoint the self-updater polls.
|
||||
pub update_base_url: &'static str,
|
||||
/// Human-facing page for subscription upgrade guidance.
|
||||
pub upgrade_page_url: &'static str,
|
||||
}
|
||||
|
||||
pub const PRODUCTION_ENDPOINTS: KigiEndpoints = KigiEndpoints {
|
||||
coding_api_base_url: "https://api.kimi.com/coding/v1",
|
||||
oauth_host: "https://auth.kimi.com",
|
||||
update_base_url: "https://api.github.com/repos/ZacharyZhang-NY/Kigi-CLI/releases",
|
||||
upgrade_page_url: "https://www.kimi.com/code/",
|
||||
};
|
||||
|
||||
/// Env var overriding [`coding_api_base_url`] (PRD F3).
|
||||
pub const CODE_BASE_URL_ENV: &str = "KIGI_CODE_BASE_URL";
|
||||
/// Env var overriding [`oauth_host`] (PRD F1).
|
||||
pub const OAUTH_HOST_ENV: &str = "KIGI_OAUTH_HOST";
|
||||
|
||||
fn resolve(var: &str, compiled: &'static str) -> String {
|
||||
match std::env::var(var) {
|
||||
Ok(v) if !v.trim().is_empty() => v,
|
||||
_ => compiled.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Subscription inference base URL: `KIGI_CODE_BASE_URL` override when set,
|
||||
/// else the compiled production endpoint.
|
||||
pub fn coding_api_base_url() -> String {
|
||||
resolve(CODE_BASE_URL_ENV, PRODUCTION_ENDPOINTS.coding_api_base_url)
|
||||
}
|
||||
|
||||
/// OAuth host: `KIGI_OAUTH_HOST` override when set, else the compiled
|
||||
/// production endpoint.
|
||||
pub fn oauth_host() -> String {
|
||||
resolve(OAUTH_HOST_ENV, PRODUCTION_ENDPOINTS.oauth_host)
|
||||
}
|
||||
|
||||
/// GitHub Releases API endpoint for the self-updater. Compile-time constant;
|
||||
/// the updater's channel/rollback semantics layer on top of it.
|
||||
pub fn update_base_url() -> &'static str {
|
||||
PRODUCTION_ENDPOINTS.update_base_url
|
||||
}
|
||||
|
||||
/// Subscription upgrade page shown in rate-limit and upsell surfaces.
|
||||
pub fn upgrade_page_url() -> &'static str {
|
||||
PRODUCTION_ENDPOINTS.upgrade_page_url
|
||||
}
|
||||
|
||||
/// Serializes env-var mutation across tests; `std::env` is process-global.
|
||||
static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
|
||||
ENV_LOCK.lock().unwrap_or_else(|p| p.into_inner())
|
||||
}
|
||||
|
||||
/// RAII env-var override for tests: constructors snapshot the prior value
|
||||
/// under [`ENV_LOCK`], `Drop` restores it, panics included.
|
||||
pub struct EnvVarGuard {
|
||||
key: &'static str,
|
||||
prev: Option<String>,
|
||||
_lock: std::sync::MutexGuard<'static, ()>,
|
||||
}
|
||||
|
||||
impl EnvVarGuard {
|
||||
pub fn set(key: &'static str, value: &str) -> Self {
|
||||
let lock = env_lock();
|
||||
let prev = std::env::var(key).ok();
|
||||
unsafe { std::env::set_var(key, value) };
|
||||
Self {
|
||||
key,
|
||||
prev,
|
||||
_lock: lock,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(key: &'static str) -> Self {
|
||||
let lock = env_lock();
|
||||
let prev = std::env::var(key).ok();
|
||||
unsafe { std::env::remove_var(key) };
|
||||
Self {
|
||||
key,
|
||||
prev,
|
||||
_lock: lock,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the value while still holding the env lock.
|
||||
pub fn set_value(&self, value: &str) {
|
||||
unsafe { std::env::set_var(self.key, value) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EnvVarGuard {
|
||||
fn drop(&mut self) {
|
||||
match self.prev.take() {
|
||||
Some(prev) => unsafe { std::env::set_var(self.key, prev) },
|
||||
None => unsafe { std::env::remove_var(self.key) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn env_var_guard_set_value_updates_then_restores_on_drop() {
|
||||
const KEY: &str = "KIGI_ENV_VAR_GUARD_SET_VALUE_PROBE";
|
||||
let before = std::env::var(KEY).ok();
|
||||
{
|
||||
let guard = EnvVarGuard::set(KEY, "initial");
|
||||
assert_eq!(std::env::var(KEY).ok().as_deref(), Some("initial"));
|
||||
guard.set_value("updated");
|
||||
assert_eq!(
|
||||
std::env::var(KEY).ok().as_deref(),
|
||||
Some("updated"),
|
||||
"set_value must update the env var while the guard is live"
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
std::env::var(KEY).ok(),
|
||||
before,
|
||||
"Drop must restore the pre-guard snapshot (was {before:?})"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn override_env_vars_win_over_compiled_defaults() {
|
||||
let _g = EnvVarGuard::set(CODE_BASE_URL_ENV, "https://example.test/v1");
|
||||
assert_eq!(coding_api_base_url(), "https://example.test/v1");
|
||||
drop(_g);
|
||||
let _g2 = EnvVarGuard::set(OAUTH_HOST_ENV, "https://auth.example.test");
|
||||
assert_eq!(oauth_host(), "https://auth.example.test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blank_override_falls_back_to_compiled_default() {
|
||||
let _g = EnvVarGuard::set(CODE_BASE_URL_ENV, " ");
|
||||
assert_eq!(
|
||||
coding_api_base_url(),
|
||||
PRODUCTION_ENDPOINTS.coding_api_base_url
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn production_endpoints_are_https() {
|
||||
for url in [
|
||||
PRODUCTION_ENDPOINTS.coding_api_base_url,
|
||||
PRODUCTION_ENDPOINTS.oauth_host,
|
||||
PRODUCTION_ENDPOINTS.update_base_url,
|
||||
PRODUCTION_ENDPOINTS.upgrade_page_url,
|
||||
] {
|
||||
assert!(url.starts_with("https://"), "{url} must be https");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user