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,198 @@
|
||||
//! REAL host-clipboard plumbing shared by the OS-native paste e2e tests and
|
||||
//! the `paste_latency` bench: `pbcopy` / `pbpaste` / `osascript` on macOS,
|
||||
//! PowerShell `Set-Clipboard` / `Get-Clipboard` / WinForms `SetImage` on
|
||||
//! Windows.
|
||||
//!
|
||||
//! Everything here mutates or reads the MACHINE-GLOBAL clipboard, so callers
|
||||
//! must serialize against each other (e.g. `#[serial_test::serial]`) and
|
||||
//! should hold a [`HostClipboardTextGuard`] to restore the prior text. CI
|
||||
//! sessions without a usable clipboard are detected via
|
||||
//! [`clipboard_roundtrip_works`] so tests can skip instead of fail.
|
||||
//!
|
||||
//! Compiles on every platform so cross-platform builds of the consumers stay
|
||||
//! green (the bench gates at runtime); on unsupported hosts the tool spawns
|
||||
//! simply fail.
|
||||
|
||||
use std::io::Write as _;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
|
||||
/// Copy `text` to the host clipboard via `pbcopy`.
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn pbcopy(text: &str) -> Result<()> {
|
||||
let mut cmd = Command::new("pbcopy");
|
||||
cmd.stdin(Stdio::piped());
|
||||
kigi_tty_utils::detach_std_command(&mut cmd);
|
||||
let mut child = cmd.spawn().context("spawn pbcopy")?;
|
||||
child
|
||||
.stdin
|
||||
.take()
|
||||
.context("pbcopy stdin")?
|
||||
.write_all(text.as_bytes())
|
||||
.context("write pbcopy stdin")?;
|
||||
let status = child.wait().context("wait pbcopy")?;
|
||||
if !status.success() {
|
||||
bail!("pbcopy exited with {status}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copy `text` to the host clipboard via PowerShell `Set-Clipboard`.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn pbcopy(text: &str) -> Result<()> {
|
||||
// Text travels over stdin, never inside the command line — no quoting.
|
||||
let mut cmd = Command::new("powershell");
|
||||
cmd.args([
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-Command",
|
||||
"Set-Clipboard -Value ([Console]::In.ReadToEnd())",
|
||||
])
|
||||
.stdin(Stdio::piped());
|
||||
kigi_tty_utils::detach_std_command(&mut cmd);
|
||||
let mut child = cmd.spawn().context("spawn powershell Set-Clipboard")?;
|
||||
child
|
||||
.stdin
|
||||
.take()
|
||||
.context("powershell stdin")?
|
||||
.write_all(text.as_bytes())
|
||||
.context("write powershell stdin")?;
|
||||
let status = child.wait().context("wait powershell Set-Clipboard")?;
|
||||
if !status.success() {
|
||||
bail!("powershell Set-Clipboard exited with {status}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Current host clipboard TEXT via `pbpaste` (`None` when unavailable).
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn pbpaste() -> Option<String> {
|
||||
let mut cmd = Command::new("pbpaste");
|
||||
kigi_tty_utils::detach_std_command(&mut cmd);
|
||||
let out = cmd.output().ok()?;
|
||||
if !out.status.success() {
|
||||
return None;
|
||||
}
|
||||
Some(String::from_utf8_lossy(&out.stdout).into_owned())
|
||||
}
|
||||
|
||||
/// Current host clipboard TEXT via PowerShell `Get-Clipboard` (`None` when
|
||||
/// unavailable).
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn pbpaste() -> Option<String> {
|
||||
let mut cmd = Command::new("powershell");
|
||||
// Console::Out.Write avoids the trailing newline PowerShell's pipeline
|
||||
// output would append (roundtrip checks compare exact text).
|
||||
cmd.args([
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-Command",
|
||||
"$c = Get-Clipboard -Raw; if ($null -ne $c) { [Console]::Out.Write($c) }",
|
||||
]);
|
||||
kigi_tty_utils::detach_std_command(&mut cmd);
|
||||
let out = cmd.output().ok()?;
|
||||
if !out.status.success() {
|
||||
return None;
|
||||
}
|
||||
Some(String::from_utf8_lossy(&out.stdout).into_owned())
|
||||
}
|
||||
|
||||
/// Put the PNG at `path` on the host clipboard as a raster (`«class PNGf»`).
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn set_clipboard_png(path: &Path) -> Result<()> {
|
||||
let script = format!(
|
||||
"set the clipboard to (read (POSIX file \"{}\") as «class PNGf»)",
|
||||
path.display()
|
||||
);
|
||||
let mut cmd = Command::new("osascript");
|
||||
cmd.arg("-e").arg(&script);
|
||||
kigi_tty_utils::detach_std_command(&mut cmd);
|
||||
let status = cmd.status().context("spawn osascript")?;
|
||||
if !status.success() {
|
||||
bail!("osascript set-clipboard-PNG exited with {status}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Put the PNG at `path` on the host clipboard as a raster via a WinForms
|
||||
/// `Clipboard::SetImage` PowerShell one-liner.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_clipboard_png(path: &Path) -> Result<()> {
|
||||
use base64::Engine as _;
|
||||
// WinForms Clipboard requires an STA thread; -EncodedCommand (base64 of
|
||||
// UTF-16LE) sidesteps every cmd/PowerShell quoting layer, leaving only
|
||||
// the PS single-quote escape for the embedded path.
|
||||
let ps_path = path.display().to_string().replace('\'', "''");
|
||||
let script = format!(
|
||||
"Add-Type -AssemblyName System.Windows.Forms,System.Drawing; \
|
||||
$img = [System.Drawing.Image]::FromFile('{ps_path}'); \
|
||||
[System.Windows.Forms.Clipboard]::SetImage($img); \
|
||||
$img.Dispose()"
|
||||
);
|
||||
let utf16: Vec<u8> = script
|
||||
.encode_utf16()
|
||||
.flat_map(|u| u.to_le_bytes())
|
||||
.collect();
|
||||
let encoded = base64::engine::general_purpose::STANDARD.encode(utf16);
|
||||
let mut cmd = Command::new("powershell");
|
||||
cmd.args([
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-STA",
|
||||
"-EncodedCommand",
|
||||
&encoded,
|
||||
]);
|
||||
kigi_tty_utils::detach_std_command(&mut cmd);
|
||||
let status = cmd.status().context("spawn powershell SetImage")?;
|
||||
if !status.success() {
|
||||
bail!("powershell SetImage exited with {status}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write a small solid-color PNG under `dir` and return its path.
|
||||
pub fn write_fixture_png(dir: &Path) -> Result<PathBuf> {
|
||||
let path = dir.join("host_clipboard_fixture.png");
|
||||
let buf: image::ImageBuffer<image::Rgba<u8>, Vec<u8>> =
|
||||
image::ImageBuffer::from_pixel(64, 64, image::Rgba([200, 40, 120, 255]));
|
||||
buf.save(&path)
|
||||
.context("write host clipboard fixture png")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Whether the host clipboard actually works in this session: sets a nonce
|
||||
/// via the text helper and reads it back. False on clipboard-less CI sessions
|
||||
/// (e.g. a Windows service session with no interactive desktop) so tests can
|
||||
/// SKIP loudly instead of failing on environment.
|
||||
pub fn clipboard_roundtrip_works() -> bool {
|
||||
let nonce = format!("HOSTCLIPROUNDTRIP{}", std::process::id());
|
||||
if pbcopy(&nonce).is_err() {
|
||||
return false;
|
||||
}
|
||||
// trim_end: some clipboard tool chains append a trailing newline.
|
||||
pbpaste().is_some_and(|t| t.trim_end() == nonce)
|
||||
}
|
||||
|
||||
/// Best-effort save/restore of the host TEXT clipboard around a test or bench
|
||||
/// run. Restores on drop (panic/unwind included). A prior IMAGE clipboard
|
||||
/// cannot be restored — `pbpaste` only reads the text representation.
|
||||
pub struct HostClipboardTextGuard {
|
||||
prior: Option<String>,
|
||||
}
|
||||
|
||||
impl HostClipboardTextGuard {
|
||||
pub fn save() -> Self {
|
||||
Self { prior: pbpaste() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HostClipboardTextGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(prior) = self.prior.take() {
|
||||
// Non-panicking restore: a panicking test must still unwind cleanly.
|
||||
let _ = pbcopy(&prior);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user