zan-browser 1.0.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/dist/ai.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { ObserveResult, AgentAction } from "./types";
2
+ export declare class AIClient {
3
+ private client;
4
+ private model;
5
+ constructor(apiKey: string, model?: string);
6
+ decideFromObservation(goal: string, observation: ObserveResult, history: string[]): Promise<AgentAction>;
7
+ decideFromScreenshot(goal: string, screenshotBase64: string, history: string[]): Promise<AgentAction>;
8
+ private parseAction;
9
+ }
10
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAM1D,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,SAAgB;IAM3C,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,aAAa,EAC1B,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,WAAW,CAAC;IA+CjB,oBAAoB,CACxB,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,WAAW,CAAC;IAsDvB,OAAO,CAAC,WAAW;CA2BpB"}
package/dist/ai.js ADDED
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AIClient = void 0;
7
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
8
+ const DEFAULT_MODEL = "claude-haiku-4-5-20251001";
9
+ // AI is only used when observe() is not enough to make a decision.
10
+ // In practice: 0–2 calls per session.
11
+ class AIClient {
12
+ client;
13
+ model;
14
+ constructor(apiKey, model = DEFAULT_MODEL) {
15
+ this.client = new sdk_1.default({ apiKey });
16
+ this.model = model;
17
+ }
18
+ // Decide the next action given the observed DOM state (text first, no screenshot)
19
+ async decideFromObservation(goal, observation, history) {
20
+ const historyText = history.length > 0
21
+ ? `\nPREVIOUS STEPS:\n${history.slice(-5).join("\n")}`
22
+ : "";
23
+ const prompt = `You are controlling a browser to achieve a goal.
24
+ Decide the SINGLE next action to take based on the current page state.
25
+
26
+ GOAL: ${goal}
27
+ ${historyText}
28
+
29
+ CURRENT PAGE STATE:
30
+ ${observation.text}
31
+
32
+ IMPORTANT RULES:
33
+ - Always dismiss cookie banners first if hasCookieBanner=true
34
+ - If a modal is open, interact with it first
35
+ - If the page is loading, return a wait action
36
+ - Take the minimum actions needed — stop as soon as the goal is achievable
37
+ - If you cannot achieve the goal, return "impossible"
38
+ - If the goal is achieved, return "done"
39
+
40
+ Respond with a JSON object (no markdown):
41
+ {
42
+ "type": "click" | "fill" | "scroll" | "wait" | "screenshot" | "done" | "impossible",
43
+ "elementId": "@B3" (required for click/fill),
44
+ "value": "text to type" (required for fill),
45
+ "direction": "up" | "down" (required for scroll),
46
+ "amount": 300 (optional for scroll, pixels),
47
+ "ms": 1000 (required for wait),
48
+ "reason": "why this action"
49
+ }`;
50
+ const response = await this.client.messages.create({
51
+ model: this.model,
52
+ max_tokens: 256,
53
+ messages: [{ role: "user", content: prompt }],
54
+ });
55
+ const text = response.content[0].type === "text" ? response.content[0].text : "";
56
+ return this.parseAction(text);
57
+ }
58
+ // Vision fallback — only called when observe() returns ambiguous state
59
+ // e.g. canvas-rendered UI, complex dynamic content
60
+ async decideFromScreenshot(goal, screenshotBase64, history) {
61
+ const historyText = history.length > 0
62
+ ? `\nPREVIOUS STEPS:\n${history.slice(-3).join("\n")}`
63
+ : "";
64
+ const prompt = `You are controlling a browser to achieve a goal.
65
+ The DOM observer couldn't identify enough elements — you're seeing a screenshot instead.
66
+ Decide the SINGLE next action.
67
+
68
+ GOAL: ${goal}
69
+ ${historyText}
70
+
71
+ RULES: Take minimum actions needed. Return "done" when goal is achievable.
72
+ Return "impossible" if it cannot be done.
73
+
74
+ Respond with JSON (no markdown):
75
+ {
76
+ "type": "click" | "fill" | "scroll" | "wait" | "screenshot" | "done" | "impossible",
77
+ "elementId": "describe the element visually if no ID available",
78
+ "value": "text to type" (for fill),
79
+ "direction": "up" | "down" (for scroll),
80
+ "ms": 1000 (for wait),
81
+ "reason": "why"
82
+ }`;
83
+ const response = await this.client.messages.create({
84
+ model: this.model,
85
+ max_tokens: 256,
86
+ messages: [
87
+ {
88
+ role: "user",
89
+ content: [
90
+ {
91
+ type: "image",
92
+ source: {
93
+ type: "base64",
94
+ media_type: "image/jpeg",
95
+ data: screenshotBase64,
96
+ },
97
+ },
98
+ { type: "text", text: prompt },
99
+ ],
100
+ },
101
+ ],
102
+ });
103
+ const text = response.content[0].type === "text" ? response.content[0].text : "";
104
+ return this.parseAction(text);
105
+ }
106
+ // ─── Private ─────────────────────────────────────────────────────────────────
107
+ parseAction(raw) {
108
+ try {
109
+ const cleaned = raw.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
110
+ const parsed = JSON.parse(cleaned);
111
+ switch (parsed.type) {
112
+ case "click":
113
+ return { type: "click", elementId: parsed.elementId ?? "", reason: parsed.reason ?? "" };
114
+ case "fill":
115
+ return { type: "fill", elementId: parsed.elementId ?? "", value: parsed.value ?? "", reason: parsed.reason ?? "" };
116
+ case "scroll":
117
+ return { type: "scroll", direction: parsed.direction ?? "down", amount: parsed.amount, reason: parsed.reason ?? "" };
118
+ case "wait":
119
+ return { type: "wait", ms: parsed.ms ?? 1000, reason: parsed.reason ?? "" };
120
+ case "screenshot":
121
+ return { type: "screenshot", reason: parsed.reason ?? "need visual confirmation" };
122
+ case "done":
123
+ return { type: "done", reason: parsed.reason ?? "" };
124
+ case "impossible":
125
+ return { type: "impossible", reason: parsed.reason ?? "could not achieve goal" };
126
+ default:
127
+ return { type: "impossible", reason: `unknown action type: ${parsed.type}` };
128
+ }
129
+ }
130
+ catch {
131
+ return { type: "impossible", reason: `failed to parse AI response: ${raw.slice(0, 100)}` };
132
+ }
133
+ }
134
+ }
135
+ exports.AIClient = AIClient;
136
+ //# 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":"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"}
@@ -0,0 +1,9 @@
1
+ import type { BrowserConfig } from "./types";
2
+ import { Session } from "./session";
3
+ export declare class RensanBrowser {
4
+ private config;
5
+ private ai;
6
+ constructor(config: BrowserConfig);
7
+ newSession(): Promise<Session>;
8
+ }
9
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,EAAE,CAAW;gBAET,MAAM,EAAE,aAAa;IAM3B,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;CA2BrC"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RensanBrowser = void 0;
4
+ const playwright_1 = require("playwright");
5
+ const session_1 = require("./session");
6
+ const ai_1 = require("./ai");
7
+ class RensanBrowser {
8
+ config;
9
+ ai;
10
+ constructor(config) {
11
+ this.config = config;
12
+ this.ai = new ai_1.AIClient(config.ai.apiKey, config.ai.model);
13
+ }
14
+ // Create a new browser session connected to the cloud browser
15
+ async newSession() {
16
+ const { wsEndpoint, proxy, viewport, timeout } = this.config.session;
17
+ // Connect to the remote cloud browser via WebSocket
18
+ const browser = await playwright_1.chromium.connectOverCDP(wsEndpoint);
19
+ const contextOptions = {
20
+ viewport: viewport ?? { width: 1280, height: 800 },
21
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
22
+ locale: "en-US",
23
+ timezoneId: "America/New_York",
24
+ };
25
+ if (proxy) {
26
+ contextOptions.proxy = {
27
+ server: proxy.server,
28
+ username: proxy.username,
29
+ password: proxy.password,
30
+ };
31
+ }
32
+ const context = await browser.newContext(contextOptions);
33
+ const page = await context.newPage();
34
+ return new session_1.Session(page, browser, this.ai, timeout);
35
+ }
36
+ }
37
+ exports.RensanBrowser = RensanBrowser;
38
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":";;;AAAA,2CAAsC;AAEtC,uCAAoC;AACpC,6BAAgC;AAEhC,MAAa,aAAa;IAChB,MAAM,CAAgB;IACtB,EAAE,CAAW;IAErB,YAAY,MAAqB;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,GAAG,IAAI,aAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,UAAU;QACd,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;QAErE,oDAAoD;QACpD,MAAM,OAAO,GAAG,MAAM,qBAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAE1D,MAAM,cAAc,GAA6C;YAC/D,QAAQ,EAAE,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;YAClD,SAAS,EACP,iHAAiH;YACnH,MAAM,EAAE,OAAO;YACf,UAAU,EAAE,kBAAkB;SAC/B,CAAC;QAEF,IAAI,KAAK,EAAE,CAAC;YACV,cAAc,CAAC,KAAK,GAAG;gBACrB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;aACzB,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAErC,OAAO,IAAI,iBAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;CACF;AArCD,sCAqCC"}
@@ -0,0 +1,4 @@
1
+ export { RensanBrowser } from "./browser";
2
+ export { Session } from "./session";
3
+ export type { ObservedElement, ObserveResult, CapturedRequest, AgentAction, ActResult, FindDataResult, SessionConfig, AIConfig, BrowserConfig, Screenshot, ElementType, } 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,eAAe,EACf,aAAa,EACb,eAAe,EACf,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,QAAQ,EACR,aAAa,EACb,UAAU,EACV,WAAW,GACZ,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,24 @@
1
+ import { EventEmitter } from "events";
2
+ import type { Page } from "playwright";
3
+ import type { CapturedRequest } from "./types";
4
+ export declare class Interceptor extends EventEmitter {
5
+ private captured;
6
+ private requestMap;
7
+ private active;
8
+ private page;
9
+ constructor(page: Page);
10
+ start(): void;
11
+ stop(): void;
12
+ getAll(): CapturedRequest[];
13
+ getUseful(minScore?: number): CapturedRequest[];
14
+ clear(): void;
15
+ private onRequest;
16
+ private onResponse;
17
+ private onRequestFailed;
18
+ private scoreRequest;
19
+ private shouldIgnore;
20
+ private isAsset;
21
+ private isXHR;
22
+ private isUsefulContentType;
23
+ }
24
+ //# sourceMappingURL=interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAgD/C,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAO;gBAEP,IAAI,EAAE,IAAI;IAMtB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;IASZ,MAAM,IAAI,eAAe,EAAE;IAK3B,SAAS,CAAC,QAAQ,SAAI,GAAG,eAAe,EAAE;IAM1C,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,SAAS,CAoBf;IAEF,OAAO,CAAC,UAAU,CAkChB;IAEF,OAAO,CAAC,eAAe,CASrB;IAIF,OAAO,CAAC,YAAY;IA2DpB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,mBAAmB;CAG5B"}
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Interceptor = void 0;
4
+ const events_1 = require("events");
5
+ const crypto_1 = require("crypto");
6
+ // Patterns that strongly indicate a real data API
7
+ const API_PATH_PATTERNS = [
8
+ /\/api\//i,
9
+ /\/v\d+\//i,
10
+ /\/graphql/i,
11
+ /\/rest\//i,
12
+ /\/data\//i,
13
+ /\/feed/i,
14
+ /\/search/i,
15
+ /\/query/i,
16
+ ];
17
+ const CONTENT_PATH_PATTERNS = [
18
+ /\/products/i,
19
+ /\/articles/i,
20
+ /\/flights/i,
21
+ /\/prices/i,
22
+ /\/rates/i,
23
+ /\/results/i,
24
+ /\/items/i,
25
+ ];
26
+ // Tracking params that lower score
27
+ const TRACKING_PARAMS = [
28
+ "utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term",
29
+ "_ga", "fbclid", "gclid", "msclkid", "ref", "source",
30
+ ];
31
+ // File extensions that are never useful
32
+ const ASSET_EXTENSIONS = [
33
+ ".js", ".css", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".woff",
34
+ ".woff2", ".ttf", ".eot", ".ico", ".webp", ".avif", ".mp4", ".mp3",
35
+ ".pdf", ".zip",
36
+ ];
37
+ // Content types we care about
38
+ const USEFUL_CONTENT_TYPES = [
39
+ "application/json",
40
+ "application/xml",
41
+ "text/xml",
42
+ "application/rss+xml",
43
+ "text/plain",
44
+ "application/x-www-form-urlencoded",
45
+ ];
46
+ class Interceptor extends events_1.EventEmitter {
47
+ captured = new Map();
48
+ requestMap = new Map(); // playwright req id → our id
49
+ active = false;
50
+ page;
51
+ constructor(page) {
52
+ super();
53
+ this.page = page;
54
+ }
55
+ // Start capturing immediately — called as soon as session opens (t=0)
56
+ start() {
57
+ if (this.active)
58
+ return;
59
+ this.active = true;
60
+ this.page.on("request", this.onRequest);
61
+ this.page.on("response", this.onResponse);
62
+ this.page.on("requestfailed", this.onRequestFailed);
63
+ }
64
+ stop() {
65
+ if (!this.active)
66
+ return;
67
+ this.active = false;
68
+ this.page.off("request", this.onRequest);
69
+ this.page.off("response", this.onResponse);
70
+ this.page.off("requestfailed", this.onRequestFailed);
71
+ }
72
+ getAll() {
73
+ return Array.from(this.captured.values()).sort((a, b) => b.score - a.score);
74
+ }
75
+ // Get only XHR/fetch requests with score > threshold, sorted by relevance
76
+ getUseful(minScore = 1) {
77
+ return this.getAll().filter((r) => !r.isAsset && r.isXHR && r.score >= minScore);
78
+ }
79
+ clear() {
80
+ this.captured.clear();
81
+ this.requestMap.clear();
82
+ }
83
+ // ─── Private handlers ───────────────────────────────────────────────────────
84
+ onRequest = (request) => {
85
+ const url = request.url();
86
+ if (this.shouldIgnore(url))
87
+ return;
88
+ const id = (0, crypto_1.randomUUID)();
89
+ this.requestMap.set(request.url() + request.method(), id);
90
+ const entry = {
91
+ id,
92
+ timestamp: Date.now(),
93
+ method: request.method(),
94
+ url,
95
+ requestHeaders: request.headers(),
96
+ requestBody: request.postData() ?? undefined,
97
+ isXHR: this.isXHR(request),
98
+ isAsset: this.isAsset(url),
99
+ score: 0, // scored when response arrives
100
+ };
101
+ this.captured.set(id, entry);
102
+ };
103
+ onResponse = async (response) => {
104
+ const url = response.url();
105
+ if (this.shouldIgnore(url))
106
+ return;
107
+ const key = url + response.request().method();
108
+ const id = this.requestMap.get(key);
109
+ if (!id)
110
+ return;
111
+ const entry = this.captured.get(id);
112
+ if (!entry)
113
+ return;
114
+ const contentType = response.headers()["content-type"] ?? "";
115
+ // Only read body for useful content types to avoid wasting memory
116
+ let body;
117
+ if (this.isUsefulContentType(contentType)) {
118
+ try {
119
+ body = await response.text();
120
+ }
121
+ catch {
122
+ body = undefined;
123
+ }
124
+ }
125
+ const updated = {
126
+ ...entry,
127
+ status: response.status(),
128
+ responseHeaders: response.headers(),
129
+ responseBody: body,
130
+ contentType,
131
+ score: this.scoreRequest(entry, response.status(), contentType, body),
132
+ };
133
+ this.captured.set(id, updated);
134
+ this.emit("request", updated); // real-time stream
135
+ };
136
+ onRequestFailed = (request) => {
137
+ const key = request.url() + request.method();
138
+ const id = this.requestMap.get(key);
139
+ if (!id)
140
+ return;
141
+ const entry = this.captured.get(id);
142
+ if (!entry)
143
+ return;
144
+ this.captured.set(id, { ...entry, status: 0 });
145
+ };
146
+ // ─── Scoring ────────────────────────────────────────────────────────────────
147
+ scoreRequest(req, status, contentType, body) {
148
+ let score = 0;
149
+ if (req.isAsset)
150
+ return -10;
151
+ if (!req.isXHR)
152
+ return -5;
153
+ // Status must be successful
154
+ if (status >= 400 || status === 0)
155
+ return -1;
156
+ // First-party domain check (same domain as page)
157
+ try {
158
+ const pageHost = new URL(this.page.url()).hostname;
159
+ const reqHost = new URL(req.url).hostname;
160
+ if (reqHost === pageHost || reqHost.endsWith(`.${pageHost}`))
161
+ score += 10;
162
+ }
163
+ catch {
164
+ // ignore
165
+ }
166
+ // API path patterns
167
+ if (API_PATH_PATTERNS.some((p) => p.test(req.url)))
168
+ score += 5;
169
+ // Content path patterns
170
+ if (CONTENT_PATH_PATTERNS.some((p) => p.test(req.url)))
171
+ score += 3;
172
+ // Content type
173
+ if (contentType.includes("application/json"))
174
+ score += 5;
175
+ if (contentType.includes("xml") || contentType.includes("rss"))
176
+ score += 3;
177
+ // Has body with actual data
178
+ if (body && body.length > 100)
179
+ score += 3;
180
+ if (body && body.length > 1000)
181
+ score += 2;
182
+ // Tracking params lower score
183
+ try {
184
+ const params = new URL(req.url).searchParams;
185
+ for (const t of TRACKING_PARAMS) {
186
+ if (params.has(t))
187
+ score -= 2;
188
+ }
189
+ }
190
+ catch {
191
+ // ignore
192
+ }
193
+ // Very long URLs are usually tracking
194
+ if (req.url.length > 300)
195
+ score -= 3;
196
+ if (req.url.length > 500)
197
+ score -= 2;
198
+ // access-control-allow-origin: * means it's designed to be called directly
199
+ if (req.responseHeaders?.["access-control-allow-origin"] === "*")
200
+ score += 3;
201
+ return score;
202
+ }
203
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
204
+ shouldIgnore(url) {
205
+ return url.startsWith("data:") || url.startsWith("blob:");
206
+ }
207
+ isAsset(url) {
208
+ try {
209
+ const path = new URL(url).pathname.toLowerCase();
210
+ return ASSET_EXTENSIONS.some((ext) => path.endsWith(ext));
211
+ }
212
+ catch {
213
+ return false;
214
+ }
215
+ }
216
+ isXHR(request) {
217
+ const type = request.resourceType();
218
+ return type === "xhr" || type === "fetch";
219
+ }
220
+ isUsefulContentType(contentType) {
221
+ return USEFUL_CONTENT_TYPES.some((t) => contentType.includes(t));
222
+ }
223
+ }
224
+ exports.Interceptor = Interceptor;
225
+ //# 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,14 @@
1
+ import type { Page } from "playwright";
2
+ import type { ObserveResult } from "./types";
3
+ export declare class Observer {
4
+ private page;
5
+ constructor(page: Page);
6
+ observe(): Promise<ObserveResult>;
7
+ resolveElementId(id: string): Promise<{
8
+ x: number;
9
+ y: number;
10
+ } | null>;
11
+ private resolveType;
12
+ private buildTextRepresentation;
13
+ }
14
+ //# sourceMappingURL=observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAmB,aAAa,EAAe,MAAM,SAAS,CAAC;AAc3E,qBAAa,QAAQ;IACnB,OAAO,CAAC,IAAI,CAAO;gBAEP,IAAI,EAAE,IAAI;IAMhB,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;IAwLjC,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IA2B5E,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,uBAAuB;CAkChC"}
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Observer = void 0;
4
+ // Prefix map for element IDs
5
+ const PREFIX = {
6
+ button: "B",
7
+ input: "I",
8
+ link: "L",
9
+ select: "S",
10
+ textarea: "T",
11
+ checkbox: "C",
12
+ radio: "R",
13
+ other: "O",
14
+ };
15
+ class Observer {
16
+ page;
17
+ constructor(page) {
18
+ this.page = page;
19
+ }
20
+ // Returns the full DOM state as structured data + human-readable text
21
+ // This is always called FIRST before any screenshot
22
+ async observe() {
23
+ const result = await this.page.evaluate(() => {
24
+ const elements = [];
25
+ const isVisible = (el) => {
26
+ const rect = el.getBoundingClientRect();
27
+ const style = window.getComputedStyle(el);
28
+ return (rect.width > 0 &&
29
+ rect.height > 0 &&
30
+ style.display !== "none" &&
31
+ style.visibility !== "hidden" &&
32
+ style.opacity !== "0");
33
+ };
34
+ const getText = (el) => {
35
+ const aria = el.getAttribute("aria-label");
36
+ if (aria)
37
+ return aria.trim();
38
+ const title = el.getAttribute("title");
39
+ if (title)
40
+ return title.trim();
41
+ return (el.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 100);
42
+ };
43
+ // Buttons
44
+ document.querySelectorAll("button, [role='button'], input[type='submit'], input[type='button']").forEach((el) => {
45
+ elements.push({
46
+ tag: "button",
47
+ text: getText(el),
48
+ disabled: el.disabled ?? false,
49
+ visible: isVisible(el),
50
+ ariaLabel: el.getAttribute("aria-label") ?? undefined,
51
+ });
52
+ });
53
+ // Inputs
54
+ document.querySelectorAll("input:not([type='submit']):not([type='button']):not([type='hidden']):not([type='checkbox']):not([type='radio'])").forEach((el) => {
55
+ const input = el;
56
+ elements.push({
57
+ tag: "input",
58
+ type: input.type,
59
+ text: getText(el),
60
+ placeholder: input.placeholder || undefined,
61
+ value: input.value || undefined,
62
+ disabled: input.disabled,
63
+ visible: isVisible(el),
64
+ });
65
+ });
66
+ // Links
67
+ document.querySelectorAll("a[href]").forEach((el) => {
68
+ const anchor = el;
69
+ const text = getText(el);
70
+ if (!text)
71
+ return; // skip empty links
72
+ elements.push({
73
+ tag: "link",
74
+ text,
75
+ href: anchor.href,
76
+ disabled: false,
77
+ visible: isVisible(el),
78
+ });
79
+ });
80
+ // Selects
81
+ document.querySelectorAll("select").forEach((el) => {
82
+ const select = el;
83
+ elements.push({
84
+ tag: "select",
85
+ text: getText(el),
86
+ value: select.value || undefined,
87
+ disabled: select.disabled,
88
+ visible: isVisible(el),
89
+ });
90
+ });
91
+ // Textareas
92
+ document.querySelectorAll("textarea").forEach((el) => {
93
+ const textarea = el;
94
+ elements.push({
95
+ tag: "textarea",
96
+ text: getText(el),
97
+ placeholder: textarea.placeholder || undefined,
98
+ value: textarea.value || undefined,
99
+ disabled: textarea.disabled,
100
+ visible: isVisible(el),
101
+ });
102
+ });
103
+ // Checkboxes & radios
104
+ document.querySelectorAll("input[type='checkbox'], input[type='radio']").forEach((el) => {
105
+ const input = el;
106
+ elements.push({
107
+ tag: input.type,
108
+ text: getText(el),
109
+ value: input.checked ? "checked" : "unchecked",
110
+ disabled: input.disabled,
111
+ visible: isVisible(el),
112
+ });
113
+ });
114
+ // Modal detection
115
+ const modalSelectors = [
116
+ "[role='dialog']",
117
+ "[role='alertdialog']",
118
+ ".modal",
119
+ ".overlay",
120
+ "[aria-modal='true']",
121
+ ];
122
+ const hasModal = modalSelectors.some((s) => document.querySelector(s) !== null);
123
+ // Cookie banner detection
124
+ const cookieKeywords = ["cookie", "gdpr", "consent", "accept all"];
125
+ const bodyText = document.body.innerText.toLowerCase();
126
+ const hasCookieBanner = cookieKeywords.some((k) => bodyText.includes(k));
127
+ // Loading detection
128
+ const loadingSelectors = [
129
+ "[aria-busy='true']",
130
+ ".loading",
131
+ ".spinner",
132
+ "[role='progressbar']",
133
+ ];
134
+ const isLoading = loadingSelectors.some((s) => document.querySelector(s) !== null);
135
+ return {
136
+ url: window.location.href,
137
+ title: document.title,
138
+ elements,
139
+ hasModal,
140
+ hasCookieBanner,
141
+ isLoading,
142
+ };
143
+ });
144
+ // Assign IDs and build structured list
145
+ const counters = {};
146
+ const typedElements = [];
147
+ for (const raw of result.elements) {
148
+ const type = this.resolveType(raw.tag, raw.type);
149
+ counters[type] = (counters[type] ?? 0) + 1;
150
+ const id = `@${PREFIX[type]}${counters[type]}`;
151
+ typedElements.push({
152
+ id,
153
+ type,
154
+ text: raw.text || undefined,
155
+ placeholder: raw.placeholder,
156
+ href: raw.href,
157
+ value: raw.value,
158
+ disabled: raw.disabled,
159
+ visible: raw.visible,
160
+ });
161
+ }
162
+ const text = this.buildTextRepresentation(typedElements, result);
163
+ return {
164
+ url: result.url,
165
+ title: result.title,
166
+ elements: typedElements,
167
+ hasModal: result.hasModal,
168
+ hasCookieBanner: result.hasCookieBanner,
169
+ isLoading: result.isLoading,
170
+ text,
171
+ };
172
+ }
173
+ // Find an element by ID (@B3, @I1, etc.) and return its locator
174
+ async resolveElementId(id) {
175
+ const elements = await this.observe();
176
+ const el = elements.elements.find((e) => e.id === id);
177
+ if (!el)
178
+ return null;
179
+ // Get coordinates by re-querying the DOM
180
+ return await this.page.evaluate((targetText) => {
181
+ const all = document.querySelectorAll("button, input, a, select, textarea, [role='button']");
182
+ for (const el of Array.from(all)) {
183
+ const text = (el.textContent ?? "").replace(/\s+/g, " ").trim();
184
+ const aria = el.getAttribute("aria-label") ?? "";
185
+ if (text.includes(targetText) || aria.includes(targetText)) {
186
+ const rect = el.getBoundingClientRect();
187
+ return {
188
+ x: rect.left + rect.width / 2,
189
+ y: rect.top + rect.height / 2,
190
+ };
191
+ }
192
+ }
193
+ return null;
194
+ }, el.text ?? el.placeholder ?? "");
195
+ }
196
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
197
+ resolveType(tag, inputType) {
198
+ if (tag === "button")
199
+ return "button";
200
+ if (tag === "input") {
201
+ if (inputType === "checkbox")
202
+ return "checkbox";
203
+ if (inputType === "radio")
204
+ return "radio";
205
+ return "input";
206
+ }
207
+ if (tag === "link")
208
+ return "link";
209
+ if (tag === "select")
210
+ return "select";
211
+ if (tag === "textarea")
212
+ return "textarea";
213
+ if (tag === "checkbox")
214
+ return "checkbox";
215
+ if (tag === "radio")
216
+ return "radio";
217
+ return "other";
218
+ }
219
+ buildTextRepresentation(elements, meta) {
220
+ const lines = [
221
+ `PAGE: ${meta.title}`,
222
+ `URL: ${meta.url}`,
223
+ ];
224
+ if (meta.isLoading)
225
+ lines.push("[⏳ PAGE IS LOADING]");
226
+ if (meta.hasModal)
227
+ lines.push("[⚠️ MODAL IS OPEN — may block interactions]");
228
+ if (meta.hasCookieBanner)
229
+ lines.push("[🍪 COOKIE BANNER DETECTED — dismiss first]");
230
+ lines.push("", "INTERACTIVE ELEMENTS:");
231
+ const visible = elements.filter((e) => e.visible !== false);
232
+ const hidden = elements.filter((e) => e.visible === false);
233
+ for (const el of visible) {
234
+ let desc = ` ${el.id} [${el.type}]`;
235
+ if (el.text)
236
+ desc += ` "${el.text}"`;
237
+ if (el.placeholder)
238
+ desc += ` placeholder="${el.placeholder}"`;
239
+ if (el.value)
240
+ desc += ` value="${el.value}"`;
241
+ if (el.href)
242
+ desc += ` → ${el.href.slice(0, 80)}`;
243
+ if (el.disabled)
244
+ desc += " (disabled)";
245
+ lines.push(desc);
246
+ }
247
+ if (hidden.length > 0) {
248
+ lines.push(` ... and ${hidden.length} hidden elements`);
249
+ }
250
+ return lines.join("\n");
251
+ }
252
+ }
253
+ exports.Observer = Observer;
254
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Page, Browser } from "playwright";
2
+ import type { ObserveResult, CapturedRequest, ActResult, FindDataResult, Screenshot } from "./types";
3
+ import { AIClient } from "./ai";
4
+ export declare class Session {
5
+ private page;
6
+ private browser;
7
+ private interceptor;
8
+ private observer;
9
+ private ai;
10
+ private defaultTimeout;
11
+ constructor(page: Page, browser: Browser, ai: AIClient, timeout?: number);
12
+ goto(url: string): Promise<void>;
13
+ wait(ms: number): Promise<void>;
14
+ get url(): string;
15
+ observe(): Promise<ObserveResult>;
16
+ screenshot(): Promise<Screenshot>;
17
+ clickById(elementId: string): Promise<void>;
18
+ fillById(elementId: string, value: string): Promise<void>;
19
+ getCapturedRequests(): CapturedRequest[];
20
+ getUsefulRequests(minScore?: number): CapturedRequest[];
21
+ onRequest(handler: (req: CapturedRequest) => void): void;
22
+ act(goal: string, maxSteps?: number): Promise<ActResult>;
23
+ findData(goal: string, maxSteps?: number): Promise<FindDataResult>;
24
+ close(): Promise<void>;
25
+ closeAll(): Promise<void>;
26
+ private executeAction;
27
+ }
28
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,SAAS,EACT,cAAc,EAEd,UAAU,EACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,qBAAa,OAAO;IAClB,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,cAAc,CAAS;gBAG7B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,QAAQ,EACZ,OAAO,SAAS;IAiBZ,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,IAAI,GAAG,IAAI,MAAM,CAEhB;IAIK,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;IAMjC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAYjC,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO3C,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D,mBAAmB,IAAI,eAAe,EAAE;IAKxC,iBAAiB,CAAC,QAAQ,SAAI,GAAG,eAAe,EAAE;IAKlD,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAMlD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,OAAO,CAAC,SAAS,CAAC;IAgDpD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,OAAO,CAAC,cAAc,CAAC;IAmB9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAOjB,aAAa;CAoC5B"}
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Session = void 0;
4
+ const interceptor_1 = require("./interceptor");
5
+ const observer_1 = require("./observer");
6
+ class Session {
7
+ page;
8
+ browser;
9
+ interceptor;
10
+ observer;
11
+ ai;
12
+ defaultTimeout;
13
+ constructor(page, browser, ai, timeout = 30_000) {
14
+ this.page = page;
15
+ this.browser = browser;
16
+ this.ai = ai;
17
+ this.defaultTimeout = timeout;
18
+ this.interceptor = new interceptor_1.Interceptor(page);
19
+ this.observer = new observer_1.Observer(page);
20
+ // Start capturing traffic immediately — t=0
21
+ // This catches requests the site fires on load before we do anything
22
+ this.interceptor.start();
23
+ }
24
+ // ─── Navigation ───────────────────────────────────────────────────────────────
25
+ async goto(url) {
26
+ await this.page.goto(url, {
27
+ waitUntil: "domcontentloaded",
28
+ timeout: this.defaultTimeout,
29
+ });
30
+ }
31
+ async wait(ms) {
32
+ await this.page.waitForTimeout(ms);
33
+ }
34
+ get url() {
35
+ return this.page.url();
36
+ }
37
+ // ─── Observe — always first, before any action ────────────────────────────────
38
+ async observe() {
39
+ return this.observer.observe();
40
+ }
41
+ // ─── Screenshot — only when observe() is not enough ──────────────────────────
42
+ async screenshot() {
43
+ const buffer = await this.page.screenshot({ type: "jpeg", quality: 80 });
44
+ return {
45
+ base64: buffer.toString("base64"),
46
+ mimeType: "image/jpeg",
47
+ timestamp: Date.now(),
48
+ };
49
+ }
50
+ // ─── Interaction ──────────────────────────────────────────────────────────────
51
+ // Click by element ID from observe() (e.g. @B3)
52
+ async clickById(elementId) {
53
+ const coords = await this.observer.resolveElementId(elementId);
54
+ if (!coords)
55
+ throw new Error(`Element ${elementId} not found in DOM`);
56
+ await this.page.mouse.click(coords.x, coords.y);
57
+ }
58
+ // Fill input by element ID from observe()
59
+ async fillById(elementId, value) {
60
+ const coords = await this.observer.resolveElementId(elementId);
61
+ if (!coords)
62
+ throw new Error(`Element ${elementId} not found in DOM`);
63
+ await this.page.mouse.click(coords.x, coords.y);
64
+ await this.page.keyboard.press("Control+a");
65
+ await this.page.keyboard.type(value);
66
+ }
67
+ // ─── Captured requests ────────────────────────────────────────────────────────
68
+ // All captured requests sorted by relevance score
69
+ getCapturedRequests() {
70
+ return this.interceptor.getAll();
71
+ }
72
+ // Only useful XHR/fetch requests above score threshold
73
+ getUsefulRequests(minScore = 1) {
74
+ return this.interceptor.getUseful(minScore);
75
+ }
76
+ // Listen to requests in real-time as they come in
77
+ onRequest(handler) {
78
+ this.interceptor.on("request", handler);
79
+ }
80
+ // ─── act() — achieve a goal through multi-step navigation ────────────────────
81
+ async act(goal, maxSteps = 10) {
82
+ const steps = [];
83
+ const history = [];
84
+ for (let i = 0; i < maxSteps; i++) {
85
+ // ALWAYS observe first — no screenshot by default
86
+ const observation = await this.observe();
87
+ let action;
88
+ let usedScreenshot = false;
89
+ // If observe() gives us enough elements, use text-based decision
90
+ if (observation.elements.filter((e) => e.visible).length > 0) {
91
+ action = await this.ai.decideFromObservation(goal, observation, history);
92
+ }
93
+ else {
94
+ // Fallback: page has no readable elements (canvas, iframe, etc.) → screenshot
95
+ const shot = await this.screenshot();
96
+ action = await this.ai.decideFromScreenshot(goal, shot.base64, history);
97
+ usedScreenshot = true;
98
+ }
99
+ steps.push({ action, observeBefore: observation, usedScreenshot });
100
+ history.push(`Step ${i + 1}: ${action.type} — ${action.reason}`);
101
+ // Terminal states
102
+ if (action.type === "done") {
103
+ return { success: true, steps, finalUrl: this.url, reason: action.reason };
104
+ }
105
+ if (action.type === "impossible") {
106
+ return { success: false, steps, finalUrl: this.url, reason: action.reason };
107
+ }
108
+ // Execute action
109
+ await this.executeAction(action);
110
+ // Short wait after interactions for page to settle
111
+ await this.page.waitForTimeout(500);
112
+ }
113
+ return {
114
+ success: false,
115
+ steps,
116
+ finalUrl: this.url,
117
+ reason: `Reached max steps (${maxSteps}) without completing goal`,
118
+ };
119
+ }
120
+ // ─── findData() — navigate + capture requests until goal achieved ─────────────
121
+ async findData(goal, maxSteps = 15) {
122
+ // Clear previous captures for a clean run
123
+ this.interceptor.clear();
124
+ // Restart capture (was already running, but clear resets the map)
125
+ // Give the page a moment to emit any initial requests
126
+ await this.page.waitForTimeout(300);
127
+ const actResult = await this.act(goal, maxSteps);
128
+ const capturedRequests = this.interceptor.getUseful();
129
+ return {
130
+ ...actResult,
131
+ capturedRequests,
132
+ };
133
+ }
134
+ // ─── Cleanup ──────────────────────────────────────────────────────────────────
135
+ async close() {
136
+ this.interceptor.stop();
137
+ await this.page.close();
138
+ }
139
+ async closeAll() {
140
+ this.interceptor.stop();
141
+ await this.browser.close();
142
+ }
143
+ // ─── Execute action ───────────────────────────────────────────────────────────
144
+ async executeAction(action) {
145
+ switch (action.type) {
146
+ case "click":
147
+ await this.clickById(action.elementId).catch(async () => {
148
+ // If ID resolution fails, try clicking by text content
149
+ await this.page.getByText(action.elementId).first().click();
150
+ });
151
+ break;
152
+ case "fill":
153
+ await this.fillById(action.elementId, action.value).catch(async () => {
154
+ await this.page.getByRole("textbox").first().fill(action.value);
155
+ });
156
+ break;
157
+ case "scroll":
158
+ await this.page.evaluate(({ direction, amount }) => {
159
+ window.scrollBy(0, direction === "down" ? amount : -amount);
160
+ }, { direction: action.direction, amount: action.amount ?? 300 });
161
+ break;
162
+ case "wait":
163
+ await this.page.waitForTimeout(action.ms);
164
+ break;
165
+ case "screenshot":
166
+ // Already handled in act() loop — screenshot triggers AI vision fallback
167
+ break;
168
+ default:
169
+ break;
170
+ }
171
+ }
172
+ }
173
+ exports.Session = Session;
174
+ //# 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,104 @@
1
+ export type ElementType = "button" | "input" | "link" | "select" | "textarea" | "checkbox" | "radio" | "other";
2
+ export interface ObservedElement {
3
+ id: string;
4
+ type: ElementType;
5
+ text?: string;
6
+ placeholder?: string;
7
+ href?: string;
8
+ value?: string;
9
+ disabled?: boolean;
10
+ visible?: boolean;
11
+ }
12
+ export interface ObserveResult {
13
+ url: string;
14
+ title: string;
15
+ elements: ObservedElement[];
16
+ hasModal: boolean;
17
+ hasCookieBanner: boolean;
18
+ isLoading: boolean;
19
+ text: string;
20
+ }
21
+ export interface CapturedRequest {
22
+ id: string;
23
+ timestamp: number;
24
+ method: string;
25
+ url: string;
26
+ requestHeaders: Record<string, string>;
27
+ requestBody?: string;
28
+ status?: number;
29
+ responseHeaders?: Record<string, string>;
30
+ responseBody?: string;
31
+ contentType?: string;
32
+ isXHR: boolean;
33
+ isAsset: boolean;
34
+ score: number;
35
+ }
36
+ export type AgentAction = {
37
+ type: "click";
38
+ elementId: string;
39
+ reason: string;
40
+ } | {
41
+ type: "fill";
42
+ elementId: string;
43
+ value: string;
44
+ reason: string;
45
+ } | {
46
+ type: "scroll";
47
+ direction: "up" | "down";
48
+ amount?: number;
49
+ reason: string;
50
+ } | {
51
+ type: "wait";
52
+ ms: number;
53
+ reason: string;
54
+ } | {
55
+ type: "screenshot";
56
+ reason: string;
57
+ } | {
58
+ type: "done";
59
+ reason: string;
60
+ } | {
61
+ type: "impossible";
62
+ reason: string;
63
+ };
64
+ export interface ActResult {
65
+ success: boolean;
66
+ steps: Array<{
67
+ action: AgentAction;
68
+ observeBefore: ObserveResult;
69
+ usedScreenshot: boolean;
70
+ }>;
71
+ finalUrl: string;
72
+ reason: string;
73
+ }
74
+ export interface FindDataResult extends ActResult {
75
+ capturedRequests: CapturedRequest[];
76
+ }
77
+ export interface SessionConfig {
78
+ wsEndpoint: string;
79
+ proxy?: {
80
+ server: string;
81
+ username?: string;
82
+ password?: string;
83
+ };
84
+ profileId?: string;
85
+ timeout?: number;
86
+ viewport?: {
87
+ width: number;
88
+ height: number;
89
+ };
90
+ }
91
+ export interface AIConfig {
92
+ apiKey: string;
93
+ model?: string;
94
+ }
95
+ export interface BrowserConfig {
96
+ session: SessionConfig;
97
+ ai: AIConfig;
98
+ }
99
+ export interface Screenshot {
100
+ base64: string;
101
+ mimeType: "image/jpeg" | "image/png";
102
+ timestamp: number;
103
+ }
104
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // ─── Element types returned by observe() ─────────────────────────────────────
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,gFAAgF"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "zan-browser",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered cloud browser library with observe-first, screenshot-as-fallback pattern",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": [
16
+ "browser",
17
+ "automation",
18
+ "playwright",
19
+ "ai",
20
+ "claude",
21
+ "scraping",
22
+ "observe",
23
+ "cloud-browser"
24
+ ],
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "@anthropic-ai/sdk": "^0.39.0",
28
+ "playwright": "^1.49.0"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.7.2",
32
+ "@types/node": "^22.10.0"
33
+ }
34
+ }