agentmux_cef\browser_api/
types.rs

1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Request/response types for the browser DOM API.
5
6use serde::{Deserialize, Serialize};
7
8// ── Generic response envelope ───────────────────────────────────────────
9
10/// Every `/agentmux/browser/*` response is one of these two shapes.
11/// Matches `SPEC_BROWSER_DOM_API.md` §5.1.
12#[derive(Debug, Serialize)]
13#[serde(untagged)]
14pub enum ApiResponse<T> {
15    Ok { ok: bool, data: T },
16    Err { ok: bool, error: String },
17}
18
19impl<T> ApiResponse<T> {
20    pub fn ok(data: T) -> Self {
21        ApiResponse::Ok { ok: true, data }
22    }
23    pub fn err(msg: impl Into<String>) -> Self {
24        ApiResponse::Err {
25            ok: false,
26            error: msg.into(),
27        }
28    }
29}
30
31// ── browser.query ───────────────────────────────────────────────────────
32
33#[derive(Debug, Deserialize)]
34pub struct QueryReq {
35    pub block_id: String,
36    pub selector: String,
37    #[serde(default)]
38    pub limit: Option<u32>,
39}
40
41#[derive(Debug, Serialize)]
42pub struct QueryData {
43    pub matches: Vec<Element>,
44}
45
46#[derive(Debug, Serialize, Deserialize)]
47pub struct Element {
48    /// Unique CSS path the backend-injected helper computed for this node.
49    /// Stable enough to target the same element in a follow-up call,
50    /// provided the DOM hasn't been restructured in between.
51    pub selector: String,
52    pub tag: String,
53    /// First ~500 chars of textContent — full text would balloon responses.
54    pub text: String,
55    pub attrs: serde_json::Map<String, serde_json::Value>,
56    pub rect: Rect,
57    pub focused: bool,
58}
59
60#[derive(Debug, Serialize, Deserialize)]
61pub struct Rect {
62    pub x: f64,
63    pub y: f64,
64    pub width: f64,
65    pub height: f64,
66}
67
68// ── browser.focus_info ──────────────────────────────────────────────────
69
70#[derive(Debug, Deserialize)]
71pub struct FocusInfoReq {
72    pub block_id: String,
73}
74
75#[derive(Debug, Serialize)]
76pub struct FocusInfoData {
77    /// null when `document.activeElement` is null or the `<body>`
78    /// (the default resting state with no focused control).
79    pub focused: Option<Element>,
80}
81
82// ── browser.eval ────────────────────────────────────────────────────────
83
84#[derive(Debug, Deserialize)]
85pub struct EvalReq {
86    pub block_id: String,
87    pub script: String,
88    /// If true and the script returns a Promise, wait for it to
89    /// resolve before returning. Maps to CDP `Runtime.evaluate`
90    /// `awaitPromise`.
91    #[serde(default)]
92    pub await_promise: bool,
93}
94
95#[derive(Debug, Serialize)]
96pub struct EvalData {
97    /// The serialized JS return value, whatever shape the script
98    /// produced. `null` if the script returned `undefined` or threw.
99    pub result: serde_json::Value,
100    /// CDP's type tag: "object" | "string" | "number" | "boolean" |
101    /// "undefined" | "function" | "symbol" | "bigint". Kept so
102    /// callers can distinguish `null`-the-value from `null`-the-failure.
103    #[serde(rename = "type")]
104    pub type_: String,
105    /// Populated when the script threw. The message + stack, when
106    /// available. `result` is null in this case.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub exception: Option<String>,
109}
110
111// ── browser.screenshot ──────────────────────────────────────────────────
112
113#[derive(Debug, Deserialize)]
114pub struct ScreenshotReq {
115    pub block_id: String,
116}
117
118#[derive(Debug, Serialize)]
119pub struct ScreenshotData {
120    /// Base64-encoded PNG bytes — same format CDP's
121    /// `Page.captureScreenshot` returns.
122    pub png_base64: String,
123}
124
125// ── browser.click_element ───────────────────────────────────────────────
126
127#[derive(Debug, Deserialize)]
128pub struct ClickElementReq {
129    pub block_id: String,
130    pub selector: String,
131}
132
133// ── browser.focus_element ───────────────────────────────────────────────
134
135#[derive(Debug, Deserialize)]
136pub struct FocusElementReq {
137    pub block_id: String,
138    pub selector: String,
139}
140
141// ── browser.dispatch_key ────────────────────────────────────────────────
142
143#[derive(Debug, Deserialize)]
144pub struct DispatchKeyReq {
145    pub block_id: String,
146    /// Optional CSS selector: focus this element before dispatching
147    /// the key event(s). If absent, the key lands on whatever has
148    /// focus currently.
149    #[serde(default)]
150    pub selector: Option<String>,
151    /// Send this text as `Input.insertText` (atomic, preserves IME
152    /// and autocomplete behaviour). Mutually exclusive with `key`.
153    #[serde(default)]
154    pub text: Option<String>,
155    /// Send this named key as a `keyDown`+`keyUp` pair. Supported:
156    /// `Enter`, `Tab`, `Escape`, `Backspace`, `ArrowUp`, `ArrowDown`,
157    /// `ArrowLeft`, `ArrowRight`, `Space`. Unknown keys → error.
158    #[serde(default)]
159    pub key: Option<String>,
160}
161
162// ── browser.navigate ────────────────────────────────────────────────────
163
164#[derive(Debug, Deserialize)]
165pub struct NavigateReq {
166    pub block_id: String,
167    pub url: String,
168}
169
170// ── browser.back / .forward / .reload ───────────────────────────────────
171//
172// Share a single request shape — all three only need the target block id.
173// These exist so agents driving a browser pane during dev / tests can walk
174// its history without a human clicking the toolbar. Also useful for the
175// agent workflow where a tool says "open this URL, try to click X, if not
176// found go back and try Y."
177
178#[derive(Debug, Deserialize)]
179pub struct HistoryReq {
180    pub block_id: String,
181    /// Reload only: skip the http cache and force a network refetch.
182    /// Ignored by `back` / `forward`. Defaults to false.
183    #[serde(default)]
184    pub ignore_cache: bool,
185}
186
187// ── Generic "ok:true" success body for write endpoints ──────────────────
188
189#[derive(Debug, Serialize)]
190pub struct AckData {
191    pub ok: bool,
192}
193
194impl AckData {
195    pub fn new() -> Self {
196        Self { ok: true }
197    }
198}