1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
5pub struct CommandPoint {
6 pub x: f64,
7 pub y: f64,
8}
9
10#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
11#[serde(rename_all = "snake_case")]
12pub enum CommandType {
13 Action,
14 Extraction,
15}
16
17#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
18#[serde(rename_all = "snake_case")]
19pub enum PageContentKind {
20 Markdown,
21 Html,
22 Json,
23}
24
25impl Default for PageContentKind {
26 fn default() -> Self {
27 PageContentKind::Markdown
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
32#[serde(tag = "command", rename_all = "snake_case", content = "data")]
33pub enum Commands {
34 ToggleClickOverlay {
35 enabled: bool,
36 },
37 ToggleBoundingBoxes {
38 enabled: bool,
39 selector: Option<String>,
40 limit: Option<usize>,
41 include_html: Option<bool>,
42 },
43 NavigateTo {
45 url: String,
46 },
47 Refresh,
48 WaitForNavigation {
49 timeout_ms: Option<u64>,
50 },
51 WaitForElement {
52 selector: String,
53 timeout_ms: Option<u64>,
54 visible_only: Option<bool>,
55 },
56
57 Click {
59 selector: String,
60 },
61 ClickAt {
62 x: f64,
63 y: f64,
64 },
65 Clear {
66 selector: String,
67 },
68 PressKey {
69 selector: String,
70 key: String,
71 },
72
73 GetContent {
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 selector: Option<String>,
77 #[serde(default)]
78 kind: Option<PageContentKind>,
79 },
80 GetText {
81 selector: String,
82 },
83 GetAttribute {
84 selector: String,
85 attribute: String,
86 },
87
88 GetTitle,
90 ExtractStructuredContent {
91 query: String,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
94 schema: Option<serde_json::Value>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 max_chars: Option<usize>,
97 },
98
99 Evaluate {
101 expression: String,
102 },
103 GetBoundingBoxes {
104 selector: String,
105 limit: Option<usize>,
106 include_html: Option<bool>,
107 },
108 InspectElement {
109 selector: String,
110 },
111
112 ScrollTo {
117 #[serde(default)]
118 x: Option<f64>,
119 #[serde(default)]
120 y: Option<f64>,
121 },
122 MoveMouseTo {
126 x: f64,
127 y: f64,
128 steps: Option<u32>,
131 },
132 Drag {
133 from: CommandPoint,
134 to: CommandPoint,
135 modifiers: Option<i64>,
136 },
137 ScrollIntoView {
138 selector: String,
139 },
140
141 Screenshot {
143 full_page: Option<bool>,
144 path: Option<String>,
145 },
146
147 ClickAdvanced {
149 selector: String,
150 button: Option<String>,
151 click_count: Option<u8>,
152 modifiers: Option<i64>,
153 },
154 TypeText {
155 selector: String,
156 text: String,
157 clear: Option<bool>,
158 },
159 Hover {
160 selector: String,
161 },
162 Focus {
163 selector: String,
164 },
165 Check {
166 selector: String,
167 },
168 SelectOption {
169 selector: String,
170 values: Vec<String>,
171 },
172 DragTo {
173 selector: String,
174 target_selector: Option<String>,
175 source_position: Option<CommandPoint>,
176 target_position: Option<CommandPoint>,
177 modifiers: Option<i64>,
178 },
179 EvaluateOnElement {
180 selector: String,
181 expression: String,
182 },
183 GetElementBoundingBox {
184 selector: String,
185 },
186 ElementScreenshot {
187 selector: String,
188 format: Option<String>,
189 quality: Option<u8>,
190 },
191 GetBasicInfo {
192 selector: String,
193 },
194
195 SetCookie {
198 name: String,
199 value: String,
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 domain: Option<String>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
203 path: Option<String>,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
205 url: Option<String>,
206 #[serde(default, skip_serializing_if = "Option::is_none")]
207 secure: Option<bool>,
208 #[serde(default, skip_serializing_if = "Option::is_none")]
209 http_only: Option<bool>,
210 #[serde(default, skip_serializing_if = "Option::is_none")]
211 same_site: Option<String>,
212 #[serde(default, skip_serializing_if = "Option::is_none")]
213 expires: Option<f64>,
214 },
215 SetCookies {
217 cookies: String,
219 domain: String,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
222 path: Option<String>,
223 #[serde(default, skip_serializing_if = "Option::is_none")]
224 secure: Option<bool>,
225 #[serde(default, skip_serializing_if = "Option::is_none")]
226 http_only: Option<bool>,
227 },
228 GetCookies,
230 DeleteCookie {
232 name: String,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
234 domain: Option<String>,
235 },
236
237 GetLocalStorage,
240 SetLocalStorage {
242 items: Vec<LocalStorageItem>,
244 },
245 ClearLocalStorage,
247
248 GetSessionStorage,
251 SetSessionStorage {
253 items: Vec<LocalStorageItem>,
255 },
256 ClearSessionStorage,
258
259 CaptureState,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
265pub struct LocalStorageItem {
266 pub key: String,
267 pub value: String,
268}
269
270#[derive(serde::Deserialize)]
271pub struct StepPayload {
272 pub commands: Vec<Commands>,
273 pub thinking: Option<String>,
274 pub evaluation_previous_goal: Option<String>,
275 pub memory: Option<String>,
276 pub next_goal: Option<String>,
277}
278
279impl Commands {
280 pub fn command_type(&self) -> CommandType {
281 match self {
282 Commands::GetContent { .. }
283 | Commands::GetText { .. }
284 | Commands::GetAttribute { .. }
285 | Commands::GetTitle
286 | Commands::ExtractStructuredContent { .. }
287 | Commands::Screenshot { .. }
288 | Commands::ElementScreenshot { .. }
289 | Commands::GetElementBoundingBox { .. }
290 | Commands::Evaluate { .. }
291 | Commands::EvaluateOnElement { .. }
292 | Commands::GetBoundingBoxes { .. }
293 | Commands::InspectElement { .. }
294 | Commands::GetBasicInfo { .. }
295 | Commands::GetCookies
296 | Commands::GetLocalStorage
297 | Commands::GetSessionStorage
298 | Commands::CaptureState => CommandType::Extraction,
299 _ => CommandType::Action,
300 }
301 }
302}