rensan-browser 0.1.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 ADDED
@@ -0,0 +1,133 @@
1
+ # rensan-browser
2
+
3
+ AI-powered browser for agents — navigate, click, type, read, see images, capture requests.
4
+
5
+ Built on [Playwright](https://playwright.dev) + [Claude](https://anthropic.com). Designed for AI agents that need to interact with websites and intercept their API traffic.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install rensan-browser playwright
11
+ npx playwright install chromium
12
+ ```
13
+
14
+ ## Quick start
15
+
16
+ ```typescript
17
+ import { RensanBrowser } from 'rensan-browser'
18
+
19
+ const browser = new RensanBrowser({ apiKey: 'sk-ant-...' })
20
+
21
+ const session = await browser.open('https://example.com')
22
+
23
+ // Read text from the page
24
+ const headline = await session.read('the main headline')
25
+
26
+ // Click anything — AI finds it
27
+ await session.click('the Accept cookies button')
28
+
29
+ // Fill any input — AI finds it
30
+ await session.fill('Buenos Aires', 'the city search input')
31
+
32
+ // Take a screenshot (base64 JPEG)
33
+ const img = await session.screenshot()
34
+
35
+ // Capture JSON requests fired by the page
36
+ const requests = await session.capture(async () => {
37
+ await session.fill('BUE', 'origin airport')
38
+ await session.click('search flights button')
39
+ })
40
+ // requests = [{ url, data, score }, ...]
41
+
42
+ await session.close()
43
+ await browser.close()
44
+ ```
45
+
46
+ ## API
47
+
48
+ ### `new RensanBrowser(config)`
49
+
50
+ | Option | Type | Default | Description |
51
+ |---|---|---|---|
52
+ | `apiKey` | `string` | required | Anthropic API key |
53
+ | `model` | `string` | `claude-haiku-4-5` | Vision model for AI decisions |
54
+ | `headless` | `boolean` | `true` | Run browser headless |
55
+ | `timeout` | `number` | `25000` | Default navigation timeout (ms) |
56
+
57
+ ### Browser methods
58
+
59
+ ```typescript
60
+ browser.open(url, options?) // open session and navigate
61
+ browser.newSession() // open blank session
62
+ browser.openMany(urls[], options?) // multiple sessions in parallel
63
+ browser.run(async (session) => ...) // auto-closing session
64
+ browser.close() // close browser
65
+ ```
66
+
67
+ ### Session methods
68
+
69
+ ```typescript
70
+ // Navigation
71
+ session.goto(url, options?)
72
+
73
+ // Interaction — AI finds the element via vision
74
+ session.click(goal) // 'the login button'
75
+ session.fill(value, goal, options?) // ('BUE', 'origin airport input')
76
+ session.type(text, options?) // type at current focus
77
+ session.moveTo(goal | { x, y }) // move cursor
78
+
79
+ // Reading
80
+ session.read(goal?) // 'the product price' or all text
81
+ session.screenshot() // base64 JPEG
82
+
83
+ // Scrolling
84
+ session.scroll('down' | 'up', options?)
85
+
86
+ // Request capture
87
+ session.capture(async () => { ... }) // returns CapturedRequest[]
88
+ session.startCapture()
89
+ session.stopCapture()
90
+ session.capturedRequests
91
+
92
+ // Lifecycle
93
+ session.wait(ms)
94
+ session.close()
95
+ ```
96
+
97
+ ### Parallel sessions
98
+
99
+ ```typescript
100
+ const [s1, s2, s3] = await browser.openMany([
101
+ 'https://skyscanner.com',
102
+ 'https://kayak.com',
103
+ 'https://despegar.com',
104
+ ])
105
+
106
+ const results = await Promise.all([
107
+ s1.capture(async () => s1.fill('BUE', 'origin')),
108
+ s2.capture(async () => s2.fill('BUE', 'origin')),
109
+ s3.capture(async () => s3.fill('BUE', 'origin')),
110
+ ])
111
+ ```
112
+
113
+ ### Request scoring
114
+
115
+ Captured requests are automatically ranked by relevance — first-party API calls score higher than tracking, ads, and analytics. No hardcoded domain lists.
116
+
117
+ ```typescript
118
+ const requests = await session.capture(async () => {
119
+ await session.goto('https://news-site.com')
120
+ })
121
+ // requests[0] is the most likely real data API
122
+ ```
123
+
124
+ ## How AI interaction works
125
+
126
+ 1. **Layer 1 — Playwright semantic (free)**: tries `getByRole('searchbox')`, `getByRole('textbox')` and common patterns. Works for most standard inputs.
127
+ 2. **Layer 2 — Claude Haiku vision (~$0.001)**: takes a screenshot, sends it to Haiku, gets back a CSS selector. Works for any page layout.
128
+
129
+ Total cost per interaction: ~$0.001 with Haiku.
130
+
131
+ ## License
132
+
133
+ MIT
package/dist/ai.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ai.ts
3
+ * All AI calls for rensan-browser.
4
+ * Haiku sees the page screenshot and decides what to do.
5
+ */
6
+ import type { AIDecision } from './types';
7
+ export declare class AIClient {
8
+ readonly model: string;
9
+ private client;
10
+ constructor(apiKey: string, model: string);
11
+ /**
12
+ * Look at a screenshot and decide what element to interact with.
13
+ * Returns a CSS/semantic selector and action type.
14
+ */
15
+ decide(screenshot: string, goal: string, value?: string): Promise<AIDecision>;
16
+ /**
17
+ * Read and extract specific text from a screenshot.
18
+ */
19
+ read(screenshot: string, goal?: string): Promise<string>;
20
+ }
21
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,qBAAa,QAAQ;IAGS,QAAQ,CAAC,KAAK,EAAE,MAAM;IAFlD,OAAO,CAAC,MAAM,CAAY;gBAEd,MAAM,EAAE,MAAM,EAAW,KAAK,EAAE,MAAM;IAIlD;;;OAGG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA4CnF;;OAEG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAsB/D"}
package/dist/ai.js ADDED
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * ai.ts
4
+ * All AI calls for rensan-browser.
5
+ * Haiku sees the page screenshot and decides what to do.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.AIClient = void 0;
12
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
13
+ class AIClient {
14
+ constructor(apiKey, model) {
15
+ this.model = model;
16
+ this.client = new sdk_1.default({ apiKey });
17
+ }
18
+ /**
19
+ * Look at a screenshot and decide what element to interact with.
20
+ * Returns a CSS/semantic selector and action type.
21
+ */
22
+ async decide(screenshot, goal, value) {
23
+ const valueHint = value ? `Value to type: "${value}"` : '';
24
+ const msg = await this.client.messages.create({
25
+ model: this.model,
26
+ max_tokens: 200,
27
+ messages: [{
28
+ role: 'user',
29
+ content: [
30
+ {
31
+ type: 'image',
32
+ source: { type: 'base64', media_type: 'image/jpeg', data: screenshot },
33
+ },
34
+ {
35
+ type: 'text',
36
+ text: `Goal: ${goal}
37
+ ${valueHint}
38
+
39
+ Look at this screenshot of a webpage.
40
+ Find the best element to interact with to achieve the goal.
41
+
42
+ Respond ONLY with JSON:
43
+ {
44
+ "action": "click" | "fill" | "none",
45
+ "selector": "a valid CSS selector or Playwright locator string",
46
+ "confidence": 0.0-1.0
47
+ }
48
+
49
+ Rules:
50
+ - For search/input goals use "fill"
51
+ - For button/link goals use "click"
52
+ - If nothing relevant exists use "none"
53
+ - Prefer specific selectors: [placeholder="..."], [aria-label="..."], button:has-text("...")`,
54
+ },
55
+ ],
56
+ }],
57
+ });
58
+ const raw = msg.content[0].text.trim()
59
+ .replace(/^```json?\s*/i, '').replace(/\s*```$/i, '').trim();
60
+ return JSON.parse(raw);
61
+ }
62
+ /**
63
+ * Read and extract specific text from a screenshot.
64
+ */
65
+ async read(screenshot, goal) {
66
+ const prompt = goal
67
+ ? `From this webpage screenshot, extract: ${goal}. Return only the extracted text, nothing else.`
68
+ : 'Extract all visible main content text from this webpage. Skip navigation, ads, and footers.';
69
+ const msg = await this.client.messages.create({
70
+ model: this.model,
71
+ max_tokens: 1000,
72
+ messages: [{
73
+ role: 'user',
74
+ content: [
75
+ {
76
+ type: 'image',
77
+ source: { type: 'base64', media_type: 'image/jpeg', data: screenshot },
78
+ },
79
+ { type: 'text', text: prompt },
80
+ ],
81
+ }],
82
+ });
83
+ return msg.content[0].text.trim();
84
+ }
85
+ }
86
+ exports.AIClient = AIClient;
87
+ //# sourceMappingURL=ai.js.map
package/dist/ai.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;AAEH,4DAA0C;AAG1C,MAAa,QAAQ;IAGnB,YAAY,MAAc,EAAW,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,aAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,IAAY,EAAE,KAAc;QAC3D,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,mBAAmB,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC5C,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,CAAC;oBACT,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,OAAO;4BACb,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE;yBACvE;wBACD;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,SAAS,IAAI;EAC7B,SAAS;;;;;;;;;;;;;;;;6FAgBkF;yBAClF;qBACF;iBACF,CAAC;SACH,CAAC,CAAC;QAEH,MAAM,GAAG,GAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAoC,CAAC,IAAI,CAAC,IAAI,EAAE;aACvE,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAE/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,UAAkB,EAAE,IAAa;QAC1C,MAAM,MAAM,GAAG,IAAI;YACjB,CAAC,CAAC,0CAA0C,IAAI,iDAAiD;YACjG,CAAC,CAAC,6FAA6F,CAAC;QAElG,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC5C,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE,CAAC;oBACT,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,OAAO;4BACb,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE;yBACvE;wBACD,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;qBAC/B;iBACF,CAAC;SACH,CAAC,CAAC;QAEH,OAAQ,GAAG,CAAC,OAAO,CAAC,CAAC,CAAoC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACxE,CAAC;CACF;AAhFD,4BAgFC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * browser.ts
3
+ * Main entry point. Manages Playwright browser lifecycle and creates Sessions.
4
+ */
5
+ import { Session } from './session';
6
+ import type { RensanConfig, GotoOptions } from './types';
7
+ export declare class RensanBrowser {
8
+ private browser;
9
+ private config;
10
+ constructor(config: RensanConfig);
11
+ launch(): Promise<void>;
12
+ close(): Promise<void>;
13
+ /**
14
+ * Open a new browser session (tab).
15
+ * Auto-launches browser if not running.
16
+ *
17
+ * @example
18
+ * const session = await browser.newSession()
19
+ * await session.goto('https://example.com')
20
+ */
21
+ newSession(): Promise<Session>;
22
+ /**
23
+ * Open a session and navigate to a URL in one step.
24
+ *
25
+ * @example
26
+ * const session = await browser.open('https://example.com')
27
+ * await session.click('the search button')
28
+ */
29
+ open(url: string, options?: GotoOptions): Promise<Session>;
30
+ /**
31
+ * Open multiple sessions in parallel, each navigated to its own URL.
32
+ *
33
+ * @example
34
+ * const [s1, s2, s3] = await browser.openMany([
35
+ * 'https://skyscanner.com',
36
+ * 'https://kayak.com',
37
+ * 'https://despegar.com',
38
+ * ])
39
+ * const results = await Promise.all([
40
+ * s1.capture(async () => s1.fill('BUE', 'origin')),
41
+ * s2.capture(async () => s2.fill('BUE', 'origin')),
42
+ * s3.capture(async () => s3.fill('BUE', 'origin')),
43
+ * ])
44
+ */
45
+ openMany(urls: string[], options?: GotoOptions): Promise<Session[]>;
46
+ /**
47
+ * Run a task in a temporary session that auto-closes when done.
48
+ *
49
+ * @example
50
+ * const requests = await browser.run(async (session) => {
51
+ * await session.goto('https://example.com')
52
+ * return session.capture(async () => {
53
+ * await session.fill('argentina', 'search')
54
+ * })
55
+ * })
56
+ */
57
+ run<T>(fn: (session: Session) => Promise<T>): Promise<T>;
58
+ }
59
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAQzD,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAAyB;gBAE3B,MAAM,EAAE,YAAY;IAM1B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;;;;OAOG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAOpC;;;;;;OAMG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAMhE;;;;;;;;;;;;;;OAcG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAIzE;;;;;;;;;;OAUG;IACG,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAQ/D"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ /**
3
+ * browser.ts
4
+ * Main entry point. Manages Playwright browser lifecycle and creates Sessions.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.RensanBrowser = void 0;
8
+ const playwright_1 = require("playwright");
9
+ const session_1 = require("./session");
10
+ const DEFAULTS = {
11
+ model: 'claude-haiku-4-5',
12
+ headless: true,
13
+ timeout: 25000,
14
+ };
15
+ class RensanBrowser {
16
+ constructor(config) {
17
+ this.browser = null;
18
+ this.config = { ...DEFAULTS, ...config };
19
+ }
20
+ // ─── Lifecycle ───────────────────────────────────────────────────────────────
21
+ async launch() {
22
+ if (this.browser)
23
+ return;
24
+ this.browser = await playwright_1.chromium.launch({ headless: this.config.headless });
25
+ }
26
+ async close() {
27
+ await this.browser?.close().catch(() => { });
28
+ this.browser = null;
29
+ }
30
+ // ─── Session factory ─────────────────────────────────────────────────────────
31
+ /**
32
+ * Open a new browser session (tab).
33
+ * Auto-launches browser if not running.
34
+ *
35
+ * @example
36
+ * const session = await browser.newSession()
37
+ * await session.goto('https://example.com')
38
+ */
39
+ async newSession() {
40
+ await this.launch();
41
+ const ctx = await this.browser.newContext({ viewport: { width: 1280, height: 800 } });
42
+ const page = await ctx.newPage();
43
+ return new session_1.Session(page, this.config.apiKey, this.config.model, this.config.timeout);
44
+ }
45
+ /**
46
+ * Open a session and navigate to a URL in one step.
47
+ *
48
+ * @example
49
+ * const session = await browser.open('https://example.com')
50
+ * await session.click('the search button')
51
+ */
52
+ async open(url, options) {
53
+ const session = await this.newSession();
54
+ await session.goto(url, options);
55
+ return session;
56
+ }
57
+ /**
58
+ * Open multiple sessions in parallel, each navigated to its own URL.
59
+ *
60
+ * @example
61
+ * const [s1, s2, s3] = await browser.openMany([
62
+ * 'https://skyscanner.com',
63
+ * 'https://kayak.com',
64
+ * 'https://despegar.com',
65
+ * ])
66
+ * const results = await Promise.all([
67
+ * s1.capture(async () => s1.fill('BUE', 'origin')),
68
+ * s2.capture(async () => s2.fill('BUE', 'origin')),
69
+ * s3.capture(async () => s3.fill('BUE', 'origin')),
70
+ * ])
71
+ */
72
+ async openMany(urls, options) {
73
+ return Promise.all(urls.map(url => this.open(url, options)));
74
+ }
75
+ /**
76
+ * Run a task in a temporary session that auto-closes when done.
77
+ *
78
+ * @example
79
+ * const requests = await browser.run(async (session) => {
80
+ * await session.goto('https://example.com')
81
+ * return session.capture(async () => {
82
+ * await session.fill('argentina', 'search')
83
+ * })
84
+ * })
85
+ */
86
+ async run(fn) {
87
+ const session = await this.newSession();
88
+ try {
89
+ return await fn(session);
90
+ }
91
+ finally {
92
+ await session.close();
93
+ }
94
+ }
95
+ }
96
+ exports.RensanBrowser = RensanBrowser;
97
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,2CAAoD;AACpD,uCAAoC;AAGpC,MAAM,QAAQ,GAAG;IACf,KAAK,EAAK,kBAAkB;IAC5B,QAAQ,EAAE,IAAI;IACd,OAAO,EAAG,KAAK;CAChB,CAAC;AAEF,MAAa,aAAa;IAIxB,YAAY,MAAoB;QAHxB,YAAO,GAAmB,IAAI,CAAC;QAIrC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;IAC3C,CAAC;IAED,gFAAgF;IAEhF,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,MAAM,qBAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,gFAAgF;IAEhF;;;;;;;OAOG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,GAAG,GAAI,MAAM,IAAI,CAAC,OAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACxF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACjC,OAAO,IAAI,iBAAO,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,OAAqB;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACjC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,QAAQ,CAAC,IAAc,EAAE,OAAqB;QAClD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,GAAG,CAAI,EAAoC;QAC/C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;CACF;AAxFD,sCAwFC"}
@@ -0,0 +1,4 @@
1
+ export { RensanBrowser } from './browser';
2
+ export { Session } from './session';
3
+ export type { RensanConfig, CapturedRequest, GotoOptions, FillOptions, ScrollOptions, } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EACV,YAAY,EACZ,eAAe,EACf,WAAW,EACX,WAAW,EACX,aAAa,GACd,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Session = exports.RensanBrowser = void 0;
4
+ var browser_1 = require("./browser");
5
+ Object.defineProperty(exports, "RensanBrowser", { enumerable: true, get: function () { return browser_1.RensanBrowser; } });
6
+ var session_1 = require("./session");
7
+ Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_1.Session; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAA0C;AAAjC,wGAAA,aAAa,OAAA;AACtB,qCAAoC;AAA3B,kGAAA,OAAO,OAAA"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * interceptor.ts
3
+ * Captures JSON network responses from a Playwright page.
4
+ * Uses structural scoring to rank useful vs junk URLs.
5
+ */
6
+ import type { Page } from 'playwright';
7
+ import type { CapturedRequest } from './types';
8
+ export declare class Interceptor {
9
+ private page;
10
+ private captured;
11
+ private handler;
12
+ private siteUrl;
13
+ constructor(page: Page, siteUrl?: string);
14
+ setSiteUrl(url: string): void;
15
+ start(): void;
16
+ stop(): CapturedRequest[];
17
+ get raw(): Array<{
18
+ url: string;
19
+ data: unknown;
20
+ }>;
21
+ ranked(topN?: number): CapturedRequest[];
22
+ }
23
+ //# sourceMappingURL=interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAsC/C,qBAAa,WAAW;IAKV,OAAO,CAAC,IAAI;IAJxB,OAAO,CAAC,QAAQ,CAA6C;IAC7D,OAAO,CAAC,OAAO,CAAmD;IAClE,OAAO,CAAC,OAAO,CAAS;gBAEJ,IAAI,EAAE,IAAI,EAAE,OAAO,SAAK;IAI5C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,KAAK,IAAI,IAAI;IAyBb,IAAI,IAAI,eAAe,EAAE;IAQzB,IAAI,GAAG,IAAI,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAE/C;IAED,MAAM,CAAC,IAAI,SAAK,GAAG,eAAe,EAAE;CAMrC"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * interceptor.ts
4
+ * Captures JSON network responses from a Playwright page.
5
+ * Uses structural scoring to rank useful vs junk URLs.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.Interceptor = void 0;
9
+ function scoreUrl(url, siteUrl) {
10
+ let score = 0;
11
+ try {
12
+ const u = new URL(url);
13
+ const site = new URL(siteUrl);
14
+ const siteDomain = site.hostname.replace(/^www\./, '');
15
+ const urlDomain = u.hostname.replace(/^www\./, '');
16
+ // First-party = strong signal
17
+ if (urlDomain === siteDomain || urlDomain.endsWith('.' + siteDomain) || siteDomain.endsWith('.' + urlDomain)) {
18
+ score += 10;
19
+ }
20
+ const path = u.pathname.toLowerCase();
21
+ const params = [...u.searchParams];
22
+ if (/\/api\/|\/v\d+\/|\/rest\/|\/graphql/.test(path))
23
+ score += 5;
24
+ if (/\/content\/|\/articles\/|\/news\/|\/feed\//.test(path))
25
+ score += 3;
26
+ if (/\/search\/|\/query\/|\/data\/|\/results\//.test(path))
27
+ score += 3;
28
+ if (/\/products?\/|\/items?\/|\/catalog\//.test(path))
29
+ score += 2;
30
+ // Tracking param keys
31
+ const trackingKeys = ['uid', 'partner_id', 'gdpr', 'xcid', 'anId', 'tid', 'sjk', 'pid', 'coppa', 'enrich', 'sdkp'];
32
+ const trackingHits = params.filter(([k]) => trackingKeys.includes(k)).length;
33
+ score -= trackingHits * 2;
34
+ if (params.length > 8)
35
+ score -= 3;
36
+ if (params.length > 15)
37
+ score -= 5;
38
+ if (url.length > 300)
39
+ score -= 3;
40
+ if (url.length > 500)
41
+ score -= 5;
42
+ }
43
+ catch {
44
+ return -99;
45
+ }
46
+ return score;
47
+ }
48
+ class Interceptor {
49
+ constructor(page, siteUrl = '') {
50
+ this.page = page;
51
+ this.captured = [];
52
+ this.handler = null;
53
+ this.siteUrl = siteUrl;
54
+ }
55
+ setSiteUrl(url) {
56
+ this.siteUrl = url;
57
+ }
58
+ start() {
59
+ this.captured = [];
60
+ this.handler = async (res) => {
61
+ const status = res.status();
62
+ const ct = res.headers()['content-type'] ?? '';
63
+ const url = res.url();
64
+ if (status !== 200)
65
+ return;
66
+ if (!ct.includes('json'))
67
+ return;
68
+ if (url.length > 600)
69
+ return;
70
+ try {
71
+ const data = await res.json();
72
+ if (!data || typeof data !== 'object')
73
+ return;
74
+ const keys = Array.isArray(data)
75
+ ? Object.keys(data[0] ?? {})
76
+ : Object.keys(data);
77
+ if (keys.length < 2)
78
+ return;
79
+ this.captured.push({ url, data });
80
+ }
81
+ catch { /* skip */ }
82
+ };
83
+ this.page.on('response', this.handler);
84
+ }
85
+ stop() {
86
+ if (this.handler) {
87
+ this.page.off('response', this.handler);
88
+ this.handler = null;
89
+ }
90
+ return this.ranked();
91
+ }
92
+ get raw() {
93
+ return [...this.captured];
94
+ }
95
+ ranked(topN = 15) {
96
+ return this.captured
97
+ .map(r => ({ url: r.url, data: r.data, score: scoreUrl(r.url, this.siteUrl) }))
98
+ .sort((a, b) => b.score - a.score)
99
+ .slice(0, topN);
100
+ }
101
+ }
102
+ exports.Interceptor = Interceptor;
103
+ //# sourceMappingURL=interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * session.ts
3
+ * One browser tab — the main object you interact with.
4
+ *
5
+ * session.goto() — navigate
6
+ * session.click() — AI finds and clicks element
7
+ * session.fill() — AI finds input and types
8
+ * session.type() — type at current focus
9
+ * session.read() — AI reads text from page
10
+ * session.screenshot() — returns base64 JPEG
11
+ * session.scroll() — scroll up/down
12
+ * session.moveTo() — move cursor to element or coords
13
+ * session.capture() — run block while capturing requests
14
+ */
15
+ import type { Page } from 'playwright';
16
+ import type { CapturedRequest, GotoOptions, FillOptions, ScrollOptions } from './types';
17
+ export declare class Session {
18
+ readonly page: Page;
19
+ private defaultTimeout;
20
+ private ai;
21
+ private interceptor;
22
+ private currentUrl;
23
+ constructor(page: Page, apiKey: string, model: string, defaultTimeout: number);
24
+ goto(url: string, options?: GotoOptions): Promise<void>;
25
+ wait(ms: number): Promise<void>;
26
+ screenshot(): Promise<string>;
27
+ /**
28
+ * Click an element described in natural language.
29
+ * AI sees the page and finds the right element.
30
+ *
31
+ * @example
32
+ * await session.click('the Accept cookies button')
33
+ * await session.click('the search icon in the top right')
34
+ */
35
+ click(goal: string): Promise<boolean>;
36
+ private semanticClick;
37
+ /**
38
+ * Find an input and type a value.
39
+ * AI sees the page and locates the right field.
40
+ *
41
+ * @example
42
+ * await session.fill('BUE', 'the origin airport input')
43
+ * await session.fill('argentina', 'the main search field')
44
+ */
45
+ fill(value: string, goal: string, options?: FillOptions): Promise<boolean>;
46
+ /**
47
+ * Type text at the current focused element.
48
+ */
49
+ type(text: string, options?: FillOptions): Promise<void>;
50
+ /**
51
+ * Read text from the page using AI vision.
52
+ *
53
+ * @example
54
+ * const title = await session.read('the main article headline')
55
+ * const price = await session.read('the product price')
56
+ * const all = await session.read() // all visible content
57
+ */
58
+ read(goal?: string): Promise<string>;
59
+ /**
60
+ * Scroll the page.
61
+ *
62
+ * @example
63
+ * await session.scroll('down')
64
+ * await session.scroll('up', { pixels: 200 })
65
+ * await session.scroll('down', { pixels: 800 })
66
+ */
67
+ scroll(direction: 'up' | 'down', options?: ScrollOptions): Promise<void>;
68
+ /**
69
+ * Move cursor to coordinates or to an element described in natural language.
70
+ *
71
+ * @example
72
+ * await session.moveTo({ x: 100, y: 200 })
73
+ * await session.moveTo('the hamburger menu icon')
74
+ */
75
+ moveTo(target: {
76
+ x: number;
77
+ y: number;
78
+ } | string): Promise<void>;
79
+ startCapture(): void;
80
+ stopCapture(): CapturedRequest[];
81
+ get capturedRequests(): CapturedRequest[];
82
+ /**
83
+ * Run a block and return captured + scored requests after.
84
+ *
85
+ * @example
86
+ * const requests = await session.capture(async () => {
87
+ * await session.fill('BUE', 'origin airport')
88
+ * await session.click('search button')
89
+ * })
90
+ */
91
+ capture(fn: () => Promise<void>): Promise<CapturedRequest[]>;
92
+ close(): Promise<void>;
93
+ }
94
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExF,qBAAa,OAAO;IAMhB,QAAQ,CAAC,IAAI,EAAE,IAAI;IAGnB,OAAO,CAAC,cAAc;IARxB,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAM;gBAGb,IAAI,EAAE,IAAI,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACL,cAAc,EAAE,MAAM;IAQ1B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAOnC;;;;;;;OAOG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAkB7B,aAAa;IAyB3B;;;;;;;OAOG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAsCpF;;OAEG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlE;;;;;;;OAOG;IACG,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAc1C;;;;;;;OAOG;IACG,MAAM,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASlF;;;;;;OAMG;IACG,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtE,YAAY,IAAI,IAAI;IAKpB,WAAW,IAAI,eAAe,EAAE;IAIhC,IAAI,gBAAgB,IAAI,eAAe,EAAE,CAExC;IAED;;;;;;;;OAQG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAY5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ /**
3
+ * session.ts
4
+ * One browser tab — the main object you interact with.
5
+ *
6
+ * session.goto() — navigate
7
+ * session.click() — AI finds and clicks element
8
+ * session.fill() — AI finds input and types
9
+ * session.type() — type at current focus
10
+ * session.read() — AI reads text from page
11
+ * session.screenshot() — returns base64 JPEG
12
+ * session.scroll() — scroll up/down
13
+ * session.moveTo() — move cursor to element or coords
14
+ * session.capture() — run block while capturing requests
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.Session = void 0;
18
+ const ai_1 = require("./ai");
19
+ const interceptor_1 = require("./interceptor");
20
+ class Session {
21
+ constructor(page, apiKey, model, defaultTimeout) {
22
+ this.page = page;
23
+ this.defaultTimeout = defaultTimeout;
24
+ this.currentUrl = '';
25
+ this.ai = new ai_1.AIClient(apiKey, model);
26
+ this.interceptor = new interceptor_1.Interceptor(page);
27
+ }
28
+ // ─── Navigation ─────────────────────────────────────────────────────────────
29
+ async goto(url, options = {}) {
30
+ const { waitUntil = 'domcontentloaded', timeout = this.defaultTimeout } = options;
31
+ this.currentUrl = url;
32
+ this.interceptor.setSiteUrl(url);
33
+ await this.page.goto(url, { waitUntil, timeout }).catch(async () => {
34
+ await this.page.waitForTimeout(2500);
35
+ });
36
+ }
37
+ async wait(ms) {
38
+ await this.page.waitForTimeout(ms);
39
+ }
40
+ // ─── Screenshot ─────────────────────────────────────────────────────────────
41
+ async screenshot() {
42
+ const buf = await this.page.screenshot({ type: 'jpeg', quality: 60 });
43
+ return buf.toString('base64');
44
+ }
45
+ // ─── Click ──────────────────────────────────────────────────────────────────
46
+ /**
47
+ * Click an element described in natural language.
48
+ * AI sees the page and finds the right element.
49
+ *
50
+ * @example
51
+ * await session.click('the Accept cookies button')
52
+ * await session.click('the search icon in the top right')
53
+ */
54
+ async click(goal) {
55
+ // Layer 1 — try Playwright semantic first (free)
56
+ const semanticResult = await this.semanticClick(goal);
57
+ if (semanticResult)
58
+ return true;
59
+ // Layer 2 — vision
60
+ try {
61
+ const img = await this.screenshot();
62
+ const decision = await this.ai.decide(img, goal);
63
+ if (decision.action === 'none' || !decision.selector)
64
+ return false;
65
+ await this.page.locator(decision.selector).first().click({ timeout: 4000 });
66
+ return true;
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }
72
+ async semanticClick(goal) {
73
+ const lower = goal.toLowerCase();
74
+ // Cookie-like goals: try common patterns
75
+ if (/cookie|accept|consent|agree|dismiss/i.test(lower)) {
76
+ for (const sel of [
77
+ 'button:has-text("Accept")', 'button:has-text("Aceptar")',
78
+ 'button:has-text("I agree")', 'button:has-text("Got it")',
79
+ '#onetrust-accept-btn-handler', '[class*="cookie"] button',
80
+ ]) {
81
+ try {
82
+ const el = this.page.locator(sel).first();
83
+ if (await el.isVisible({ timeout: 400 }).catch(() => false)) {
84
+ await el.click({ timeout: 2000 });
85
+ return true;
86
+ }
87
+ }
88
+ catch { /* try next */ }
89
+ }
90
+ }
91
+ return false;
92
+ }
93
+ // ─── Fill ───────────────────────────────────────────────────────────────────
94
+ /**
95
+ * Find an input and type a value.
96
+ * AI sees the page and locates the right field.
97
+ *
98
+ * @example
99
+ * await session.fill('BUE', 'the origin airport input')
100
+ * await session.fill('argentina', 'the main search field')
101
+ */
102
+ async fill(value, goal, options = {}) {
103
+ const { pressEnter = true, delay = 60 } = options;
104
+ // Layer 1 — Playwright semantic roles (free)
105
+ for (const role of ['searchbox', 'textbox']) {
106
+ try {
107
+ const loc = this.page.getByRole(role).first();
108
+ if (await loc.isVisible({ timeout: 500 }).catch(() => false)) {
109
+ await loc.fill('');
110
+ await loc.type(value, { delay });
111
+ if (pressEnter)
112
+ await this.page.keyboard.press('Enter');
113
+ return true;
114
+ }
115
+ }
116
+ catch { /* try next */ }
117
+ }
118
+ // Layer 2 — vision
119
+ try {
120
+ const img = await this.screenshot();
121
+ const decision = await this.ai.decide(img, goal, value);
122
+ if (decision.action === 'none' || !decision.selector)
123
+ return false;
124
+ const el = this.page.locator(decision.selector).first();
125
+ if (decision.action === 'fill') {
126
+ await el.fill('');
127
+ await el.type(value, { delay });
128
+ if (pressEnter)
129
+ await this.page.keyboard.press('Enter');
130
+ }
131
+ else {
132
+ await el.click({ timeout: 4000 });
133
+ }
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ // ─── Type ───────────────────────────────────────────────────────────────────
141
+ /**
142
+ * Type text at the current focused element.
143
+ */
144
+ async type(text, options = {}) {
145
+ const { pressEnter = false, delay = 60 } = options;
146
+ await this.page.keyboard.type(text, { delay });
147
+ if (pressEnter)
148
+ await this.page.keyboard.press('Enter');
149
+ }
150
+ // ─── Read ───────────────────────────────────────────────────────────────────
151
+ /**
152
+ * Read text from the page using AI vision.
153
+ *
154
+ * @example
155
+ * const title = await session.read('the main article headline')
156
+ * const price = await session.read('the product price')
157
+ * const all = await session.read() // all visible content
158
+ */
159
+ async read(goal) {
160
+ // Try to get text from DOM first (free)
161
+ if (!goal) {
162
+ const text = await this.page.evaluate(() => document.body.innerText).catch(() => '');
163
+ if (text.length > 50)
164
+ return text.slice(0, 5000);
165
+ }
166
+ // Vision for specific extractions
167
+ const img = await this.screenshot();
168
+ return this.ai.read(img, goal);
169
+ }
170
+ // ─── Scroll ─────────────────────────────────────────────────────────────────
171
+ /**
172
+ * Scroll the page.
173
+ *
174
+ * @example
175
+ * await session.scroll('down')
176
+ * await session.scroll('up', { pixels: 200 })
177
+ * await session.scroll('down', { pixels: 800 })
178
+ */
179
+ async scroll(direction, options = {}) {
180
+ const { pixels = 400 } = options;
181
+ const delta = direction === 'down' ? pixels : -pixels;
182
+ await this.page.evaluate((d) => window.scrollBy(0, d), delta);
183
+ await this.page.waitForTimeout(300);
184
+ }
185
+ // ─── Move cursor ─────────────────────────────────────────────────────────────
186
+ /**
187
+ * Move cursor to coordinates or to an element described in natural language.
188
+ *
189
+ * @example
190
+ * await session.moveTo({ x: 100, y: 200 })
191
+ * await session.moveTo('the hamburger menu icon')
192
+ */
193
+ async moveTo(target) {
194
+ if (typeof target === 'object') {
195
+ await this.page.mouse.move(target.x, target.y);
196
+ return;
197
+ }
198
+ // AI finds the element
199
+ try {
200
+ const img = await this.screenshot();
201
+ const decision = await this.ai.decide(img, `hover over: ${target}`);
202
+ if (decision.action === 'none' || !decision.selector)
203
+ return;
204
+ await this.page.locator(decision.selector).first().hover({ timeout: 3000 });
205
+ }
206
+ catch { /* ignore */ }
207
+ }
208
+ // ─── Request capture ─────────────────────────────────────────────────────────
209
+ startCapture() {
210
+ this.interceptor.setSiteUrl(this.currentUrl);
211
+ this.interceptor.start();
212
+ }
213
+ stopCapture() {
214
+ return this.interceptor.stop();
215
+ }
216
+ get capturedRequests() {
217
+ return this.interceptor.ranked();
218
+ }
219
+ /**
220
+ * Run a block and return captured + scored requests after.
221
+ *
222
+ * @example
223
+ * const requests = await session.capture(async () => {
224
+ * await session.fill('BUE', 'origin airport')
225
+ * await session.click('search button')
226
+ * })
227
+ */
228
+ async capture(fn) {
229
+ this.startCapture();
230
+ try {
231
+ await fn();
232
+ await this.page.waitForTimeout(2000);
233
+ }
234
+ finally {
235
+ return this.stopCapture();
236
+ }
237
+ }
238
+ // ─── Lifecycle ───────────────────────────────────────────────────────────────
239
+ async close() {
240
+ await this.page.close().catch(() => { });
241
+ }
242
+ }
243
+ exports.Session = Session;
244
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface RensanConfig {
2
+ /** Anthropic API key */
3
+ apiKey: string;
4
+ /** Vision model for page understanding. Default: claude-haiku-4-5 */
5
+ model?: string;
6
+ /** Run browser headless. Default: true */
7
+ headless?: boolean;
8
+ /** Default navigation timeout ms. Default: 25000 */
9
+ timeout?: number;
10
+ }
11
+ export interface GotoOptions {
12
+ waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';
13
+ timeout?: number;
14
+ }
15
+ export interface FillOptions {
16
+ /** Press Enter after typing. Default: true */
17
+ pressEnter?: boolean;
18
+ /** Delay between keystrokes ms. Default: 60 */
19
+ delay?: number;
20
+ }
21
+ export interface ScrollOptions {
22
+ /** Pixels to scroll. Default: 400 */
23
+ pixels?: number;
24
+ }
25
+ export interface CapturedRequest {
26
+ url: string;
27
+ data: Record<string, unknown>;
28
+ score: number;
29
+ }
30
+ export interface AIDecision {
31
+ action: 'click' | 'fill' | 'none';
32
+ selector: string;
33
+ confidence: number;
34
+ }
35
+ export interface ReadResult {
36
+ text: string;
37
+ raw: string;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,aAAa,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAID,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // ─── Config ───────────────────────────────────────────────────────────────────
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,iFAAiF"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "rensan-browser",
3
+ "version": "0.1.0",
4
+ "description": "AI-powered browser for agents — navigate, click, type, read, see images, capture requests",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.js",
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist"],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "keywords": ["browser", "ai", "agent", "playwright", "automation", "vision", "scraping", "claude"],
21
+ "license": "MIT",
22
+ "peerDependencies": {
23
+ "playwright": ">=1.46.0"
24
+ },
25
+ "dependencies": {
26
+ "@anthropic-ai/sdk": "^0.39.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^20.0.0",
30
+ "playwright": "^1.58.0",
31
+ "typescript": "^5.0.0"
32
+ }
33
+ }