Files
huskies/server/src/agent_mode/context.rs
T

104 lines
3.8 KiB
Rust
Raw Normal View History

2026-04-28 18:59:10 +00:00
//! Agent-mode HTTP context construction and gateway registration.
use std::path::Path;
use std::sync::Arc;
use tokio::sync::broadcast;
use crate::agents::AgentPool;
use crate::io::watcher;
use crate::slog;
/// Register this build agent with a gateway using a one-time join token.
///
/// POSTs `{ token, label, address }` to `{gateway_url}/gateway/register`. On
/// success the gateway stores the agent and it will appear in the gateway UI.
pub(super) async fn register_with_gateway(
gateway_url: &str,
token: &str,
label: &str,
address: &str,
) {
let client = reqwest::Client::new();
let url = format!("{}/gateway/register", gateway_url.trim_end_matches('/'));
let body = serde_json::json!({
"token": token,
"label": label,
"address": address,
});
match client.post(&url).json(&body).send().await {
Ok(resp) if resp.status().is_success() => {
slog!("[agent-mode] Registered with gateway at {gateway_url}");
}
Ok(resp) => {
slog!(
"[agent-mode] Gateway registration failed: HTTP {}",
resp.status()
);
}
Err(e) => {
slog!("[agent-mode] Gateway registration error: {e}");
}
}
}
/// Build a minimal [`AppContext`] for the agent-mode HTTP server.
///
/// The `/crdt-sync` handler receives `Data<&Arc<AppContext>>` but doesn't
/// actually use it (the parameter is named `_ctx`). We construct a
/// lightweight context with just enough state to satisfy Poem's data
/// extractor.
pub(super) fn build_agent_app_context(
project_root: &Path,
port: u16,
watcher_tx: broadcast::Sender<watcher::WatcherEvent>,
) -> crate::http::context::AppContext {
let state = crate::state::SessionState::default();
*state.project_root.lock().unwrap() = Some(project_root.to_path_buf());
let store_path = project_root.join(".huskies").join("store.json");
let store = Arc::new(
crate::store::JsonFileStore::from_path(store_path)
.unwrap_or_else(|e| panic!("Failed to open store: {e}")),
);
let (reconciliation_tx, _) = broadcast::channel(64);
let (perm_tx, perm_rx) = tokio::sync::mpsc::unbounded_channel();
let timer_store = Arc::new(crate::service::timer::TimerStore::load(
project_root.join(".huskies").join("timers.json"),
));
2026-05-14 16:26:49 +00:00
let scheduled_timer_store = Arc::new(crate::service::timer::ScheduledTimerStore::load(
project_root.join(".huskies").join("scheduled_timers.json"),
));
2026-04-28 18:59:10 +00:00
let agents = Arc::new(AgentPool::new(port, watcher_tx.clone()));
let services = Arc::new(crate::services::Services {
project_root: project_root.to_path_buf(),
agents: Arc::clone(&agents),
bot_name: "Agent".to_string(),
bot_user_id: String::new(),
ambient_rooms: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
perm_rx: Arc::new(tokio::sync::Mutex::new(perm_rx)),
pending_perm_replies: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
permission_timeout_secs: 120,
status: agents.status_broadcaster(),
});
crate::http::context::AppContext {
state: Arc::new(state),
store,
workflow: Arc::new(std::sync::Mutex::new(
crate::workflow::WorkflowState::default(),
)),
services,
watcher_tx,
reconciliation_tx,
perm_tx,
qa_app_process: Arc::new(std::sync::Mutex::new(None)),
bot_shutdown: None,
matrix_shutdown_tx: None,
timer_store,
2026-05-14 16:26:49 +00:00
scheduled_timer_store,
2026-05-14 17:00:33 +00:00
event_trigger_store: Arc::new(
crate::service::event_triggers::store::EventTriggerStore::load(
project_root.join(".huskies").join("event_triggers.json"),
),
),
2026-04-28 18:59:10 +00:00
}
}