huskies: merge 1209 story Gateway lifecycle & telemetry MCP: gateway_info, restart_gateway, gateway_logs, start_story, chat_telemetry
This commit is contained in:
@@ -79,6 +79,7 @@ pub fn read_bot_config_raw(config_dir: &Path) -> BotConfigFields {
|
||||
password: s("password"),
|
||||
slack_bot_token: s("slack_bot_token"),
|
||||
slack_signing_secret: s("slack_signing_secret"),
|
||||
model: s("model"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +92,8 @@ pub struct BotConfigFields {
|
||||
pub password: Option<String>,
|
||||
pub slack_bot_token: Option<String>,
|
||||
pub slack_signing_secret: Option<String>,
|
||||
/// Claude Code model override configured for this bot (`gateway_info` MCP tool, story 1209).
|
||||
pub model: Option<String>,
|
||||
}
|
||||
|
||||
/// Write a `bot.toml` from the given content string.
|
||||
|
||||
@@ -38,13 +38,32 @@ use io::Client;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::time::Instant;
|
||||
use tokio::sync::Mutex as TokioMutex;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub use crate::crdt_state::NodePresenceView;
|
||||
|
||||
// ── Uptime (gateway_info MCP tool, story 1209) ──────────────────────────────
|
||||
|
||||
/// Instant the gateway process started, set once by `GatewayState::new`.
|
||||
static GATEWAY_START_TIME: OnceLock<Instant> = OnceLock::new();
|
||||
|
||||
/// Seconds elapsed since the gateway process started.
|
||||
///
|
||||
/// Lazily initialises the start time on first call so this never panics, but
|
||||
/// `GatewayState::new` calls it once at startup so the timer reflects actual
|
||||
/// process start rather than first `gateway_info` call in normal operation.
|
||||
pub fn gateway_uptime_secs() -> u64 {
|
||||
GATEWAY_START_TIME
|
||||
.get_or_init(Instant::now)
|
||||
.elapsed()
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
// ── Status event broadcaster ────────────────────────────────────────────────
|
||||
|
||||
/// Capacity of the gateway status event broadcast channel.
|
||||
@@ -272,6 +291,7 @@ impl GatewayState {
|
||||
config_dir: PathBuf,
|
||||
port: u16,
|
||||
) -> Result<Self, String> {
|
||||
GATEWAY_START_TIME.get_or_init(Instant::now);
|
||||
let first_from_config = config::validate_config(&gateway_config)?;
|
||||
// Restore active project from CRDT if the stored value is still valid.
|
||||
let first = crate::crdt_state::read_gateway_active_project()
|
||||
@@ -838,6 +858,17 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_uptime_secs_is_zero_or_positive_immediately_after_start() {
|
||||
// Just ensure it doesn't panic and returns a sane (small) value —
|
||||
// the static is process-global so we can't assert it's exactly 0.
|
||||
let uptime = gateway_uptime_secs();
|
||||
assert!(
|
||||
uptime < 3600,
|
||||
"uptime should be small in a fresh test run, got {uptime}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_state_rejects_empty_config() {
|
||||
let config = GatewayConfig {
|
||||
|
||||
Reference in New Issue
Block a user