feat: initial commit — antigravity proxy with MITM, standalone LS, and snapshot tooling
This commit is contained in:
69
src/warmup.rs
Normal file
69
src/warmup.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
//! Startup warmup and periodic heartbeat — mimics real webview lifecycle.
|
||||
//!
|
||||
//! The real Electron webview calls these methods on startup and then sends
|
||||
//! Heartbeat every ~30 seconds. Without this, the LS sees a "user" that
|
||||
//! never initializes and never heartbeats — an obvious bot fingerprint.
|
||||
|
||||
use crate::backend::Backend;
|
||||
use rand::Rng;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
/// Run the exact startup sequence the real webview performs on load.
|
||||
///
|
||||
/// Called BEFORE accepting any API requests. Each call is fire-and-forget
|
||||
/// (we don't care if some fail — the LS might not support all methods).
|
||||
pub async fn warmup_sequence(backend: &Backend) {
|
||||
info!("Running webview warmup sequence...");
|
||||
|
||||
let calls: &[(&str, serde_json::Value)] = &[
|
||||
("GetStatus", serde_json::json!({})),
|
||||
("Heartbeat", serde_json::json!({})),
|
||||
("GetUserStatus", serde_json::json!({})),
|
||||
("GetCascadeModelConfigs", serde_json::json!({})),
|
||||
("GetCascadeModelConfigData", serde_json::json!({})),
|
||||
("GetWorkspaceInfos", serde_json::json!({})),
|
||||
("GetWorkingDirectories", serde_json::json!({})),
|
||||
("GetAllCascadeTrajectories", serde_json::json!({})),
|
||||
("GetMcpServerStates", serde_json::json!({})),
|
||||
("GetWebDocsOptions", serde_json::json!({})),
|
||||
("GetRepoInfos", serde_json::json!({})),
|
||||
("GetAllSkills", serde_json::json!({})),
|
||||
("InitializeCascadePanelState", serde_json::json!({})),
|
||||
];
|
||||
|
||||
for (method, body) in calls {
|
||||
match backend.call_json(method, body).await {
|
||||
Ok((status, _)) => debug!("Warmup {method}: {status}"),
|
||||
Err(e) => warn!("Warmup {method} failed: {e}"),
|
||||
}
|
||||
// Small delay between calls — real webview doesn't blast them instantly
|
||||
let delay = rand::thread_rng().gen_range(50..200);
|
||||
tokio::time::sleep(Duration::from_millis(delay)).await;
|
||||
}
|
||||
|
||||
info!("Warmup complete");
|
||||
}
|
||||
|
||||
/// Spawn a background task that sends Heartbeat every ~30s ± jitter.
|
||||
///
|
||||
/// Returns a JoinHandle that runs until the task is aborted (on shutdown).
|
||||
pub fn start_heartbeat(backend: Arc<Backend>) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
// ~30s interval (± 500ms) — matches real setInterval(30000) precision
|
||||
let interval_ms = rand::thread_rng().gen_range(29_500..30_500);
|
||||
tokio::time::sleep(Duration::from_millis(interval_ms)).await;
|
||||
|
||||
match backend
|
||||
.call_json("Heartbeat", &serde_json::json!({}))
|
||||
.await
|
||||
{
|
||||
Ok((status, _)) => debug!("Heartbeat: {status}"),
|
||||
Err(e) => warn!("Heartbeat failed: {e}"),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user