use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; pub const PROTOCOL_VERSION: &str = "1.0"; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ExecutionSurfaceKind { PrivilegedBrowserPipe, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct BrowserContext { pub page_url: String, pub page_title: String, } impl BrowserContext { pub fn is_empty(&self) -> bool { self.page_url.trim().is_empty() && self.page_title.trim().is_empty() } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ExecutionSurfaceMetadata { pub kind: ExecutionSurfaceKind, pub privileged: bool, pub defines_runtime_identity: bool, pub guard: String, #[serde(default)] pub allowed_domains: Vec, #[serde(default)] pub allowed_actions: Vec, } impl ExecutionSurfaceMetadata { pub fn privileged_browser_pipe(guard: impl Into) -> Self { Self { kind: ExecutionSurfaceKind::PrivilegedBrowserPipe, privileged: true, defines_runtime_identity: false, guard: guard.into(), allowed_domains: Vec::new(), allowed_actions: Vec::new(), } } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum BrowserMessage { Init { version: String, hmac_seed: String, #[serde(default)] capabilities: Vec, }, Connect, Start, Stop, SubmitTask { instruction: String, #[serde(default)] conversation_id: String, #[serde(default)] messages: Vec, #[serde(default)] page_url: String, #[serde(default)] page_title: String, }, Response { seq: u64, success: bool, #[serde(default = "default_object")] data: Value, #[serde(default)] aom_snapshot: Vec, timing: Timing, }, } impl BrowserMessage { pub fn browser_context(&self) -> Option { match self { Self::SubmitTask { page_url, page_title, .. } => { let context = BrowserContext { page_url: page_url.clone(), page_title: page_title.clone(), }; (!context.is_empty()).then_some(context) } _ => None, } } pub fn requested_surface_metadata(&self) -> Option { match self { Self::SubmitTask { .. } => Some(ExecutionSurfaceMetadata::privileged_browser_pipe( "browser_host_and_mac_policy", )), _ => None, } } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ConversationMessage { pub role: String, pub content: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum AgentMessage { InitAck { version: String, agent_id: String, supported_actions: Vec, }, StatusChanged { state: String, }, LogEntry { level: String, message: String, }, TaskComplete { success: bool, summary: String, }, Command { seq: u64, action: Action, params: Value, security: SecurityFields, }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum Action { Click, Type, Navigate, GetText, Eval, GetHtml, WaitForSelector, PageScreenshot, Select, ScrollTo, GetAomSnapshot, StorageSet, StorageGet, ZombieSpawn, ZombieKill, } impl Action { pub fn as_str(&self) -> &'static str { match self { Action::Click => "click", Action::Type => "type", Action::Navigate => "navigate", Action::GetText => "getText", Action::Eval => "eval", Action::GetHtml => "getHtml", Action::WaitForSelector => "waitForSelector", Action::PageScreenshot => "pageScreenshot", Action::Select => "select", Action::ScrollTo => "scrollTo", Action::GetAomSnapshot => "getAomSnapshot", Action::StorageSet => "storageSet", Action::StorageGet => "storageGet", Action::ZombieSpawn => "zombieSpawn", Action::ZombieKill => "zombieKill", } } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SecurityFields { pub expected_domain: String, pub hmac: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Timing { pub queue_ms: u64, pub exec_ms: u64, } pub fn supported_actions() -> Vec { vec![ Action::Click, Action::Type, Action::Navigate, Action::GetText, Action::Eval, Action::GetHtml, Action::WaitForSelector, Action::PageScreenshot, Action::Select, Action::ScrollTo, Action::GetAomSnapshot, Action::StorageSet, Action::StorageGet, Action::ZombieSpawn, Action::ZombieKill, ] } fn default_object() -> Value { json!({}) }