sentienceapi 0.92.3 → 0.94.0
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/README.md +125 -54
- package/dist/agent-runtime.d.ts +190 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/agent-runtime.js +257 -0
- package/dist/agent-runtime.js.map +1 -0
- package/dist/asserts/expect.d.ts +159 -0
- package/dist/asserts/expect.d.ts.map +1 -0
- package/dist/asserts/expect.js +547 -0
- package/dist/asserts/expect.js.map +1 -0
- package/dist/asserts/index.d.ts +58 -0
- package/dist/asserts/index.d.ts.map +1 -0
- package/dist/asserts/index.js +70 -0
- package/dist/asserts/index.js.map +1 -0
- package/dist/asserts/query.d.ts +199 -0
- package/dist/asserts/query.d.ts.map +1 -0
- package/dist/asserts/query.js +288 -0
- package/dist/asserts/query.js.map +1 -0
- package/dist/backends/actions.d.ts +118 -0
- package/dist/backends/actions.d.ts.map +1 -0
- package/dist/backends/actions.js +262 -0
- package/dist/backends/actions.js.map +1 -0
- package/dist/backends/browser-use-adapter.d.ts +131 -0
- package/dist/backends/browser-use-adapter.d.ts.map +1 -0
- package/dist/backends/browser-use-adapter.js +219 -0
- package/dist/backends/browser-use-adapter.js.map +1 -0
- package/dist/backends/cdp-backend.d.ts +66 -0
- package/dist/backends/cdp-backend.d.ts.map +1 -0
- package/dist/backends/cdp-backend.js +273 -0
- package/dist/backends/cdp-backend.js.map +1 -0
- package/dist/backends/index.d.ts +80 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +100 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/protocol.d.ts +156 -0
- package/dist/backends/protocol.d.ts.map +1 -0
- package/dist/backends/protocol.js +16 -0
- package/dist/backends/protocol.js.map +1 -0
- package/dist/backends/sentience-context.d.ts +136 -0
- package/dist/backends/sentience-context.d.ts.map +1 -0
- package/dist/backends/sentience-context.js +354 -0
- package/dist/backends/sentience-context.js.map +1 -0
- package/dist/backends/snapshot.d.ts +180 -0
- package/dist/backends/snapshot.d.ts.map +1 -0
- package/dist/backends/snapshot.js +308 -0
- package/dist/backends/snapshot.js.map +1 -0
- package/dist/canonicalization.d.ts +126 -0
- package/dist/canonicalization.d.ts.map +1 -0
- package/dist/canonicalization.js +161 -0
- package/dist/canonicalization.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -1
- package/dist/index.js.map +1 -1
- package/dist/ordinal.d.ts +90 -0
- package/dist/ordinal.d.ts.map +1 -0
- package/dist/ordinal.js +249 -0
- package/dist/ordinal.js.map +1 -0
- package/dist/snapshot-diff.d.ts +8 -15
- package/dist/snapshot-diff.d.ts.map +1 -1
- package/dist/snapshot-diff.js +38 -43
- package/dist/snapshot-diff.js.map +1 -1
- package/dist/snapshot.js +2 -0
- package/dist/snapshot.js.map +1 -1
- package/dist/tracing/indexer.d.ts.map +1 -1
- package/dist/tracing/indexer.js +3 -46
- package/dist/tracing/indexer.js.map +1 -1
- package/dist/tracing/types.d.ts +19 -0
- package/dist/tracing/types.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/verification.d.ts +177 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +315 -0
- package/dist/verification.js.map +1 -0
- package/package.json +1 -1
- package/src/extension/injected_api.js +9 -2
- package/src/extension/manifest.json +1 -1
- package/src/extension/pkg/sentience_core_bg.wasm +0 -0
- package/src/extension/release.json +48 -47
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ npx playwright install chromium
|
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**For local development:**
|
|
16
|
+
|
|
16
17
|
```bash
|
|
17
18
|
npm install
|
|
18
19
|
npm run build
|
|
@@ -44,7 +45,7 @@ const response = await agent.execute(
|
|
|
44
45
|
console.log(response); // "I found the top result for wireless mouse on Amazon. It's priced at $24.99..."
|
|
45
46
|
|
|
46
47
|
// Follow-up questions maintain context
|
|
47
|
-
const followUp = await agent.chat(
|
|
48
|
+
const followUp = await agent.chat('Add it to cart');
|
|
48
49
|
console.log(followUp);
|
|
49
50
|
|
|
50
51
|
await browser.close();
|
|
@@ -144,7 +145,9 @@ await browser.close();
|
|
|
144
145
|
## 🆕 What's New (2026-01-06)
|
|
145
146
|
|
|
146
147
|
### Human-like Typing
|
|
148
|
+
|
|
147
149
|
Add realistic delays between keystrokes to mimic human typing:
|
|
150
|
+
|
|
148
151
|
```typescript
|
|
149
152
|
// Type instantly (default)
|
|
150
153
|
await typeText(browser, elementId, 'Hello World');
|
|
@@ -154,7 +157,9 @@ await typeText(browser, elementId, 'Hello World', false, 10);
|
|
|
154
157
|
```
|
|
155
158
|
|
|
156
159
|
### Scroll to Element
|
|
160
|
+
|
|
157
161
|
Scroll elements into view with smooth animation:
|
|
162
|
+
|
|
158
163
|
```typescript
|
|
159
164
|
const snap = await snapshot(browser);
|
|
160
165
|
const button = find(snap, 'role=button text~"Submit"');
|
|
@@ -181,7 +186,7 @@ import {
|
|
|
181
186
|
SentienceAgent,
|
|
182
187
|
OpenAIProvider,
|
|
183
188
|
Tracer,
|
|
184
|
-
JsonlTraceSink
|
|
189
|
+
JsonlTraceSink,
|
|
185
190
|
} from 'sentienceapi';
|
|
186
191
|
import { randomUUID } from 'crypto';
|
|
187
192
|
|
|
@@ -235,6 +240,38 @@ Traces are **100% compatible** with Python SDK traces - use the same tools to an
|
|
|
235
240
|
|
|
236
241
|
</details>
|
|
237
242
|
|
|
243
|
+
<details>
|
|
244
|
+
<summary><h2>🔍 Agent Runtime Verification</h2></summary>
|
|
245
|
+
|
|
246
|
+
`AgentRuntime` provides assertion predicates for runtime verification in agent loops, enabling programmatic verification of browser state during execution.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { SentienceBrowser } from 'sentienceapi';
|
|
250
|
+
import { AgentRuntime, urlContains, exists, allOf } from 'sentienceapi';
|
|
251
|
+
import { createTracer } from 'sentienceapi';
|
|
252
|
+
|
|
253
|
+
const browser = new SentienceBrowser();
|
|
254
|
+
await browser.start();
|
|
255
|
+
const tracer = await createTracer({ runId: 'my-run', uploadTrace: false });
|
|
256
|
+
const runtime = new AgentRuntime(browser, browser.getPage(), tracer);
|
|
257
|
+
|
|
258
|
+
// Navigate and take snapshot
|
|
259
|
+
await browser.getPage().goto('https://example.com');
|
|
260
|
+
runtime.beginStep('Verify page');
|
|
261
|
+
await runtime.snapshot();
|
|
262
|
+
|
|
263
|
+
// Run assertions
|
|
264
|
+
runtime.assert(urlContains('example.com'), 'on_correct_domain');
|
|
265
|
+
runtime.assert(exists('role=heading'), 'has_heading');
|
|
266
|
+
runtime.assertDone(exists("text~'Example'"), 'task_complete');
|
|
267
|
+
|
|
268
|
+
console.log(`Task done: ${runtime.isTaskDone}`);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**See example:** [examples/agent-runtime-verification.ts](examples/agent-runtime-verification.ts)
|
|
272
|
+
|
|
273
|
+
</details>
|
|
274
|
+
|
|
238
275
|
---
|
|
239
276
|
|
|
240
277
|
<details>
|
|
@@ -261,14 +298,14 @@ async function main() {
|
|
|
261
298
|
console.log(`Found ${snap.elements.length} elements`);
|
|
262
299
|
|
|
263
300
|
// Find first product in viewport using spatial filtering
|
|
264
|
-
const products = snap.elements
|
|
265
|
-
|
|
301
|
+
const products = snap.elements.filter(
|
|
302
|
+
el =>
|
|
266
303
|
el.role === 'link' &&
|
|
267
304
|
el.visual_cues.is_clickable &&
|
|
268
305
|
el.in_viewport &&
|
|
269
306
|
!el.is_occluded &&
|
|
270
|
-
el.bbox.y < 600
|
|
271
|
-
|
|
307
|
+
el.bbox.y < 600 // First row
|
|
308
|
+
);
|
|
272
309
|
|
|
273
310
|
if (products.length > 0) {
|
|
274
311
|
// Sort by position (left to right, top to bottom)
|
|
@@ -323,12 +360,14 @@ main();
|
|
|
323
360
|
**`snapshot(browser, options?)`** - Capture page state with AI-ranked elements
|
|
324
361
|
|
|
325
362
|
Features:
|
|
363
|
+
|
|
326
364
|
- Returns semantic elements with roles, text, importance scores, and bounding boxes
|
|
327
365
|
- Optional screenshot capture (PNG/JPEG)
|
|
328
366
|
- Optional visual overlay to see what elements are detected
|
|
329
367
|
- TypeScript types for type safety
|
|
330
368
|
|
|
331
369
|
**Example:**
|
|
370
|
+
|
|
332
371
|
```typescript
|
|
333
372
|
const snap = await snapshot(browser, { screenshot: true, show_overlay: true });
|
|
334
373
|
|
|
@@ -353,6 +392,7 @@ for (const element of snap.elements) {
|
|
|
353
392
|
- Powerful query DSL with multiple operators
|
|
354
393
|
|
|
355
394
|
**Query Examples:**
|
|
395
|
+
|
|
356
396
|
```typescript
|
|
357
397
|
// Find by role and text
|
|
358
398
|
const button = find(snap, 'role=button text="Sign in"');
|
|
@@ -393,12 +433,13 @@ All actions return `ActionResult` with success status, timing, and outcome:
|
|
|
393
433
|
const result = await click(browser, element.id);
|
|
394
434
|
|
|
395
435
|
console.log(`Success: ${result.success}`);
|
|
396
|
-
console.log(`Outcome: ${result.outcome}`);
|
|
436
|
+
console.log(`Outcome: ${result.outcome}`); // "navigated", "dom_updated", "error"
|
|
397
437
|
console.log(`Duration: ${result.duration_ms}ms`);
|
|
398
438
|
console.log(`URL changed: ${result.url_changed}`);
|
|
399
439
|
```
|
|
400
440
|
|
|
401
441
|
**Coordinate-based clicking:**
|
|
442
|
+
|
|
402
443
|
```typescript
|
|
403
444
|
import { clickRect } from './src';
|
|
404
445
|
|
|
@@ -416,7 +457,7 @@ if (element) {
|
|
|
416
457
|
x: element.bbox.x,
|
|
417
458
|
y: element.bbox.y,
|
|
418
459
|
w: element.bbox.width,
|
|
419
|
-
h: element.bbox.height
|
|
460
|
+
h: element.bbox.height,
|
|
420
461
|
});
|
|
421
462
|
}
|
|
422
463
|
```
|
|
@@ -430,6 +471,7 @@ if (element) {
|
|
|
430
471
|
- **`expect(browser, selector)`** - Assertion helper with fluent API
|
|
431
472
|
|
|
432
473
|
**Examples:**
|
|
474
|
+
|
|
433
475
|
```typescript
|
|
434
476
|
// Wait for element (auto-detects optimal interval based on API usage)
|
|
435
477
|
const result = await waitFor(browser, 'role=button text="Submit"', 10000);
|
|
@@ -447,9 +489,9 @@ const result = await waitFor(browser, 'role=button', 5000, undefined, true);
|
|
|
447
489
|
const result = await waitFor(browser, 'role=button', 5000, 500, false);
|
|
448
490
|
|
|
449
491
|
// Semantic wait conditions
|
|
450
|
-
await waitFor(browser, 'clickable=true', 5000);
|
|
451
|
-
await waitFor(browser, 'importance>100', 5000);
|
|
452
|
-
await waitFor(browser, 'role=link visible=true', 5000);
|
|
492
|
+
await waitFor(browser, 'clickable=true', 5000); // Wait for clickable element
|
|
493
|
+
await waitFor(browser, 'importance>100', 5000); // Wait for important element
|
|
494
|
+
await waitFor(browser, 'role=link visible=true', 5000); // Wait for visible link
|
|
453
495
|
|
|
454
496
|
// Assertions
|
|
455
497
|
await expect(browser, 'role=button text="Submit"').toExist(5000);
|
|
@@ -475,7 +517,7 @@ import { showOverlay, clearOverlay } from 'sentienceapi';
|
|
|
475
517
|
const snap = await snapshot(browser);
|
|
476
518
|
|
|
477
519
|
// Show overlay anytime without re-snapshotting
|
|
478
|
-
await showOverlay(browser, snap);
|
|
520
|
+
await showOverlay(browser, snap); // Auto-clears after 5 seconds
|
|
479
521
|
|
|
480
522
|
// Highlight specific target element in red
|
|
481
523
|
const button = find(snap, 'role=button text~"Submit"');
|
|
@@ -487,11 +529,13 @@ await clearOverlay(browser);
|
|
|
487
529
|
```
|
|
488
530
|
|
|
489
531
|
**Color Coding:**
|
|
532
|
+
|
|
490
533
|
- 🔴 Red: Target element
|
|
491
534
|
- 🔵 Blue: Primary elements (`is_primary=true`)
|
|
492
535
|
- 🟢 Green: Regular interactive elements
|
|
493
536
|
|
|
494
537
|
**Visual Indicators:**
|
|
538
|
+
|
|
495
539
|
- Border thickness/opacity scales with importance
|
|
496
540
|
- Semi-transparent fill
|
|
497
541
|
- Importance badges
|
|
@@ -504,21 +548,23 @@ await clearOverlay(browser);
|
|
|
504
548
|
<summary><h3>📄 Content Reading</h3></summary>
|
|
505
549
|
|
|
506
550
|
**`read(browser, options?)`** - Extract page content
|
|
551
|
+
|
|
507
552
|
- `format: "text"` - Plain text extraction
|
|
508
553
|
- `format: "markdown"` - High-quality markdown conversion (uses Turndown)
|
|
509
554
|
- `format: "raw"` - Cleaned HTML (default)
|
|
510
555
|
|
|
511
556
|
**Example:**
|
|
557
|
+
|
|
512
558
|
```typescript
|
|
513
559
|
import { read } from './src';
|
|
514
560
|
|
|
515
561
|
// Get markdown content
|
|
516
562
|
const result = await read(browser, { format: 'markdown' });
|
|
517
|
-
console.log(result.content);
|
|
563
|
+
console.log(result.content); // Markdown text
|
|
518
564
|
|
|
519
565
|
// Get plain text
|
|
520
566
|
const result = await read(browser, { format: 'text' });
|
|
521
|
-
console.log(result.content);
|
|
567
|
+
console.log(result.content); // Plain text
|
|
522
568
|
```
|
|
523
569
|
|
|
524
570
|
</details>
|
|
@@ -527,11 +573,13 @@ console.log(result.content); // Plain text
|
|
|
527
573
|
<summary><h3>📷 Screenshots</h3></summary>
|
|
528
574
|
|
|
529
575
|
**`screenshot(browser, options?)`** - Standalone screenshot capture
|
|
576
|
+
|
|
530
577
|
- Returns base64-encoded data URL
|
|
531
578
|
- PNG or JPEG format
|
|
532
579
|
- Quality control for JPEG (1-100)
|
|
533
580
|
|
|
534
581
|
**Example:**
|
|
582
|
+
|
|
535
583
|
```typescript
|
|
536
584
|
import { screenshot } from './src';
|
|
537
585
|
import { writeFileSync } from 'fs';
|
|
@@ -558,6 +606,7 @@ const dataUrl = await screenshot(browser, { format: 'jpeg', quality: 85 });
|
|
|
558
606
|
Find buttons, links, or any UI elements by their visible text without needing element IDs or CSS selectors. Returns exact pixel coordinates for each match.
|
|
559
607
|
|
|
560
608
|
**Example:**
|
|
609
|
+
|
|
561
610
|
```typescript
|
|
562
611
|
import { SentienceBrowser, findTextRect, clickRect } from 'sentienceapi';
|
|
563
612
|
|
|
@@ -565,8 +614,8 @@ const browser = await SentienceBrowser.create();
|
|
|
565
614
|
await browser.getPage().goto('https://example.com');
|
|
566
615
|
|
|
567
616
|
// Find "Sign In" button (simple string syntax)
|
|
568
|
-
const result = await findTextRect(browser.getPage(),
|
|
569
|
-
if (result.status ===
|
|
617
|
+
const result = await findTextRect(browser.getPage(), 'Sign In');
|
|
618
|
+
if (result.status === 'success' && result.results) {
|
|
570
619
|
const firstMatch = result.results[0];
|
|
571
620
|
console.log(`Found at: (${firstMatch.rect.x}, ${firstMatch.rect.y})`);
|
|
572
621
|
console.log(`In viewport: ${firstMatch.in_viewport}`);
|
|
@@ -577,30 +626,31 @@ if (result.status === "success" && result.results) {
|
|
|
577
626
|
x: firstMatch.rect.x,
|
|
578
627
|
y: firstMatch.rect.y,
|
|
579
628
|
w: firstMatch.rect.width,
|
|
580
|
-
h: firstMatch.rect.height
|
|
629
|
+
h: firstMatch.rect.height,
|
|
581
630
|
});
|
|
582
631
|
}
|
|
583
632
|
}
|
|
584
633
|
```
|
|
585
634
|
|
|
586
635
|
**Advanced Options:**
|
|
636
|
+
|
|
587
637
|
```typescript
|
|
588
638
|
// Case-sensitive search
|
|
589
639
|
const result = await findTextRect(browser.getPage(), {
|
|
590
|
-
text:
|
|
591
|
-
caseSensitive: true
|
|
640
|
+
text: 'LOGIN',
|
|
641
|
+
caseSensitive: true,
|
|
592
642
|
});
|
|
593
643
|
|
|
594
644
|
// Whole word only (won't match "login" as part of "loginButton")
|
|
595
645
|
const result = await findTextRect(browser.getPage(), {
|
|
596
|
-
text:
|
|
597
|
-
wholeWord: true
|
|
646
|
+
text: 'log',
|
|
647
|
+
wholeWord: true,
|
|
598
648
|
});
|
|
599
649
|
|
|
600
650
|
// Find multiple matches
|
|
601
651
|
const result = await findTextRect(browser.getPage(), {
|
|
602
|
-
text:
|
|
603
|
-
maxResults: 10
|
|
652
|
+
text: 'Buy',
|
|
653
|
+
maxResults: 10,
|
|
604
654
|
});
|
|
605
655
|
for (const match of result.results || []) {
|
|
606
656
|
if (match.in_viewport) {
|
|
@@ -611,6 +661,7 @@ for (const match of result.results || []) {
|
|
|
611
661
|
```
|
|
612
662
|
|
|
613
663
|
**Returns:** Promise<TextRectSearchResult> with:
|
|
664
|
+
|
|
614
665
|
- **`status`**: "success" or "error"
|
|
615
666
|
- **`results`**: Array of `TextMatch` objects with:
|
|
616
667
|
- `text` - The matched text
|
|
@@ -620,6 +671,7 @@ for (const match of result.results || []) {
|
|
|
620
671
|
- `in_viewport` - Whether visible in current viewport
|
|
621
672
|
|
|
622
673
|
**Use Cases:**
|
|
674
|
+
|
|
623
675
|
- Find buttons/links by visible text without CSS selectors
|
|
624
676
|
- Get exact pixel coordinates for click automation
|
|
625
677
|
- Verify text visibility and position on page
|
|
@@ -641,15 +693,15 @@ for (const match of result.results || []) {
|
|
|
641
693
|
Elements returned by `snapshot()` have the following properties:
|
|
642
694
|
|
|
643
695
|
```typescript
|
|
644
|
-
element.id
|
|
645
|
-
element.role
|
|
646
|
-
element.text
|
|
647
|
-
element.importance
|
|
648
|
-
element.bbox
|
|
649
|
-
element.visual_cues
|
|
650
|
-
element.in_viewport
|
|
651
|
-
element.is_occluded
|
|
652
|
-
element.z_index
|
|
696
|
+
element.id; // Unique identifier for interactions
|
|
697
|
+
element.role; // ARIA role (button, link, textbox, heading, etc.)
|
|
698
|
+
element.text; // Visible text content
|
|
699
|
+
element.importance; // AI importance score (0-1000)
|
|
700
|
+
element.bbox; // Bounding box (x, y, width, height)
|
|
701
|
+
element.visual_cues; // Visual analysis (is_primary, is_clickable, background_color)
|
|
702
|
+
element.in_viewport; // Is element visible in current viewport?
|
|
703
|
+
element.is_occluded; // Is element covered by other elements?
|
|
704
|
+
element.z_index; // CSS stacking order
|
|
653
705
|
```
|
|
654
706
|
|
|
655
707
|
</details>
|
|
@@ -659,15 +711,15 @@ element.z_index // CSS stacking order
|
|
|
659
711
|
|
|
660
712
|
### Basic Operators
|
|
661
713
|
|
|
662
|
-
| Operator
|
|
663
|
-
|
|
664
|
-
| `=`
|
|
665
|
-
| `!=`
|
|
666
|
-
| `~`
|
|
667
|
-
| `^=`
|
|
668
|
-
| `$=`
|
|
669
|
-
| `>`, `>=` | Greater than
|
|
670
|
-
| `<`, `<=` | Less than
|
|
714
|
+
| Operator | Description | Example |
|
|
715
|
+
| --------- | ---------------------------- | ---------------- |
|
|
716
|
+
| `=` | Exact match | `role=button` |
|
|
717
|
+
| `!=` | Exclusion | `role!=link` |
|
|
718
|
+
| `~` | Substring (case-insensitive) | `text~"sign in"` |
|
|
719
|
+
| `^=` | Prefix match | `text^="Add"` |
|
|
720
|
+
| `$=` | Suffix match | `text$="Cart"` |
|
|
721
|
+
| `>`, `>=` | Greater than | `importance>500` |
|
|
722
|
+
| `<`, `<=` | Less than | `bbox.y<600` |
|
|
671
723
|
|
|
672
724
|
### Supported Fields
|
|
673
725
|
|
|
@@ -712,7 +764,7 @@ const browser = new SentienceBrowser(undefined, undefined, false);
|
|
|
712
764
|
const browser = new SentienceBrowser(undefined, undefined, true);
|
|
713
765
|
|
|
714
766
|
// Auto-detect based on environment (default)
|
|
715
|
-
const browser = new SentienceBrowser();
|
|
767
|
+
const browser = new SentienceBrowser(); // headless=true if CI=true, else false
|
|
716
768
|
```
|
|
717
769
|
|
|
718
770
|
</details>
|
|
@@ -723,6 +775,7 @@ const browser = new SentienceBrowser(); // headless=true if CI=true, else false
|
|
|
723
775
|
For users running from datacenters (AWS, DigitalOcean, etc.), you can configure a residential proxy to prevent IP-based detection by Cloudflare, Akamai, and other anti-bot services.
|
|
724
776
|
|
|
725
777
|
**Supported Formats:**
|
|
778
|
+
|
|
726
779
|
- HTTP: `http://username:password@host:port`
|
|
727
780
|
- HTTPS: `https://username:password@host:port`
|
|
728
781
|
- SOCKS5: `socks5://username:password@host:port`
|
|
@@ -787,9 +840,9 @@ await saveStorageState(browser.getContext(), 'auth.json');
|
|
|
787
840
|
const browser2 = new SentienceBrowser(
|
|
788
841
|
undefined, // apiKey
|
|
789
842
|
undefined, // apiUrl
|
|
790
|
-
false,
|
|
791
|
-
undefined,
|
|
792
|
-
undefined,
|
|
843
|
+
false, // headless
|
|
844
|
+
undefined, // proxy
|
|
845
|
+
undefined, // userDataDir
|
|
793
846
|
'auth.json' // storageState - inject saved session
|
|
794
847
|
);
|
|
795
848
|
await browser2.start();
|
|
@@ -797,12 +850,12 @@ await browser2.start();
|
|
|
797
850
|
|
|
798
851
|
// Workflow 2: Persistent sessions (cookies persist across runs)
|
|
799
852
|
const browser3 = new SentienceBrowser(
|
|
800
|
-
undefined,
|
|
801
|
-
undefined,
|
|
802
|
-
false,
|
|
803
|
-
undefined,
|
|
853
|
+
undefined, // apiKey
|
|
854
|
+
undefined, // apiUrl
|
|
855
|
+
false, // headless
|
|
856
|
+
undefined, // proxy
|
|
804
857
|
'./chrome_profile', // userDataDir - persist cookies
|
|
805
|
-
undefined
|
|
858
|
+
undefined // storageState
|
|
806
859
|
);
|
|
807
860
|
await browser3.start();
|
|
808
861
|
// First run: Log in
|
|
@@ -810,6 +863,7 @@ await browser3.start();
|
|
|
810
863
|
```
|
|
811
864
|
|
|
812
865
|
**Benefits:**
|
|
866
|
+
|
|
813
867
|
- Bypass login screens and CAPTCHAs with valid sessions
|
|
814
868
|
- Save 5-10 agent steps and hundreds of tokens per run
|
|
815
869
|
- Maintain stateful sessions for accessing authenticated pages
|
|
@@ -827,13 +881,15 @@ See `examples/auth-injection-agent.ts` for complete examples.
|
|
|
827
881
|
<summary>Click to expand best practices</summary>
|
|
828
882
|
|
|
829
883
|
### 1. Wait for Dynamic Content
|
|
884
|
+
|
|
830
885
|
```typescript
|
|
831
886
|
await browser.goto('https://example.com');
|
|
832
887
|
await browser.getPage().waitForLoadState('networkidle');
|
|
833
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
888
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Extra buffer
|
|
834
889
|
```
|
|
835
890
|
|
|
836
891
|
### 2. Use Multiple Strategies for Finding Elements
|
|
892
|
+
|
|
837
893
|
```typescript
|
|
838
894
|
// Try exact match first
|
|
839
895
|
let btn = find(snap, 'role=button text="Add to Cart"');
|
|
@@ -845,6 +901,7 @@ if (!btn) {
|
|
|
845
901
|
```
|
|
846
902
|
|
|
847
903
|
### 3. Check Element Visibility Before Clicking
|
|
904
|
+
|
|
848
905
|
```typescript
|
|
849
906
|
if (element.in_viewport && !element.is_occluded) {
|
|
850
907
|
await click(browser, element.id);
|
|
@@ -852,6 +909,7 @@ if (element.in_viewport && !element.is_occluded) {
|
|
|
852
909
|
```
|
|
853
910
|
|
|
854
911
|
### 4. Handle Navigation
|
|
912
|
+
|
|
855
913
|
```typescript
|
|
856
914
|
const result = await click(browser, linkId);
|
|
857
915
|
if (result.url_changed) {
|
|
@@ -860,6 +918,7 @@ if (result.url_changed) {
|
|
|
860
918
|
```
|
|
861
919
|
|
|
862
920
|
### 5. Use Screenshots Sparingly
|
|
921
|
+
|
|
863
922
|
```typescript
|
|
864
923
|
// Fast - no screenshot (only element data)
|
|
865
924
|
const snap = await snapshot(browser);
|
|
@@ -869,6 +928,7 @@ const snap = await snapshot(browser, { screenshot: true });
|
|
|
869
928
|
```
|
|
870
929
|
|
|
871
930
|
### 6. Always Close Browser
|
|
931
|
+
|
|
872
932
|
```typescript
|
|
873
933
|
const browser = new SentienceBrowser();
|
|
874
934
|
|
|
@@ -876,7 +936,7 @@ try {
|
|
|
876
936
|
await browser.start();
|
|
877
937
|
// ... your automation code
|
|
878
938
|
} finally {
|
|
879
|
-
await browser.close();
|
|
939
|
+
await browser.close(); // Always clean up
|
|
880
940
|
}
|
|
881
941
|
```
|
|
882
942
|
|
|
@@ -890,14 +950,18 @@ try {
|
|
|
890
950
|
<summary>Click to expand common issues and solutions</summary>
|
|
891
951
|
|
|
892
952
|
### "Extension failed to load"
|
|
953
|
+
|
|
893
954
|
**Solution:** Build the extension first:
|
|
955
|
+
|
|
894
956
|
```bash
|
|
895
957
|
cd sentience-chrome
|
|
896
958
|
./build.sh
|
|
897
959
|
```
|
|
898
960
|
|
|
899
961
|
### "Cannot use import statement outside a module"
|
|
962
|
+
|
|
900
963
|
**Solution:** Don't use `node` directly. Use `ts-node` or npm scripts:
|
|
964
|
+
|
|
901
965
|
```bash
|
|
902
966
|
npx ts-node examples/hello.ts
|
|
903
967
|
# or
|
|
@@ -905,13 +969,17 @@ npm run example:hello
|
|
|
905
969
|
```
|
|
906
970
|
|
|
907
971
|
### "Element not found"
|
|
972
|
+
|
|
908
973
|
**Solutions:**
|
|
974
|
+
|
|
909
975
|
- Ensure page is loaded: `await browser.getPage().waitForLoadState('networkidle')`
|
|
910
976
|
- Use `waitFor()`: `await waitFor(browser, 'role=button', 10000)`
|
|
911
977
|
- Debug elements: `console.log(snap.elements.map(el => el.text))`
|
|
912
978
|
|
|
913
979
|
### Button not clickable
|
|
980
|
+
|
|
914
981
|
**Solutions:**
|
|
982
|
+
|
|
915
983
|
- Check visibility: `element.in_viewport && !element.is_occluded`
|
|
916
984
|
- Scroll to element: ``await browser.getPage().evaluate(`window.sentience_registry[${element.id}].scrollIntoView()`)``
|
|
917
985
|
|
|
@@ -948,6 +1016,7 @@ npm run example:hello
|
|
|
948
1016
|
**⚠️ Important**: You cannot use `node` directly to run TypeScript files. Use one of these methods:
|
|
949
1017
|
|
|
950
1018
|
### Option 1: Using npm scripts (recommended)
|
|
1019
|
+
|
|
951
1020
|
```bash
|
|
952
1021
|
npm run example:hello
|
|
953
1022
|
npm run example:basic
|
|
@@ -956,6 +1025,7 @@ npm run example:wait
|
|
|
956
1025
|
```
|
|
957
1026
|
|
|
958
1027
|
### Option 2: Using ts-node directly
|
|
1028
|
+
|
|
959
1029
|
```bash
|
|
960
1030
|
npx ts-node examples/hello.ts
|
|
961
1031
|
# or if ts-node is installed globally:
|
|
@@ -963,6 +1033,7 @@ ts-node examples/hello.ts
|
|
|
963
1033
|
```
|
|
964
1034
|
|
|
965
1035
|
### Option 3: Compile then run
|
|
1036
|
+
|
|
966
1037
|
```bash
|
|
967
1038
|
npm run build
|
|
968
1039
|
# Then use compiled JavaScript from dist/
|
|
@@ -1001,7 +1072,7 @@ npm test -- snapshot.test.ts
|
|
|
1001
1072
|
|
|
1002
1073
|
This project is licensed under either of:
|
|
1003
1074
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1075
|
+
- Apache License, Version 2.0, ([LICENSE-APACHE](./LICENSE-APACHE))
|
|
1076
|
+
- MIT license ([LICENSE-MIT](./LICENSE-MIT))
|
|
1006
1077
|
|
|
1007
1078
|
at your option.
|