From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: iceBear67 Date: Sat, 15 Aug 2026 06:39:14 +0000 Subject: [PATCH] Add [remote_control] config section for the /rc glance bridge diff --git a/crates/codegen/xai-grok-shell/src/agent/config.rs b/crates/codegen/xai-grok-shell/src/agent/config.rs index 2a2ab0491b226cee84fd968869a2daaff707f9a7..be842a31d205db6bc88ac8f46c0af8e2cecbb4ef 100644 --- a/crates/codegen/xai-grok-shell/src/agent/config.rs +++ b/crates/codegen/xai-grok-shell/src/agent/config.rs @@ -1023,6 +1023,79 @@ pub struct CliConfig { #[serde(skip_serializing_if = "Option::is_none")] pub session_picker_grouped: Option, } +/// `[remote_control]` section: the `/rc` bridge to a grok-glance server. +/// +/// `/rc` mirrors the session you are already in to glance over ACP -- it does not +/// restart it, switch it to headless, or hand it over. Both ends stay live and +/// either can drive the session. +/// +/// Every field has an env override, which is how the API key is normally supplied: +/// a bearer token in a plaintext config file is a poor default. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct RemoteControlConfig { + /// Glance WebSocket endpoint, e.g. `wss://glance.example.com/api/acp/agent`. + /// Env `GROK_RC_URL`. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + /// Bearer token presented on the WebSocket upgrade. Env `GROK_RC_API_KEY`. + #[serde(skip_serializing_if = "Option::is_none")] + pub api_key: Option, + /// Connect at session start instead of waiting for `/rc`. Env `GROK_RC_AUTO_START`. + #[serde(skip_serializing_if = "Option::is_none")] + pub auto_start: Option, + /// Session updates retained for replay when glance (re)connects. + /// See [`RemoteControlConfig::DEFAULT_REPLAY_BUFFER`]. + #[serde(skip_serializing_if = "Option::is_none")] + pub replay_buffer: Option, +} +impl RemoteControlConfig { + /// Bounded so a long session cannot grow the bridge without limit; large + /// enough that a browser attaching mid-session still sees useful history. + pub const DEFAULT_REPLAY_BUFFER: usize = 2048; + /// Env wins over config, and an empty value counts as unset -- an exported + /// but blank `GROK_RC_URL` should not mask a working config entry. + fn env_override(key: &str) -> Option { + std::env::var(key) + .ok() + .map(|v| v.trim().to_owned()) + .filter(|v| !v.is_empty()) + } + pub fn resolved_url(&self) -> Option { + Self::env_override("GROK_RC_URL").or_else(|| { + self.url + .as_deref() + .map(str::trim) + .filter(|v| !v.is_empty()) + .map(str::to_owned) + }) + } + pub fn resolved_api_key(&self) -> Option { + Self::env_override("GROK_RC_API_KEY").or_else(|| { + self.api_key + .as_deref() + .map(str::trim) + .filter(|v| !v.is_empty()) + .map(str::to_owned) + }) + } + pub fn auto_start_enabled(&self) -> bool { + Self::env_override("GROK_RC_AUTO_START") + .map(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on")) + .or(self.auto_start) + .unwrap_or(false) + } + pub fn replay_buffer_len(&self) -> usize { + self.replay_buffer + .filter(|n| *n > 0) + .unwrap_or(Self::DEFAULT_REPLAY_BUFFER) + } + /// Both a URL and a key are required; `/rc` reports which one is missing + /// rather than dialing an endpoint that will reject it. + pub fn is_configured(&self) -> bool { + self.resolved_url().is_some() && self.resolved_api_key().is_some() + } +} #[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(default)] pub struct DiagnosticsConfig { @@ -1361,6 +1434,9 @@ pub struct Config { pub paths: PathsConfig, #[serde(default, skip_serializing)] pub cli: CliConfig, + /// `[remote_control]` section: the `/rc` bridge to grok-glance. + #[serde(default, skip_serializing)] + pub remote_control: RemoteControlConfig, #[serde(default, skip_serializing)] pub models: ModelsConfig, #[serde(default, skip_serializing)] @@ -1767,6 +1843,7 @@ impl Default for Config { feedback: FeedbackConfig::default(), paths: PathsConfig::default(), cli: CliConfig::default(), + remote_control: RemoteControlConfig::default(), models: ModelsConfig::default(), harness: HarnessConfig::default(), relay: RelayConfig::default(),