surfagent 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md CHANGED
@@ -226,6 +226,26 @@ Get clean, structured readable content from a page. Use this instead of screensh
226
226
 
227
227
  ---
228
228
 
229
+ ### POST /dismiss
230
+
231
+ Dismiss cookie banners, consent dialogs, and modal overlays. Supports 15+ language patterns (English, Norwegian, German, French, Spanish, Italian, Portuguese).
232
+
233
+ ```json
234
+ { "tab": "0" }
235
+ ```
236
+
237
+ Response:
238
+ ```json
239
+ { "dismissed": [{ "type": "cookie", "text": "reject all" }], "count": 1 }
240
+ ```
241
+
242
+ If nothing was found to dismiss:
243
+ ```json
244
+ { "dismissed": [], "count": 0 }
245
+ ```
246
+
247
+ ---
248
+
229
249
  ### POST /focus
230
250
 
231
251
  Bring a tab to the front in Chrome. Use this when a tab is behind other tabs or windows.
@@ -250,11 +270,18 @@ Click an element on a page.
250
270
  { "tab": "0", "selector": "#submit-btn" }
251
271
  ```
252
272
 
253
- **By text** (fuzzy match against visible text):
273
+ **By text** (fuzzy match against visible text, including dropdown options):
254
274
  ```json
255
275
  { "tab": "0", "text": "Submit" }
256
276
  ```
257
277
 
278
+ **With wait** (wait for page to settle after click — useful for SPA navigation):
279
+ ```json
280
+ { "tab": "0", "text": "Search", "waitAfter": 2000 }
281
+ ```
282
+
283
+ Text search matches: buttons, links, `role="option"`, `role="menuitem"`, `role="listitem"`, `li[aria-label]`, and elements with `onclick`. This means autocomplete dropdown items are clickable by text without needing `/eval`.
284
+
258
285
  **Response:**
259
286
  ```json
260
287
  { "success": true, "clicked": "BUTTON: Submit" }
package/README.md CHANGED
@@ -68,7 +68,8 @@ curl -X POST localhost:3456/read -H 'Content-Type: application/json' \
68
68
  | `/recon` | POST | Full page map — every element, form, selector, heading, nav link, metadata, captcha detection |
69
69
  | `/read` | POST | Structured page content — headings, tables, code blocks, notifications, result areas |
70
70
  | `/fill` | POST | Fill form fields with real CDP keystrokes (works with React, Vue, SPAs) |
71
- | `/click` | POST | Click by CSS selector or text match (handles `target="_blank"` automatically) |
71
+ | `/click` | POST | Click by selector or text, including dropdown options. Optional `waitAfter` for SPAs |
72
+ | `/dismiss` | POST | Auto-dismiss cookie banners, consent dialogs, modals (multi-language) |
72
73
  | `/scroll` | POST | Scroll page, returns visible content preview and scroll position |
73
74
  | `/navigate` | POST | Go to URL, back, or forward in the same tab |
74
75
  | `/eval` | POST | Run JavaScript in any tab or cross-origin iframe |
package/dist/api/act.js CHANGED
@@ -78,8 +78,11 @@ export async function fillFields(request, options) {
78
78
  if (actual === field.value) {
79
79
  results.push({ selector: field.selector, success: true });
80
80
  }
81
+ else if (actual === undefined || actual === null) {
82
+ results.push({ selector: field.selector, success: false, error: `Element not found or has no value: ${field.selector}` });
83
+ }
81
84
  else {
82
- results.push({ selector: field.selector, success: true, error: `Value mismatch: got "${actual}"` });
85
+ results.push({ selector: field.selector, success: false, error: `Value mismatch: expected "${field.value}", got "${actual}"` });
83
86
  }
84
87
  }
85
88
  catch (error) {
@@ -256,12 +259,14 @@ export async function evalInTab(tab, expression, options) {
256
259
  const resolved = await resolveTab(tab, port, host);
257
260
  const client = await connectToTab(resolved.id, port, host);
258
261
  try {
259
- const result = await client.Runtime.evaluate({
262
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Eval timed out after 30s')), 30000));
263
+ const evalPromise = client.Runtime.evaluate({
260
264
  expression,
261
265
  returnByValue: true
262
266
  });
267
+ const result = await Promise.race([evalPromise, timeout]);
263
268
  await client.close();
264
- return result.result.value;
269
+ return result.result.value ?? null;
265
270
  }
266
271
  catch (error) {
267
272
  await client.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "surfagent",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Browser automation API for AI agents — structured page recon, form filling, clicking, and navigation via Chrome CDP",
5
5
  "keywords": [
6
6
  "ai-agent",
package/src/api/act.ts CHANGED
@@ -104,8 +104,10 @@ export async function fillFields(
104
104
  const actual = verify.result.value as string;
105
105
  if (actual === field.value) {
106
106
  results.push({ selector: field.selector, success: true });
107
+ } else if (actual === undefined || actual === null) {
108
+ results.push({ selector: field.selector, success: false, error: `Element not found or has no value: ${field.selector}` });
107
109
  } else {
108
- results.push({ selector: field.selector, success: true, error: `Value mismatch: got "${actual}"` });
110
+ results.push({ selector: field.selector, success: false, error: `Value mismatch: expected "${field.value}", got "${actual}"` });
109
111
  }
110
112
  } catch (error) {
111
113
  results.push({ selector: field.selector, success: false, error: (error as Error).message });
@@ -329,12 +331,16 @@ export async function evalInTab(
329
331
  const client = await connectToTab(resolved.id, port, host);
330
332
 
331
333
  try {
332
- const result = await client.Runtime.evaluate({
334
+ const timeout = new Promise<never>((_, reject) =>
335
+ setTimeout(() => reject(new Error('Eval timed out after 30s')), 30000)
336
+ );
337
+ const evalPromise = client.Runtime.evaluate({
333
338
  expression,
334
339
  returnByValue: true
335
340
  });
341
+ const result = await Promise.race([evalPromise, timeout]);
336
342
  await client.close();
337
- return result.result.value;
343
+ return result.result.value ?? null;
338
344
  } catch (error) {
339
345
  await client.close();
340
346
  throw error;