Wire the service/browser runtime onto the websocket-driven execution path and add the new browser/service modules needed for the submit flow and runtime integration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
221 lines
5.4 KiB
Rust
221 lines
5.4 KiB
Rust
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<String>,
|
|
#[serde(default)]
|
|
pub allowed_actions: Vec<String>,
|
|
}
|
|
|
|
impl ExecutionSurfaceMetadata {
|
|
pub fn privileged_browser_pipe(guard: impl Into<String>) -> 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<String>,
|
|
},
|
|
Connect,
|
|
Start,
|
|
Stop,
|
|
SubmitTask {
|
|
instruction: String,
|
|
#[serde(default)]
|
|
conversation_id: String,
|
|
#[serde(default)]
|
|
messages: Vec<ConversationMessage>,
|
|
#[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<Value>,
|
|
timing: Timing,
|
|
},
|
|
}
|
|
|
|
impl BrowserMessage {
|
|
pub fn browser_context(&self) -> Option<BrowserContext> {
|
|
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<ExecutionSurfaceMetadata> {
|
|
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<Action>,
|
|
},
|
|
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<Action> {
|
|
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!({})
|
|
}
|