@vpxa/aikit 0.1.317 → 0.1.319

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vpxa/aikit",
3
- "version": "0.1.317",
3
+ "version": "0.1.319",
4
4
  "type": "module",
5
5
  "description": "Local-first AI developer toolkit — knowledge base, code analysis, context management, and developer tools for LLM agents",
6
6
  "license": "MIT",
@@ -1,20 +1,46 @@
1
- import { BrowserContext, Page } from "playwright-core";
2
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
2
 
4
3
  //#region packages/browser/src/types.d.ts
5
4
  type BrowserMode = 'ui' | 'headless';
5
+ type AgentBrowserEngine = 'chrome' | 'lightpanda';
6
6
  interface BrowserConfig {
7
7
  defaultMode: BrowserMode;
8
+ /** Directory where agent-browser binary + Chrome will be cached */
8
9
  browsersPath: string | null;
10
+ /** User data directory for persistent browser profiles */
9
11
  userDataDirRoot: string | null;
12
+ /** Minutes of inactivity before the browser session is closed */
10
13
  idleShutdownMinutes: number;
14
+ /** Allow internal browser schemes (file:, chrome:, etc.) */
11
15
  allowInternalSchemes: boolean;
16
+ /** Allow loopback/localhost addresses (default: false for security) */
12
17
  allowLoopback?: boolean;
18
+ /** Timeout for eval operations in ms */
13
19
  evalTimeoutMs: number;
20
+ /** Max bytes for eval results before truncation */
14
21
  evalMaxResultBytes: number;
22
+ /** Mask password fields in screenshots */
15
23
  redactPasswordFieldsInScreenshots: boolean;
24
+ /** Browser engine: 'chrome' (default) or 'lightpanda' */
25
+ engine?: AgentBrowserEngine;
26
+ /** Proxy server URL */
27
+ proxy?: string;
28
+ /** Viewport size "width,height" */
29
+ viewport?: string;
16
30
  }
17
31
  declare const DEFAULT_BROWSER_CONFIG: BrowserConfig;
32
+ interface TabInfo {
33
+ tabId: string;
34
+ url: string;
35
+ title: string;
36
+ label?: string;
37
+ snapshot?: string;
38
+ createdAt: Date;
39
+ }
40
+ /**
41
+ * Replaces the old PageInfo. agent-browser uses stable tab IDs (t0, t1, t2...).
42
+ * We alias them through the PageInfo shape for backward compat.
43
+ */
18
44
  interface PageInfo {
19
45
  pageId: string;
20
46
  url: string;
@@ -32,15 +58,61 @@ interface EvalValidationResult {
32
58
  truncated?: boolean;
33
59
  reason?: string;
34
60
  }
61
+ /**
62
+ * Result from an agent-browser CLI command execution.
63
+ */
64
+ interface AgentBrowserResult {
65
+ stdout: string;
66
+ stderr: string;
67
+ exitCode: number;
68
+ }
35
69
  //#endregion
36
70
  //#region packages/browser/src/session.d.ts
71
+ /**
72
+ * TabRegistry manages agent-browser's stable tab IDs (t0, t1, t2...).
73
+ *
74
+ * agent-browser assigns stable string IDs to tabs that don't shift on close.
75
+ * We track tab metadata alongside these IDs.
76
+ */
77
+ declare class TabRegistry {
78
+ private tabs;
79
+ private labelToId;
80
+ /**
81
+ * Register a tab and return its ID.
82
+ */
83
+ register(tabId: string, url: string, title?: string, label?: string): string;
84
+ /**
85
+ * Resolve a pageId or label to a tab ID.
86
+ * If it's a valid tab ID, returns it as-is.
87
+ * If it's a registered label, returns the tab ID.
88
+ * Otherwise passes through (agent-browser also supports labels natively).
89
+ */
90
+ resolve(pageIdOrLabel: string): string;
91
+ getTab(tabId: string): TabInfo;
92
+ updateTabInfo(tabId: string, url: string, title: string): void;
93
+ setSnapshot(tabId: string, snapshot: string): void;
94
+ getSnapshot(tabId: string): string | undefined;
95
+ removeTab(tabId: string): Promise<void>;
96
+ listTabs(): TabInfo[];
97
+ /**
98
+ * Return tabs as PageInfo array for backward compatibility with
99
+ * the old SessionRegistry API.
100
+ */
101
+ listPages(): PageInfo[];
102
+ closeAll(): Promise<void>;
103
+ clear(): void;
104
+ get size(): number;
105
+ }
106
+ /**
107
+ * Legacy SessionRegistry re-exported for backward compat.
108
+ * Uses the new TabRegistry internally.
109
+ */
37
110
  declare class SessionRegistry {
38
- private pages;
39
- registerPage(page: Page, url: string, title: string, label?: string): string;
40
- getPageByLabel(label: string): Page;
41
- resolvePageId(pageIdOrLabel: string): string;
42
- getPage(pageId: string): Page;
111
+ private tabs;
112
+ registerPage(pageId: string, url: string, title: string, label?: string): string;
113
+ getPage(_pageId: string): never;
43
114
  getPageInfo(pageId: string): PageInfo;
115
+ resolvePageId(pageIdOrLabel: string): string;
44
116
  setSnapshot(pageId: string, snapshot: string): void;
45
117
  getSnapshot(pageId: string): string | undefined;
46
118
  updatePageInfo(pageId: string, url: string, title: string): void;
@@ -51,23 +123,73 @@ declare class SessionRegistry {
51
123
  }
52
124
  //#endregion
53
125
  //#region packages/browser/src/engine.d.ts
126
+ /**
127
+ * BrowserEngine manages the agent-browser runtime.
128
+ *
129
+ * Rather than running a long-lived daemon process ourselves, we use the
130
+ * per-command CLI approach: each call to exec() spawns agent-browser <command>.
131
+ * agent-browser's own daemon auto-starts on the first call and persists
132
+ * between commands — so subsequent calls are fast (~5ms overhead).
133
+ *
134
+ * Tab tracking is done via the TabRegistry which maps agent-browser's
135
+ * stable tab IDs (t0, t1, t2...) to page metadata.
136
+ */
54
137
  declare class BrowserEngine {
55
- private browser;
56
- private context;
138
+ private binaryPath;
139
+ private _currentMode;
140
+ private _isLaunched;
57
141
  private idleTimer;
58
- private launchPromise;
59
- private currentMode;
60
- readonly session: SessionRegistry;
142
+ readonly tabs: TabRegistry;
61
143
  private config;
62
144
  constructor(config?: Partial<BrowserConfig>);
145
+ get currentMode(): BrowserMode | null;
63
146
  launch(mode?: BrowserMode, onProgress?: (msg: string) => void): Promise<void>;
64
- private doLaunch;
65
- private stopIdleTimer;
66
- resetIdleTimer(): void;
147
+ /**
148
+ * Execute a single agent-browser CLI command.
149
+ * Returns stdout/stderr/exitCode.
150
+ */
151
+ exec(command: string, ...args: string[]): Promise<AgentBrowserResult>;
152
+ /**
153
+ * Execute a command scoped to a specific tab.
154
+ * Prepends "tab <tabId>" to the command automatically.
155
+ */
156
+ tabExec(tabId: string, command: string, ...args: string[]): Promise<AgentBrowserResult>;
157
+ /**
158
+ * Open a URL in a new tab and register it.
159
+ * Returns the tab ID (t0, t1, t2...).
160
+ */
161
+ open(url: string, mode?: BrowserMode, label?: string): Promise<string>;
162
+ /**
163
+ * Take a screenshot of a tab and return the base64-encoded PNG.
164
+ */
165
+ screenshot(tabId: string, options?: {
166
+ fullPage?: boolean;
167
+ format?: 'png' | 'jpeg';
168
+ quality?: number;
169
+ selector?: string;
170
+ }): Promise<{
171
+ base64: string;
172
+ mimeType: string;
173
+ bytes: number;
174
+ }>;
175
+ /**
176
+ * Read the accessibility snapshot of a tab.
177
+ */
178
+ readSnapshot(tabId: string, compact?: boolean): Promise<string>;
179
+ navigate(tabId: string, url: string): Promise<AgentBrowserResult>;
180
+ /**
181
+ * Close the engine and all browser tabs.
182
+ */
67
183
  close(): Promise<void>;
68
- getContext(): BrowserContext;
69
- getConfig(): BrowserConfig;
70
184
  isLaunched(): boolean;
185
+ getConfig(): BrowserConfig;
186
+ /**
187
+ * Resolve a pageId (or label) to a tab ID.
188
+ * Accepts both agent-browser tab IDs (t0, t1) and named labels.
189
+ */
190
+ resolvePageId(pageIdOrLabel: string): string;
191
+ resetIdleTimer(): void;
192
+ private stopIdleTimer;
71
193
  }
72
194
  declare function getEngine(config?: Partial<BrowserConfig>): BrowserEngine;
73
195
  declare function closeEngine(): Promise<void>;
@@ -75,25 +197,56 @@ declare function closeEngine(): Promise<void>;
75
197
  //#region packages/browser/src/install.d.ts
76
198
  declare function getDefaultBrowsersPath(): string;
77
199
  declare function resolveBrowsersPath(config: BrowserConfig): string;
78
- declare function isBrowserInstalled(browsersPath: string): boolean;
79
- declare function ensureBrowserInstalled(config: BrowserConfig, onProgress?: (msg: string) => void): Promise<string>;
200
+ /**
201
+ * Ensure agent-browser binary is available. If not found globally, download it
202
+ * via npx and cache the binary path. Returns the path to the agent-browser binary.
203
+ */
204
+ declare function ensureAgentBrowserInstalled(config: BrowserConfig, onProgress?: (msg: string) => void): Promise<string>;
205
+ /**
206
+ * Show the current agent-browser version.
207
+ */
208
+ declare function getAgentBrowserVersion(binaryPath: string): Promise<string | null>;
209
+ /**
210
+ * Check if the browser runtime is ready (binary exists + Chrome installed).
211
+ */
212
+ declare function isBrowserReady(config: BrowserConfig): boolean;
80
213
  //#endregion
81
214
  //#region packages/browser/src/modes.d.ts
82
- interface LaunchArgs {
215
+ declare function autoSelectMode(mode: BrowserMode | string): BrowserMode;
216
+ /**
217
+ * Get CLI arguments to pass to agent-browser daemon for the given mode.
218
+ * agent-browser handles headless vs headed via its internal daemon config.
219
+ * We just signal which mode we want.
220
+ */
221
+ declare function getDaemonArgs(mode: BrowserMode): string[];
222
+ /**
223
+ * Get the launch args to pass when opening a page.
224
+ */
225
+ declare function getLaunchArgs(mode: BrowserMode): {
83
226
  headless: boolean;
84
227
  args: string[];
85
- }
86
- declare function detectDisplayAvailable(): boolean;
87
- declare function autoSelectMode(requested: BrowserMode): BrowserMode;
88
- declare function getLaunchArgs(mode: BrowserMode): LaunchArgs;
228
+ };
89
229
  //#endregion
90
230
  //#region packages/browser/src/security.d.ts
91
- declare function isUrlAllowed(url: string, config: BrowserConfig): SecurityCheckResult;
92
- declare function isCookieAccessAllowed(confirm: boolean): SecurityCheckResult;
93
- declare function validateEvalResult(result: unknown, maxBytes: number): EvalValidationResult;
94
- declare function redactPasswordFields(html: string): string;
231
+ /**
232
+ * Check whether the given URL is allowed to be navigated to.
233
+ * Blocks dangerous schemes and cloud metadata endpoints.
234
+ *
235
+ * Same security policy as before — only the scheme check mechanism changes
236
+ * (agent-browser receives the URL after we validate it, rather than Playwright).
237
+ */
238
+ declare function isUrlAllowed(url: string, _config?: BrowserConfig): SecurityCheckResult;
239
+ /**
240
+ * Validate eval result size and optionally truncate.
241
+ */
242
+ declare function validateEvalResult(result: string, maxBytes: number): {
243
+ valid: boolean;
244
+ result: string;
245
+ truncated: boolean;
246
+ reason?: string;
247
+ };
95
248
  //#endregion
96
249
  //#region packages/browser/src/tools/index.d.ts
97
250
  declare function registerBrowserTools(server: McpServer, config: Record<string, unknown>): void;
98
251
  //#endregion
99
- export { BrowserConfig, BrowserEngine, BrowserMode, DEFAULT_BROWSER_CONFIG, EvalValidationResult, LaunchArgs, PageInfo, SecurityCheckResult, SessionRegistry, autoSelectMode, closeEngine, detectDisplayAvailable, ensureBrowserInstalled, getDefaultBrowsersPath, getEngine, getLaunchArgs, isBrowserInstalled, isCookieAccessAllowed, isUrlAllowed, redactPasswordFields, registerBrowserTools, resolveBrowsersPath, validateEvalResult };
252
+ export { AgentBrowserEngine, AgentBrowserResult, BrowserConfig, BrowserEngine, BrowserMode, DEFAULT_BROWSER_CONFIG, EvalValidationResult, PageInfo, SecurityCheckResult, SessionRegistry, TabInfo, TabRegistry, autoSelectMode, closeEngine, ensureAgentBrowserInstalled, getAgentBrowserVersion, getDaemonArgs, getDefaultBrowsersPath, getEngine, getLaunchArgs, isBrowserReady, isUrlAllowed, registerBrowserTools, resolveBrowsersPath, validateEvalResult };
@@ -1,32 +1,11 @@
1
- import{createRequire as e}from"node:module";import{createHash as t,randomUUID as n}from"node:crypto";import{homedir as r,platform as i}from"node:os";import{join as a}from"node:path";import{execFileSync as o}from"node:child_process";import{existsSync as s,readdirSync as c}from"node:fs";import{Buffer as l}from"node:buffer";import{z as u}from"zod";import d from"node:dns/promises";var f=class{#e;#t;#n=0;#r=0;constructor(e=200){if(!Number.isInteger(e)||e<1)throw RangeError(`CircularBuffer capacity must be a positive integer.`);this.#e=e,this.#t=Array(e)}push(e){this.#t[this.#n]=e,this.#n=(this.#n+1)%this.#e,this.#r<this.#e&&(this.#r+=1)}toArray(){if(this.#r===0)return[];let e=(this.#n-this.#r+this.#e)%this.#e,t=[];for(let n=0;n<this.#r;n+=1)t.push(this.#t[(e+n)%this.#e]);return t}clear(){this.#t=Array(this.#e),this.#n=0,this.#r=0}get size(){return this.#r}get capacity(){return this.#e}isEmpty(){return this.#r===0}};const p=[`xhr`,`fetch`,`websocket`,`document`],m=[/^authorization$/i,/^cookie$/i,/^set-cookie$/i,/^x-api-key$/i,/^x-access-token$/i,/^x-auth-token$/i,/^x-session$/i,/^x-secret$/i,/^x-password$/i,/^proxy-authorization$/i,/^www-authenticate$/i,/^x-csrf-token$/i,/^x-xsrf-token$/i];function h(e,t){if(t?.showSensitive)return{...e};let n=[...m,...t?.additionalPatterns??[]],r={};for(let[t,i]of Object.entries(e))r[t]=n.some(e=>e.test(t))?`[REDACTED]`:i;return r}function g(e,t=512){if(e)return e.length<=t?e:`${e.slice(0,t)}...[truncated]`}function ee(){return{networkBuffer:new f(200),consoleBuffer:new f(1e3),networkEnabled:!1,consoleEnabled:!1,networkFilter:{},nextRequestSequence:0,pendingRequests:new Map}}function _(e,t){if(e.capacity===t)return e;let n=new f(t),r=e.toArray(),i=Math.max(0,r.length-t);for(let e of r.slice(i))n.push(e);return n}function te(e){switch(e){case`error`:case`info`:case`log`:case`debug`:return e;case`warning`:return`warn`;default:return`log`}}function ne(e){if(e)return Buffer.byteLength(e)<=5242880?g(e):void 0}function re(e){if(!e)return{};let t=Buffer.byteLength(e);return{bodySize:t,bodyPreview:t<=5242880?g(e):void 0}}function ie(e,t){return!t.resourceTypes.has(e.resourceType)||t.urlPattern&&!t.urlPattern.test(e.url)?!1:!t.excludeUrls.some(t=>t.test(e.url))}function ae(e){if(e.length>200)throw Error(`Network filter regex patterns must be 200 characters or fewer`);try{let t=new RegExp(e);return t.test(`a`.repeat(100)),t}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Invalid network filter regex pattern: ${t}`)}}function oe(e={}){return{resourceTypes:new Set(e.resourceTypes??p),urlPattern:e.urlPattern?ae(e.urlPattern):void 0,excludeUrls:(e.excludeUrls??[]).map(e=>ae(e))}}function se(e){return e.nextRequestSequence+=1,`req-${e.nextRequestSequence}`}function ce(e,t,n){let r={request:t,previous:e.pendingRequestTail};e.pendingRequestTail?e.pendingRequestTail.next=r:e.pendingRequestHead=r,e.pendingRequestTail=r,e.pendingRequests.set(t,{entry:n,node:r})}function le(e,t){t.previous?t.previous.next=t.next:e.pendingRequestHead=t.next,t.next?t.next.previous=t.previous:e.pendingRequestTail=t.previous,delete t.previous,delete t.next}function ue(e){for(;e.pendingRequests.size>e.networkBuffer.capacity;){let t=e.pendingRequestHead;if(!t)return;le(e,t),e.pendingRequests.delete(t.request)}}function v(e){e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0}var de=class{states=new Map;getOrCreate(e){let t=this.states.get(e);if(t)return t;let n=ee();return this.states.set(e,n),n}get(e){return this.states.get(e)}remove(e){let t=this.states.get(e);t&&(t.networkEnabled=!1,t.consoleEnabled=!1,this.detachNetworkListeners(t),this.detachConsoleListeners(t),v(t),this.states.delete(e))}clear(){for(let e of this.states.values())e.networkEnabled=!1,e.consoleEnabled=!1,this.detachNetworkListeners(e),this.detachConsoleListeners(e),v(e);this.states.clear()}enableNetwork(e,t,n,r){let i=this.getOrCreate(e);i.networkEnabled&&this.disableNetwork(e,t),i.page=t,i.networkEnabled=!0,i.networkFilter=n?{...n}:{},i.compiledNetworkFilter=oe(i.networkFilter),r?.bufferSize&&(i.networkBuffer=_(i.networkBuffer,r.bufferSize));let a=e=>{let t=i.compiledNetworkFilter??oe(i.networkFilter),n={resourceType:e.resourceType(),url:e.url()};if(!ie(n,t))return;let a={...e.headers()},o=Date.now(),s={id:se(i),url:n.url,method:e.method(),resourceType:n.resourceType,headers:r?.showSensitive?a:h(a),postData:r?.includeBody?ne(e.postData()??void 0):void 0,timestamp:o};i.networkBuffer.push(s),ce(i,e,s),ue(i)},o=async e=>{let t=e.request(),n=i.pendingRequests.get(t);if(!n)return;i.pendingRequests.delete(t),le(i,n.node);let a;if(r?.includeBody)try{a=re(await e.text())}catch{a=void 0}let o={...e.headers()};n.entry.response={status:e.status(),statusText:e.statusText(),headers:r?.showSensitive?o:h(o),bodySize:a?.bodySize,bodyPreview:a?.bodyPreview,timing:Date.now()-n.entry.timestamp}};i.listenerRefs={...i.listenerRefs,requestHandler:a,responseHandler:o},this.ensurePageCloseHandler(e,i,t),t.on(`request`,a),t.on(`response`,o)}disableNetwork(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.networkEnabled=!1,this.detachNetworkListeners(n),v(n))}enableConsole(e,t,n){let r=this.getOrCreate(e);r.consoleEnabled&&this.disableConsole(e,t),r.page=t,r.consoleEnabled=!0,n?.bufferSize&&(r.consoleBuffer=_(r.consoleBuffer,n.bufferSize));let i=e=>{let t=e.location();r.consoleBuffer.push({level:te(e.type()),text:e.text(),timestamp:Date.now(),location:t.url?{url:t.url,line:t.lineNumber,column:t.columnNumber}:void 0,args:e.args().map(e=>e.toString())})};r.listenerRefs={...r.listenerRefs,consoleHandler:i},this.ensurePageCloseHandler(e,r,t),t.on(`console`,i)}disableConsole(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.consoleEnabled=!1,this.detachConsoleListeners(n))}detachNetworkListeners(e){let t=e.page,n=e.listenerRefs?.requestHandler,r=e.listenerRefs?.responseHandler;t&&n&&t.off(`request`,n),t&&r&&t.off(`response`,r),e.listenerRefs&&(delete e.listenerRefs.requestHandler,delete e.listenerRefs.responseHandler),this.detachPageCloseHandlerIfUnused(e)}detachConsoleListeners(e){let t=e.page,n=e.listenerRefs?.consoleHandler;t&&n&&t.off(`console`,n),e.listenerRefs&&delete e.listenerRefs.consoleHandler,this.detachPageCloseHandlerIfUnused(e)}ensurePageCloseHandler(e,t,n){if(t.listenerRefs?.pageCloseHandler)return;let r=()=>{this.remove(e)};t.listenerRefs={...t.listenerRefs,pageCloseHandler:r},n.on(`close`,r)}detachPageCloseHandlerIfUnused(e){if(e.networkEnabled||e.consoleEnabled)return;let t=e.page,n=e.listenerRefs?.pageCloseHandler;t&&n&&t.off(`close`,n),e.listenerRefs&&delete e.listenerRefs.pageCloseHandler,e.listenerRefs&&!e.listenerRefs.requestHandler&&!e.listenerRefs.responseHandler&&!e.listenerRefs.consoleHandler&&!e.listenerRefs.pageCloseHandler&&delete e.listenerRefs}};let y=null;function b(){return y||=new de,y}const fe=e(import.meta.url);function pe(e){return typeof e==`string`?e.trim():e instanceof Buffer?e.toString(`utf8`).trim():``}function x(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e}async function S(e){x(e);let{chromium:t}=await import(`playwright-core`),n=t.executablePath();if(!n)throw Error(`Chromium executable not found in ${e}`);return n}function C(){return a(r(),`.aikit`,`browsers`)}function w(e){return e.browsersPath??process.env.PLAYWRIGHT_BROWSERS_PATH??C()}function T(e){return s(e)?c(e,{withFileTypes:!0}).some(e=>e.isDirectory()&&e.name.toLowerCase().startsWith(`chromium-`)):!1}async function E(e,t){let n=w(e);if(x(n),t?.(`Using Chromium cache at ${n}`),T(n))return t?.(`Chromium already installed`),S(n);let r=a(fe.resolve(`playwright-core`),`..`,`cli.js`);t?.(`Installing Chromium via playwright-core`);try{o(process.execPath,[r,`install`,`chromium`],{env:{...process.env,PLAYWRIGHT_BROWSERS_PATH:n},encoding:`utf8`,stdio:`pipe`})}catch(e){let t=e,n=pe(t.stderr),r=pe(t.stdout),i=n||r||t.message||`Unknown playwright install failure`;throw Error(`Failed to install Chromium: ${i}`)}return t?.(`Chromium install complete`),S(n)}function me(){return process.platform===`win32`||process.platform===`darwin`?!0:!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY)}function he(e){return e===`headless`||!me()?`headless`:e}function ge(e){switch(e){case`headless`:return{headless:!0,args:[]};default:return{headless:!1,args:[]}}}var _e=class{pages=new Map;registerPage(e,t,r,i){let a=n();return this.pages.set(a,{page:e,url:t,title:r,label:i,createdAt:new Date}),a}getPageByLabel(e){for(let t of this.pages.values())if(t.label===e)return t.page;throw Error(`Page not found: ${e}`)}resolvePageId(e){if(this.pages.has(e))return e;for(let[t,n]of this.pages.entries())if(n.label===e)return t;throw Error(`Page not found: ${e}`)}getPage(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.page}getPageInfo(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return{pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}}setSnapshot(e,t){let n=this.pages.get(e);if(!n)throw Error(`Page not found: ${e}`);this.pages.set(e,{...n,lastSnapshot:t})}getSnapshot(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.lastSnapshot}updatePageInfo(e,t,n){let r=this.pages.get(e);if(!r)throw Error(`Page not found: ${e}`);this.pages.set(e,{...r,url:t,title:n})}async removePage(e){let t=this.pages.get(e);t&&(await t.page.close(),this.pages.delete(e))}listPages(){return[...this.pages.entries()].map(([e,t])=>({pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}))}async closeAll(){await Promise.allSettled([...this.pages.keys()].map(async e=>this.removePage(e)))}get size(){return this.pages.size}};const D={defaultMode:`ui`,browsersPath:null,userDataDirRoot:null,idleShutdownMinutes:10,allowInternalSchemes:!1,allowLoopback:!1,evalTimeoutMs:1e4,evalMaxResultBytes:262144,redactPasswordFieldsInScreenshots:!0};var O=class{browser=null;context=null;idleTimer=null;launchPromise=null;currentMode=null;session=new _e;config;constructor(e={}){this.config={...D,...e}}async launch(e,t){let n=he(e??this.config.defaultMode);if(this.context&&this.currentMode!==n)if(this.currentMode===`headless`&&n===`ui`)await this.close();else return;if(!this.context){if(this.launchPromise)return this.launchPromise;this.launchPromise=this.doLaunch(n,t);try{await this.launchPromise}finally{this.launchPromise=null}}}async doLaunch(e,n){if(this.browser&&this.context)return;let o=w(this.config);process.env.PLAYWRIGHT_BROWSERS_PATH=o;let{chromium:s}=await import(`playwright-core`),c=await E(this.config,n);if(!c)throw Error(`Chromium executable not found. Install with: npx playwright install chromium
2
- Or set AIKIT_BROWSER_PATH to an existing Chromium installation.`);let l=ge(e);i()===`win32`&&!l.args.includes(`--no-sandbox`)&&l.args.push(`--no-sandbox`),i()===`win32`&&!l.args.includes(`--disable-gpu`)&&l.args.push(`--disable-gpu`);let u=t(`sha256`).update(process.cwd()).digest(`hex`).slice(0,12),d=a(this.config.userDataDirRoot??a(r(),`.aikit`,`profiles`),u);if(n?.(`Launching Chromium in ${e} mode`),this.context=await s.launchPersistentContext(d,{headless:l.headless,args:l.args,executablePath:c,viewport:null}),this.browser=this.context.browser(),!this.browser)throw await this.context.close(),this.context=null,Error(`Failed to acquire Chromium browser instance`);this.currentMode=e,this.browser.on(`disconnected`,()=>{this.browser=null,this.context=null,this.currentMode=null,b().clear(),this.session.closeAll().catch(()=>{}),this.stopIdleTimer()}),this.resetIdleTimer()}stopIdleTimer(){this.idleTimer&&=(clearTimeout(this.idleTimer),null)}resetIdleTimer(){this.stopIdleTimer();let e=setTimeout(()=>{this.close()},this.config.idleShutdownMinutes*6e4);e.unref?.(),this.idleTimer=e}async close(){this.stopIdleTimer(),this.launchPromise=null;let e=this.context,t=this.browser;this.context=null,this.browser=null,this.currentMode=null;let n;try{b().clear()}catch(e){n=e}try{await this.session.closeAll()}catch(e){n??=e}try{e&&await e.close()}catch(e){n??=e}finally{try{t&&await t.close()}catch(e){n??=e}}if(n)throw n}getContext(){if(!this.context)throw Error(`Browser not launched. Call launch() first.`);return this.context}getConfig(){return this.config}isLaunched(){return this.browser!==null}};let k=null;function A(e){return k||=new O(e),k}async function ve(){k&&=(await k.close(),null)}const ye=[`file:`,`chrome:`,`chrome-extension:`,`data:`,`javascript:`],be=[`169.254.169.254`,`metadata.google.internal`,`metadata.google.com`],j=[`localhost`,`127.0.0.1`,`::1`,`[::1]`];function xe(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function Se(e){let t=new WeakSet,n=JSON.stringify(e,(e,n)=>{if(typeof n==`bigint`)return n.toString();if(typeof n==`function`)return`[Function ${n.name||`anonymous`}]`;if(typeof n==`symbol`)return n.toString();if(n instanceof Error)return{name:n.name,message:n.message,stack:n.stack};if(typeof n==`object`&&n){if(t.has(n))return`[Circular]`;t.add(n)}return n});return n===void 0?e===void 0?`undefined`:String(e):n}function Ce(e,t){let n=l.from(e,`utf8`);return n.byteLength<=t?e:n.subarray(0,t).toString(`utf8`)}function M(e,t){let n;try{n=new URL(e)}catch{return{allowed:!1,reason:`Invalid URL: ${e}`}}let r=n.protocol.toLowerCase();if(ye.includes(r)&&!t.allowInternalSchemes)return{allowed:!1,reason:`Blocked URL scheme: ${r}`};let i=xe(n.hostname);return be.includes(i)?{allowed:!1,reason:`Blocked host: ${n.hostname}`}:j.includes(i)||j.includes(n.hostname)?t.allowLoopback?{allowed:!0}:{allowed:!1,reason:`Loopback addresses are blocked by default. Set allowLoopback: true to enable.`}:{allowed:!0}}function N(e){return e?{allowed:!0}:{allowed:!1,reason:`Cookie access requires explicit confirmation (confirm: true)`}}function P(e,t){if(t<=0)return{valid:!1,reason:`maxBytes must be greater than 0`};try{let n=Se(e);return l.byteLength(n,`utf8`)<=t?{valid:!0,result:n,truncated:!1}:{valid:!0,result:`${Ce(n,Math.max(t-3,0))}...`,truncated:!0,reason:`Result exceeded ${t} bytes and was truncated`}}catch(e){return{valid:!1,reason:`Unable to serialize eval result: ${e instanceof Error?e.message:String(e)}`}}}function we(e){return e.replace(/<input\b[^>]*>/gi,e=>/\btype\s*=\s*(["'])password\1/i.test(e)?/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i.test(e)?e.replace(/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`value="***"`):e.replace(/\s*\/?>$/,e=>` value="***"${e}`):e)}const Te=[`ui`,`headless`];function Ee(e){return typeof e==`object`&&e?e:null}function F(e){return typeof e==`string`&&e.length>0?e:void 0}function I(e){return typeof e==`boolean`?e:void 0}function L(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function R(e){return typeof e==`string`&&Te.includes(e)?e:void 0}function z(e){let t=process.env[e];if(!t)return;let n=Number(t);return Number.isFinite(n)?n:void 0}function De(e){let t=Ee(e.browser)??{};return{...D,defaultMode:R(process.env.AIKIT_BROWSER_DEFAULT_MODE)??R(t.defaultMode)??D.defaultMode,browsersPath:F(process.env.AIKIT_BROWSER_PATH)??F(process.env.AIKIT_BROWSER_BROWSERS_PATH)??F(t.browsersPath)??D.browsersPath,userDataDirRoot:F(t.userDataDirRoot)??D.userDataDirRoot,idleShutdownMinutes:z(`AIKIT_BROWSER_IDLE_MINUTES`)??L(t.idleShutdownMinutes)??D.idleShutdownMinutes,allowInternalSchemes:I(t.allowInternalSchemes)??D.allowInternalSchemes,allowLoopback:I(t.allowLoopback)??D.allowLoopback,evalTimeoutMs:z(`AIKIT_BROWSER_EVAL_TIMEOUT_MS`)??L(t.evalTimeoutMs)??D.evalTimeoutMs,evalMaxResultBytes:L(t.evalMaxResultBytes)??D.evalMaxResultBytes,redactPasswordFieldsInScreenshots:I(t.redactPasswordFieldsInScreenshots)??D.redactPasswordFieldsInScreenshots}}function B(e,t){return{content:[{type:`text`,text:e}],structuredContent:t}}function V(e,t){let n=e.selector??e.ref??(e.element?`text=${e.element}`:void 0);if(!n)throw Error(`${t} requires selector, ref, or element`);return n}async function Oe(e,t,n){let r=null;try{return await Promise.race([e,new Promise((e,i)=>{r=setTimeout(()=>{i(Error(`${n} timed out after ${t}ms`))},t)})])}finally{r&&clearTimeout(r)}}u.string().describe(`Tracked browser page identifier`),u.enum([`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`]).describe(`Interaction kind: click, type, press, hover, drag, select, scroll, upload`),u.string().optional().describe(`Target ref alias`),u.string().optional().describe(`Playwright selector`),u.string().optional().describe(`Human-readable element label`),u.string().optional().describe(`Text to type`),u.string().optional().describe(`Key to press`),u.string().optional().describe(`Option value, drag target, scroll spec, or file path(s)`),u.string().optional().describe(`Drag source ref`),u.string().optional().describe(`Drag source selector`),u.string().optional().describe(`Drag target ref`),u.string().optional().describe(`Drag target selector`);function ke(e){return async({pageId:t,kind:n,ref:r,selector:i,element:a,text:o,key:s,value:c,fromRef:l,fromSelector:u,toRef:d,toSelector:f})=>{let p=A(e),m=p.session.getPage(t);switch(n){case`click`:{let e=V({ref:r,selector:i,element:a},`browser_act(click)`);await m.click(e);break}case`type`:{let e=V({ref:r,selector:i,element:a},`browser_act(type)`);await m.fill(e,o??``);break}case`press`:{let e=V({ref:r,selector:i,element:a},`browser_act(press)`);if(!s)throw Error(`browser_act(press) requires key`);await m.press(e,s);break}case`hover`:{let e=V({ref:r,selector:i,element:a},`browser_act(hover)`);await m.hover(e);break}case`drag`:{let e=V({ref:l??r,selector:u??i,element:a},`browser_act(drag) source`),t=V({ref:d,selector:f??c,element:a},`browser_act(drag) target`);await m.dragAndDrop(e,t);break}case`select`:{let e=V({ref:r,selector:i,element:a},`browser_act(select)`);if(c===void 0)throw Error(`browser_act(select) requires value`);await m.selectOption(e,c);break}case`scroll`:{if(i||r||a){let e=V({ref:r,selector:i,element:a},`browser_act(scroll)`);await m.locator(e).scrollIntoViewIfNeeded();break}let e=c??`down 500`;await m.evaluate(e=>{if(e===`bottom`){window.scrollTo(0,document.body.scrollHeight);return}if(e===`top`){window.scrollTo(0,0);return}let t=e.split(` `),n=t[0]||`down`,r=Number.parseInt(t[1]||`500`,10),i=n===`left`?-r:n===`right`?r:0,a=n===`up`?-r:n===`down`?r:0;window.scrollBy(i,a)},e);break}case`upload`:{let e=V({ref:r,selector:i,element:a},`browser_act(upload)`);if(!c)throw Error(`value (file path or JSON array of paths) required for upload`);let t;try{let e=JSON.parse(c);t=Array.isArray(e)?e:[c]}catch{t=[c]}await m.locator(e).setInputFiles(t);break}}return p.resetIdleTimer(),B(`ok`,{ok:!0})}}function Ae(e,t){return t?e.filter(e=>e.level===t):e}function je(e){return e.map(e=>{let t=e.location?` (${e.location.url}:${e.location.line})`:``;return`[${e.level.toUpperCase()}] ${e.text}${t}`}).join(`
3
- `)}async function Me(e,t){let n=b();switch(t.subAction){case`enable`:return n.enableConsole(t.pageId,e,{bufferSize:t.bufferSize}),B(`Console capture enabled for page ${t.pageId}`,{enabled:!0,bufferSize:t.bufferSize??1e3});case`get`:{let e=n.get(t.pageId);if(!e?.consoleEnabled)return B(`Console capture not enabled. Call with subAction: "enable" first.`,{enabled:!1,entries:[]});let r=Ae(e.consoleBuffer.toArray(),t.level);return B(je(r)||`No console messages captured yet.`,{enabled:!0,count:r.length,entries:r})}case`clear`:{let e=n.get(t.pageId);return e&&e.consoleBuffer.clear(),B(`Console buffer cleared.`,{cleared:!0})}}}function Ne(e){return async t=>{let n=A(e),r=await Me(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}u.string().describe(`Tracked browser page identifier`),u.boolean().describe(`Whether to accept the next dialog`),u.string().optional().describe(`Text to provide when accepting a prompt dialog`);function Pe(e){return async({pageId:t,accept:n,promptText:r})=>{let i=A(e);return i.session.getPage(t).once(`dialog`,async e=>{n?await e.accept(r):await e.dismiss()}),i.resetIdleTimer(),B(`ok`,{ok:!0})}}function Fe(e){return async({pageId:t})=>{let n=A(e),r=await n.session.getPage(t).accessibility.snapshot()??{},i=JSON.stringify(r,null,2),a=n.session.getSnapshot(t);if(n.session.setSnapshot(t,i),n.resetIdleTimer(),!a)return B(`First snapshot captured (no previous to diff against).\n${i}`,{pageId:t,hasChanges:!1,isFirst:!0,snapshot:r});if(a===i)return B(`No changes detected.`,{pageId:t,hasChanges:!1});let o=a.split(`
4
- `),s=i.split(`
5
- `),c=new Set(o),l=new Set(s),u=s.filter(e=>!c.has(e)),d=o.filter(e=>!l.has(e)),f=u.length>0||d.length>0;return B([u.length>0?`Added:\n${u.join(`
6
- `)}`:``,d.length>0?`Removed:\n${d.join(`
7
- `)}`:``].filter(Boolean).join(`
8
-
9
- `)||`No meaningful changes.`,{pageId:t,hasChanges:f,added:u,removed:d})}}u.string().describe(`Tracked browser page identifier`),u.string().describe(`JavaScript expression or IIFE evaluated in page context. Code is wrapped in Function("return (<code>)"). For multi-statement code, use an IIFE: (() => { stmt1; return result; })(). Do NOT use bare return or semicolons at the top level.`),u.number().min(1).max(6e4).optional().describe(`Optional evaluation timeout`);function Ie(e){return async({pageId:t,code:n,timeoutMs:r})=>{let i=A(e),a=i.session.getPage(t),o=Math.min(r??e.evalTimeoutMs,6e4),s=P(await Oe(a.evaluate(e=>{let t=Function(`return (${e});`)();return typeof t==`function`?t():t},n),o,`browser_eval`),e.evalMaxResultBytes);if(!s.valid||s.result===void 0)throw Error(s.reason??`browser_eval result validation failed`);return i.resetIdleTimer(),B(s.result,{pageId:t,result:s.result,truncated:s.truncated??!1,reason:s.reason})}}const Le=2e3,Re=new Set([`localhost`,`metadata.google.internal`]),ze=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`];function H(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function U(e){if(!/^(?:\d{1,3}\.){3}\d{1,3}$/.test(e))return;let t=e.split(`.`).map(e=>Number(e));if(!t.some(e=>!Number.isInteger(e)||e<0||e>255))return t}function W(e){let t=H(e).split(`::`);if(t.length>2)return;let n=e=>{if(e.length===0)return e;let t=e[e.length-1];if(!t?.includes(`.`))return e;let n=U(t);if(n)return[...e.slice(0,-1),(n[0]<<8|n[1]).toString(16),(n[2]<<8|n[3]).toString(16)]},r=n(t[0]?t[0].split(`:`):[]),i=n(t[1]?t[1].split(`:`):[]);if(!r||!i)return;let a=t.length===2,o=r.length+i.length;if(!a&&o!==8||o>8)return;let s=a?Array(8-o).fill(`0`):[],c=[...r,...s,...i];if(c.length!==8)return;let l=0n;for(let e of c){if(!/^[0-9a-f]{1,4}$/i.test(e))return;l=(l<<16n)+BigInt(`0x${e}`)}return l}function Be(e){let[t,n,r,i]=e;return t===127||t===10||t===172&&n>=16&&n<=31||t===192&&n===168||t===169&&n===254||t===0&&n===0&&r===0&&i===0}function Ve(e){return e===1n||(e&339950059921992234495148655666698125312n)==338288524927261089654018896841347694592n||(e&337623910929368631717566993311207522304n)==334965454937798799971759379190646833152n}function He(e){let t;try{t=new URL(e)}catch{return!1}let n=H(t.hostname);if(Re.has(n))return!0;let r=U(n);if(r)return Be(r);let i=W(n);return i===void 0?!1:Ve(i)}function Ue(e){let t;try{t=new URL(e)}catch{throw Error(`Invalid fetch URL: ${e}`)}if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`Fetch URL must use http:// or https://`);return t}function We(e){let t=H(e),n=U(t);if(n)return Be(n);let r=W(t);return r===void 0?!1:Ve(r)}async function Ge(e){let t=Ue(e);if(He(e))throw Error(`Fetch URL must not target private or local hosts`);let n=H(t.hostname);if(!(U(n)||W(n)!==void 0))try{let e=d.lookup(n,{family:0,verbatim:!0}),t=new Promise((e,t)=>setTimeout(()=>t(Error(`DNS lookup timed out after ${Le}ms for hostname: ${n}`)),Le)),{address:r}=await Promise.race([e,t]);if(We(r))throw Error(`Fetch URL must not target private or local hosts`)}catch(e){if(e instanceof Error&&e.message===`Fetch URL must not target private or local hosts`)throw e;let t=e instanceof Error?e.message:String(e);throw Error(`Failed to resolve hostname ${n}: ${t}`,{cause:e})}}async function Ke(e){await Ge(e)}function qe(e,t=262144){return e.length<=t?{body:e,truncated:!1}:{body:`${e.slice(0,t)}\n\n[truncated after ${t} chars]`,truncated:!0}}function Je(e){let t=[`HTTP ${e.status} ${e.statusText}`,`URL: ${e.url}`,`Redirected: ${e.redirected?`yes`:`no`}`,`JSON: ${e.isJson?`yes`:`no`}`],n=Object.entries(e.headers);if(n.length>0){t.push(``,`Headers:`);for(let[e,r]of n)t.push(`${e}: ${r}`)}return t.push(``,`Body:`,e.body||`<empty>`),t.join(`
10
- `)}function Ye(e){let t=(e??`GET`).toUpperCase();if(!ze.includes(t))throw Error(`Unsupported fetch method: ${t}`);return t}function Xe(e){if(e===void 0)return 3e4;if(!Number.isFinite(e)||e<=0)throw Error(`Fetch timeoutMs must be a positive number`);return e}function Ze(e){return Object.entries(e).find(([e])=>e.toLowerCase()===`content-type`)?.[1]}function Qe(e,t){if(!t||e.length===0)return e;try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}async function $e(e,t){await Ke(t.url);let n=Ye(t.method),r=Xe(t.timeoutMs),i=t.headers??{},a=n===`GET`||n===`HEAD`?void 0:t.body,o;try{o=await e.evaluate(async e=>{let t=new AbortController,n=setTimeout(()=>t.abort(),e.timeoutMs);try{let n=await fetch(e.url,{method:e.method,headers:e.headers,body:e.body,signal:t.signal,redirect:`follow`});return{status:n.status,statusText:n.statusText,headers:Object.fromEntries(n.headers.entries()),body:await n.text(),url:n.url,redirected:n.redirected}}catch(t){if(t instanceof Error&&t.name===`AbortError`)throw Error(`Fetch timed out after ${e.timeoutMs}ms`);let n=t instanceof Error?t.message:String(t);throw Error(`Fetch failed: ${n}`,{cause:t})}finally{clearTimeout(n)}},{url:t.url,method:n,headers:i,body:a,timeoutMs:r})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(t,{cause:e})}await Ke(o.url);let s=Ze(o.headers)?.toLowerCase().includes(`json`)??!1,c=qe(Qe(o.body,s)),l=t.includeHeaders===!1?{}:o.headers,u={status:o.status,statusText:o.statusText,headers:l,body:c.body,isJson:s,url:o.url,redirected:o.redirected};return B(Je(u),{...u,truncated:c.truncated})}function et(e){return async t=>{let n=A(e),r=await $e(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}u.string().describe(`Tracked browser page identifier`),u.string().url().optional().describe(`Optional URL to navigate to`),u.enum([`back`,`forward`,`reload`,`waitFor`]).optional().describe(`Navigation helper action`),u.string().optional().describe(`Selector to wait for when type="waitFor"`),u.number().min(1).max(6e4).optional().describe(`Optional wait timeout`);function tt(e){return async({pageId:t,url:n,type:r,selector:i,timeoutMs:a})=>{let o=A(e),s=o.session.getPage(t);if(n){let r=M(n,e);if(!r.allowed)return B(`Navigation blocked: ${r.reason}`,{blocked:!0,pageId:t,reason:r.reason});await s.goto(n)}else if(!r)return B(`Navigation requires url or type`,{error:`Navigation requires url or type`,pageId:t});else if(r===`back`)await s.goBack();else if(r===`forward`)await s.goForward();else if(r===`reload`)await s.reload();else if(r===`waitFor`){if(!i)throw Error(`browser_navigate(waitFor) requires selector`);await s.waitForSelector(i,a?{timeout:a}:void 0)}let c=s.url(),l=await s.title();return o.session.updatePageInfo(t,c,l),o.resetIdleTimer(),B(`${l||c}\n${c}`,{pageId:t,url:c,title:l})}}function nt(e){return Object.entries(e).map(([e,t])=>({name:e,value:t}))}function rt(e){try{return Array.from(new URL(e).searchParams.entries()).map(([e,t])=>({name:e,value:t}))}catch{return[]}}function it(e,t){let n=t.toLowerCase();for(let[t,r]of Object.entries(e))if(t.toLowerCase()===n)return r}function at(e){let t=it(e,`content-type`);return t&&t.split(`;`,1)[0]?.trim()||`application/octet-stream`}function ot(e){return e===void 0?0:Buffer.byteLength(e,`utf8`)}function st(e,t=!1){return t?{...e}:h(e)}function ct(e,t){return{...e,headers:st(e.headers,t.showSensitive),postData:t.includeBody?e.postData:void 0,response:e.response?{...e.response,headers:st(e.response.headers,t.showSensitive),bodyPreview:t.includeBody?e.response.bodyPreview:void 0}:void 0}}function lt(e){return B(`Network capture not enabled. Call with subAction: "enable" first.`,{pageId:e,enabled:!1,count:0,entries:[]})}async function ut(e,t){let n=b();switch(t.subAction){case`enable`:return n.enableNetwork(t.pageId,e,t.filter,{bufferSize:t.bufferSize,showSensitive:t.showSensitive,includeBody:t.includeBody}),B(`Network capture enabled for page ${t.pageId}`,{pageId:t.pageId,enabled:!0,filter:t.filter??{resourceTypes:[...p]}});case`get`:{let e=n.get(t.pageId);if(!e?.networkEnabled)return lt(t.pageId);let r=e.networkBuffer.toArray().map(e=>ct(e,t));return B(r.map(e=>`${e.method} ${e.url} -> ${e.response?.status??`pending`} (${e.response?.timing?`${e.response.timing}ms`:`no response`})`).join(`
11
- `)||`No requests captured yet.`,{pageId:t.pageId,enabled:!0,count:r.length,entries:r})}case`clear`:{let e=n.get(t.pageId);return e?.networkEnabled?(e.networkBuffer.clear(),e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0,B(`Network capture buffer cleared.`,{pageId:t.pageId,cleared:!0})):B(`Network capture not enabled. Call with subAction: "enable" first.`,{pageId:t.pageId,enabled:!1,cleared:!1})}case`export-har`:{let e=n.get(t.pageId);if(!e?.networkEnabled)return lt(t.pageId);let r=e.networkBuffer.toArray().map(e=>{let n=ct(e,t);return{startedDateTime:new Date(n.timestamp).toISOString(),time:n.response?.timing??-1,request:{method:n.method,url:n.url,httpVersion:`HTTP/1.1`,cookies:[],headers:nt(n.headers),queryString:rt(n.url),headersSize:-1,bodySize:ot(e.postData),postData:n.postData?{text:n.postData}:void 0},response:n.response?{status:n.response.status,statusText:n.response.statusText,httpVersion:`HTTP/1.1`,cookies:[],headers:nt(n.response.headers),content:{size:n.response.bodySize??0,mimeType:at(n.response.headers),text:n.response.bodyPreview},redirectURL:``,headersSize:-1,bodySize:n.response.bodySize??-1}:{status:0,statusText:`pending`,httpVersion:`HTTP/1.1`,cookies:[],headers:[],content:{size:0,mimeType:`application/octet-stream`},redirectURL:``,headersSize:-1,bodySize:-1},cache:{},timings:{send:-1,wait:-1,receive:-1}}});return B(`Exported ${r.length} entries in HAR 1.2 format.`,{pageId:t.pageId,har:{log:{version:`1.2`,creator:{name:`aikit-browser`,version:`1.0.0`},entries:r}}})}default:{let e=`Unknown network subAction: ${String(t.subAction)}`;return B(e,{pageId:t.pageId,error:e})}}}function dt(e){return async t=>{let n=A(e),r=await ut(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}u.string().url().describe(`Absolute URL to open`),u.enum([`ui`,`headless`]).optional().describe(`Browser launch mode`),u.boolean().optional().describe(`Reserved for future tab reuse control`),u.string().optional().describe(`Human-readable label for the page`),u.boolean().optional().describe(`Auto-accept alert/beforeunload dialogs (default: true)`),u.enum([`load`,`domcontentloaded`,`networkidle`]).optional().describe(`Navigation readiness event`);function ft(e){return async({url:t,mode:n,label:r,autoDialog:i,waitUntil:a})=>{let o=M(t,e);if(!o.allowed)throw Error(o.reason??`Blocked URL: ${t}`);let s=A(e);s.isLaunched()||await s.launch(n??e.defaultMode);let c=await s.getContext().newPage();a?await c.goto(t,{waitUntil:a}):await c.goto(t);let l=await c.title(),u=s.session.registerPage(c,t,l,r);return i!==!1&&c.on(`dialog`,async e=>{let t=e.type();(t===`alert`||t===`beforeunload`)&&await e.accept()}),s.resetIdleTimer(),B(`Opened ${l||t}\npageId: ${u}`,{pageId:u,url:t,title:l,label:r})}}u.string().describe(`Tracked browser page identifier`),u.enum([`snapshot`,`dom`,`markdown`,`text`]).optional().describe(`Extraction mode: snapshot (ARIA tree), dom (HTML), markdown (clean MD), text (plain text)`),u.string().optional().describe(`CSS selector to scope extraction to a specific element`);function pt(e){return async({pageId:t,mode:n,selector:r})=>{let i=A(e).session.getPage(t),a=r??`body`,o=n??`snapshot`;switch(o){case`dom`:{let e=r?await i.locator(r).innerHTML():await i.content();return B(e||`empty page`,{pageId:t,mode:o,selector:r,dom:e})}case`markdown`:{let e=await i.evaluate(e=>{let t=globalThis,n=e?t.document?.querySelector(e):t.document?.body;if(!n)return``;function r(e){if(e.nodeType===t.Node?.TEXT_NODE)return e.textContent?.replace(/\s+/g,` `)||``;if(e.nodeType!==t.Node?.ELEMENT_NODE)return``;let n=e,a=n.tagName.toLowerCase(),o=t.window?.getComputedStyle(n);if(o?.display===`none`||o?.visibility===`hidden`)return``;let s=Array.from(n.childNodes).map(r).filter(Boolean).join(``);switch(a){case`h1`:return`# ${s.trim()}\n\n`;case`h2`:return`## ${s.trim()}\n\n`;case`h3`:return`### ${s.trim()}\n\n`;case`h4`:return`#### ${s.trim()}\n\n`;case`h5`:return`##### ${s.trim()}\n\n`;case`h6`:return`###### ${s.trim()}\n\n`;case`p`:return`${s.trim()}\n\n`;case`br`:return`
12
- `;case`hr`:return`
13
- ---
14
-
15
- `;case`strong`:case`b`:return`**${s.trim()}**`;case`em`:case`i`:return`*${s.trim()}*`;case`code`:return n.parentElement?.tagName===`PRE`?s:`\`${s.trim()}\``;case`pre`:return`\n\`\`\`\n${n.textContent?.trim()||``}\n\`\`\`\n\n`;case`blockquote`:return`> ${s.trim().replace(/\n/g,`
16
- > `)}\n\n`;case`a`:return`[${s.trim()}](${n.getAttribute(`href`)||``})`;case`img`:return`![${n.getAttribute(`alt`)||``}](${n.getAttribute(`src`)||``})`;case`ul`:case`ol`:return`${s}\n`;case`li`:return`${n.parentElement?.tagName===`OL`?`${Array.from(n.parentElement.children).indexOf(n)+1}. `:`- `}${s.trim()}\n`;case`table`:return`${i(n)}\n\n`;case`script`:case`style`:case`noscript`:case`nav`:case`footer`:case`header`:return``;case`div`:case`section`:case`article`:case`main`:case`span`:return s;default:return s}}function i(e){let t=Array.from(e.querySelectorAll(`tr`));if(!t.length)return``;let n=[];return t.forEach((e,t)=>{let r=Array.from(e.querySelectorAll(`th, td`)).map(e=>e.textContent?.trim()||``);n.push(`| ${r.join(` | `)} |`),t===0&&n.push(`| ${r.map(()=>`---`).join(` | `)} |`)}),n.join(`
17
- `)}return r(n).replace(/\n{3,}/g,`
18
-
19
- `).trim()},r);return B(e||`empty page`,{pageId:t,mode:o,selector:r,markdown:e})}case`text`:{let e=await i.locator(a).innerText();return B(e||`empty page`,{pageId:t,mode:o,selector:r,text:e})}default:{let e=await i.locator(a).ariaSnapshot();return B(e||`empty page`,{pageId:t,mode:o,selector:r,snapshot:e})}}}}u.string().describe(`Tracked browser page identifier`),u.string().optional().describe(`Optional target ref alias`),u.string().optional().describe(`Optional target selector`),u.boolean().optional().describe(`Capture the full page when no selector is provided`),u.boolean().optional().describe(`Mask password fields before capture`),u.object({x:u.number(),y:u.number(),width:u.number(),height:u.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),u.enum([`png`,`jpeg`]).optional().describe(`Image format (default: png)`),u.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`);function mt(e){return async({pageId:t,ref:n,selector:r,fullPage:i,redactPasswords:a,clip:o,format:s,quality:c})=>{let l=A(e),u=l.session.getPage(t),d=a??e.redactPasswordFieldsInScreenshots,f=[];d&&(f=await u.evaluate(()=>{let e=globalThis.document;return Array.from(e.querySelectorAll(`input[type="password"]`)).map((e,t)=>{let n=e,r=`data-aikit-password-mask-${t}`;return n.setAttribute(r,n.value),n.value=`***`,r})}));try{let e={};s&&(e.type=s),s===`jpeg`&&c!==void 0&&(e.quality=c);let a=r??n?await u.locator(V({ref:n,selector:r},`browser_screenshot`)).screenshot(e):await u.screenshot({...e,...o?{clip:o}:{fullPage:i??!1}});return l.resetIdleTimer(),B(`Screenshot captured`,{pageId:t,base64:a.toString(`base64`)})}finally{d&&f.length>0&&await u.evaluate(e=>{let t=globalThis.document;for(let n of e){let e=t.querySelector(`input[${n}]`);e&&(e.value=e.getAttribute(n)??``,e.removeAttribute(n))}},f)}}}const ht=u.enum([`Lax`,`None`,`Strict`]),gt=u.enum([`localStorage`,`sessionStorage`]);u.enum([`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`]).describe(`Session sub-action`),u.string().optional().describe(`Page ID (required for close, storage actions)`),u.boolean().optional().describe(`Explicit confirmation for cookie operations`),u.array(u.object({name:u.string(),value:u.string(),url:u.string().optional(),domain:u.string().optional(),path:u.string().optional(),expires:u.number().optional(),httpOnly:u.boolean().optional(),secure:u.boolean().optional(),sameSite:ht.optional()})).optional().describe(`Cookies to set (for set-cookie action)`),u.string().optional().describe(`Cookie name (for delete-cookie action)`),gt.optional().describe(`Storage type for storage actions`),u.string().optional().describe(`Storage key to get/set`),u.string().optional().describe(`Storage value to set`);function _t(e){return async({action:t,pageId:n,confirm:r,cookies:i,name:a,storageType:o,storageKey:s,storageValue:c})=>{let l=A(e);if(t===`list`){let e=l.session.listPages();return B(JSON.stringify(e,null,2),{pages:e})}if(t===`close`){if(!n)throw Error(`browser_session(close) requires pageId`);return await l.session.removePage(n),l.resetIdleTimer(),B(`ok`,{ok:!0,pageId:n})}if(t===`set-cookie`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);if(!i?.length)throw Error(`cookies array required for set-cookie`);return await l.getContext().addCookies(i),l.resetIdleTimer(),B(`Set ${i.length} cookie(s)`,{ok:!0,count:i.length})}if(t===`delete-cookie`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);if(!a)throw Error(`name required for delete-cookie`);return await l.getContext().clearCookies({name:a}),l.resetIdleTimer(),B(`Deleted cookie: ${a}`,{ok:!0,name:a})}if(t===`clear-cookies`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);return await l.getContext().clearCookies(),l.resetIdleTimer(),B(`All cookies cleared`,{ok:!0})}if(t===`get-storage`){if(!n)throw Error(`pageId required for get-storage`);if(!o)throw Error(`storageType required for get-storage`);let e=await l.session.getPage(n).evaluate(({type:e,key:t})=>{let n=e===`localStorage`?localStorage:sessionStorage;if(t)return n.getItem(t);let r={};for(let e=0;e<n.length;e+=1){let t=n.key(e);t&&(r[t]=n.getItem(t)||``)}return r},{type:o,key:s});return l.resetIdleTimer(),B(s?`${o}.${s} = ${String(e)}`:`${o}: ${JSON.stringify(e)}`,{storageType:o,key:s,data:e})}if(t===`set-storage`){if(!n)throw Error(`pageId required for set-storage`);if(!o)throw Error(`storageType required for set-storage`);if(!s)throw Error(`storageKey required for set-storage`);if(c===void 0)throw Error(`storageValue required for set-storage`);return await l.session.getPage(n).evaluate(({type:e,key:t,value:n})=>{(e===`localStorage`?localStorage:sessionStorage).setItem(t,n)},{type:o,key:s,value:c}),l.resetIdleTimer(),B(`Set ${o}.${s}`,{ok:!0,storageType:o,key:s})}if(t===`clear-storage`){if(!n)throw Error(`pageId required for clear-storage`);if(!o)throw Error(`storageType required for clear-storage`);return await l.session.getPage(n).evaluate(e=>{(e===`localStorage`?localStorage:sessionStorage).clear()},o),l.resetIdleTimer(),B(`Cleared ${o}`,{ok:!0,storageType:o})}let u=N(!!r);if(!u.allowed)throw Error(u.reason??`Cookie access denied`);let d=await l.getContext().cookies();return l.resetIdleTimer(),B(JSON.stringify(d,null,2),{cookies:d})}}const vt=[`open`,`batch`,`read`,`act`,`navigate`,`network`,`console`,`fetch`,`eval`,`diff`,`screenshot`,`dialog`,`session`],yt=[`ui`,`headless`],bt=[`load`,`domcontentloaded`,`networkidle`],G=[`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`],xt=[`back`,`forward`,`reload`,`waitFor`],St=[`enable`,`get`,`clear`,`export-har`],Ct=[`enable`,`get`,`clear`],wt=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],Tt=[`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`],Et={action:u.enum(vt).describe(`Browser action to perform`),pageId:u.string().optional().describe(`Tracked browser page identifier`),label:u.string().optional().describe(`Human-readable label for the page (used as alternative to pageId)`),url:u.string().url().optional().describe(`URL to open or navigate to`),mode:u.enum(yt).optional().describe(`Browser launch mode (open only)`),forceNew:u.boolean().optional().describe(`Reserved for future tab reuse`),autoDialog:u.boolean().optional().describe(`Auto-accept alert/beforeunload dialogs (default: true). Set false to handle all dialogs manually.`),waitUntil:u.enum(bt).optional().describe(`Navigation readiness event`),kind:u.enum(G).optional().describe(`Interaction kind (act only)`),ref:u.string().optional().describe(`Target ref alias`),selector:u.string().optional().describe(`Playwright selector`),element:u.string().optional().describe(`Human-readable element label (resolved via Playwright text selector). Prefer selector for precision.`),text:u.string().optional().describe(`Text to type`),key:u.string().optional().describe(`Key to press`),value:u.string().optional().describe(`Option value or drag target`),fromRef:u.string().optional().describe(`Drag source ref`),fromSelector:u.string().optional().describe(`Drag source selector`),toRef:u.string().optional().describe(`Drag target ref`),toSelector:u.string().optional().describe(`Drag target selector`),type:u.enum(xt).optional().describe(`Navigation type`),subAction:u.enum(St).optional().describe(`Network sub-action: enable, get, clear, or export-har`),code:u.string().optional().describe(`JavaScript expression or IIFE evaluated in page context. Wrapped in Function("return (<code>)"). Multi-statement: use (() => { ...; return result; })(). No bare return or top-level semicolons.`),timeoutMs:u.number().min(1).max(6e4).optional().describe(`Timeout in milliseconds`),filter:u.object({resourceTypes:u.array(u.string()).optional(),urlPattern:u.string().optional(),excludeUrls:u.array(u.string()).optional()}).optional().describe(`Network capture filter for resource types and URL patterns`),showSensitive:u.boolean().optional().describe(`Include sensitive headers instead of redacting them`),includeBody:u.boolean().optional().describe(`Include truncated request bodies in captured entries`),bufferSize:u.number().int().min(1).max(1e4).optional().describe(`Buffer capacity (1-10000, default varies by action)`),consoleSubAction:u.enum(Ct).optional().describe(`Console sub-action: enable, get, or clear`),level:u.string().optional().describe(`Filter console entries by level (log/info/warn/error/debug)`),fetchUrl:u.string().optional().describe(`URL for fetch action (http/https only). Uses page cookies/session.`),fetchMethod:u.enum(wt).optional().describe(`HTTP method for fetch action (default: GET)`),fetchHeaders:u.record(u.string(),u.string()).optional().describe(`Custom headers for fetch action`),fetchBody:u.string().optional().describe(`Request body for fetch action (POST/PUT/PATCH)`),includeResponseHeaders:u.boolean().optional().describe(`Include response headers in fetch output (default: true)`),fullPage:u.boolean().optional().describe(`Capture full page`),redactPasswords:u.boolean().optional().describe(`Mask password fields`),readMode:u.enum([`snapshot`,`dom`,`markdown`,`text`]).optional().describe(`Extraction mode for read action: snapshot (ARIA tree), dom (HTML), markdown (clean MD), text (plain text)`),clip:u.object({x:u.number(),y:u.number(),width:u.number(),height:u.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),format:u.enum([`png`,`jpeg`]).optional().describe(`Screenshot image format (default: png)`),quality:u.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`),cookies:u.array(u.object({name:u.string(),value:u.string(),url:u.string().optional(),domain:u.string().optional(),path:u.string().optional(),expires:u.number().optional(),httpOnly:u.boolean().optional(),secure:u.boolean().optional(),sameSite:u.enum([`Lax`,`None`,`Strict`]).optional()})).optional().describe(`Cookies to set (for session set-cookie action)`),name:u.string().optional().describe(`Cookie name for delete-cookie action`),storageType:u.enum([`localStorage`,`sessionStorage`]).optional().describe(`Storage type for get/set/clear-storage actions`),storageKey:u.string().optional().describe(`Storage key to get or set`),storageValue:u.string().optional().describe(`Value to set in storage`),accept:u.boolean().optional().describe(`Accept or dismiss dialog`),promptText:u.string().optional().describe(`Text for prompt dialog`),sessionAction:u.enum(Tt).optional().describe(`Session sub-action (session only)`),confirm:u.boolean().optional().describe(`Explicit confirmation for cookie export`),steps:u.array(u.record(u.string(),u.unknown())).optional().describe(`Array of action steps for batch execution. Each step is an object with action + action-specific params.`)};function Dt(e,t){return typeof e==`string`&&t.includes(e)}function K(e,t,n){let r=e[t];if(typeof r!=`string`)throw Error(`${n} requires ${t}`);return r}function Ot(e,t,n){let r=e[t];if(typeof r!=`boolean`)throw Error(`${n} requires ${t}`);return r}function q(e,t){let n=e[t];return typeof n==`string`?n:void 0}function J(e,t){let n=e[t];return typeof n==`boolean`?n:void 0}function Y(e,t){let n=e[t];return typeof n==`number`?n:void 0}function X(e,t){let n=e[t];return typeof n==`object`&&n?n:void 0}function kt(e,t){let n=e.steps;if(!Array.isArray(n)||n.length===0)throw Error(`${t} requires non-empty steps`);return n.map((e,n)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`${t} step ${n+1} must be an object`);return e})}function Z(e,t,n,r){let i=e[t];if(!Dt(i,n))throw Error(`${r} requires ${t}`);return i}function Q(e,t,n,r){let i=e[t];if(i!==void 0){if(!Dt(i,n))throw Error(`${r} received invalid ${t}`);return i}}function At(e,t){let n=q(e,`pageId`);if(n)return A(t).session.resolvePageId(n)}function $(e,t,n){let r=q(e,`pageId`);if(!r)throw Error(`${n} requires pageId`);return A(t).session.resolvePageId(r)}async function jt(e,t,n){switch(Z(e,`action`,vt,`browser`)){case`open`:return t.openHandler({url:K(e,`url`,`browser(open)`),mode:Q(e,`mode`,yt,`browser(open)`),forceNew:J(e,`forceNew`),label:q(e,`label`),autoDialog:J(e,`autoDialog`),waitUntil:Q(e,`waitUntil`,bt,`browser(open)`)});case`batch`:{let r=kt(e,`browser(batch)`),i=[],a=0;for(let e=0;e<r.length;e+=1){let o=r[e],s=q(o,`action`)??`unknown`;try{let r=await jt(o,t,n);a+=1,i.push({index:e,action:s,ok:!0,result:r})}catch(t){i.push({index:e,action:s,ok:!1,error:t instanceof Error?t.message:String(t)})}}return B(`Batch completed: ${a}/${r.length} steps succeeded`,{total:r.length,succeeded:a,steps:i})}case`read`:return t.readHandler({pageId:$(e,n,`browser(read)`),mode:Q(e,`readMode`,[`snapshot`,`dom`,`markdown`,`text`],`browser(read)`),selector:q(e,`selector`)});case`act`:return t.actHandler({pageId:$(e,n,`browser(act)`),kind:Z(e,`kind`,G,`browser(act)`),ref:q(e,`ref`),selector:q(e,`selector`),element:q(e,`element`),text:q(e,`text`),key:q(e,`key`),value:q(e,`value`),fromRef:q(e,`fromRef`),fromSelector:q(e,`fromSelector`),toRef:q(e,`toRef`),toSelector:q(e,`toSelector`)});case`navigate`:return t.navigateHandler({pageId:$(e,n,`browser(navigate)`),url:q(e,`url`),type:Q(e,`type`,xt,`browser(navigate)`),selector:q(e,`selector`),timeoutMs:Y(e,`timeoutMs`)});case`network`:return t.networkHandler({pageId:$(e,n,`browser(network)`),subAction:Z(e,`subAction`,St,`browser(network)`),filter:X(e,`filter`),showSensitive:J(e,`showSensitive`),includeBody:J(e,`includeBody`),bufferSize:Y(e,`bufferSize`)});case`console`:return t.consoleHandler({pageId:$(e,n,`browser(console)`),subAction:Z(e,`consoleSubAction`,Ct,`browser(console)`),level:q(e,`level`),bufferSize:Y(e,`bufferSize`)});case`fetch`:return t.fetchHandler({pageId:$(e,n,`browser(fetch)`),url:K(e,`fetchUrl`,`browser(fetch)`),method:Q(e,`fetchMethod`,wt,`browser(fetch)`),headers:X(e,`fetchHeaders`),body:q(e,`fetchBody`),timeoutMs:Y(e,`timeoutMs`),includeHeaders:J(e,`includeResponseHeaders`)});case`eval`:return t.evalHandler({pageId:$(e,n,`browser(eval)`),code:K(e,`code`,`browser(eval)`),timeoutMs:Y(e,`timeoutMs`)});case`diff`:return t.diffHandler({pageId:$(e,n,`browser(diff)`)});case`screenshot`:{let r=X(e,`clip`);return t.screenshotHandler({pageId:$(e,n,`browser(screenshot)`),ref:q(e,`ref`),selector:q(e,`selector`),fullPage:J(e,`fullPage`),redactPasswords:J(e,`redactPasswords`),clip:r,format:Q(e,`format`,[`png`,`jpeg`],`browser(screenshot)`),quality:Y(e,`quality`)})}case`dialog`:return t.dialogHandler({pageId:$(e,n,`browser(dialog)`),accept:Ot(e,`accept`,`browser(dialog)`),promptText:q(e,`promptText`)});case`session`:{let r=e.cookies;return t.sessionHandler({action:Z(e,`sessionAction`,Tt,`browser(session)`),pageId:At(e,n),confirm:J(e,`confirm`),cookies:r,name:q(e,`name`),storageType:Q(e,`storageType`,[`localStorage`,`sessionStorage`],`browser(session)`),storageKey:q(e,`storageKey`),storageValue:q(e,`storageValue`)})}}}function Mt(e,t){let n=De(t),r={openHandler:ft(n),readHandler:pt(n),actHandler:ke(n),navigateHandler:tt(n),networkHandler:dt(n),consoleHandler:Ne(n),fetchHandler:et(n),evalHandler:Ie(n),diffHandler:Fe(n),screenshotHandler:mt(n),dialogHandler:Pe(n),sessionHandler:_t(n)};e.registerTool(`browser`,{title:`Browser Automation`,description:`Unified browser automation tool. Actions:
20
- - open: Launch a browser page (url required, mode/waitUntil optional)
21
- - batch: Execute multiple browser actions sequentially (steps required)
22
- - read: Get accessibility snapshot of a page (pageId required)
23
- - act: Interact with elements — click/type/press/hover/drag/select/scroll/upload (pageId + kind required)
24
- - navigate: Go to URL, back/forward/reload, or waitFor selector (pageId required)
25
- - network: Capture and inspect page network activity (pageId + subAction required)
26
- - console: Capture browser console messages — log/warn/error (pageId + consoleSubAction required)
27
- - fetch: Execute HTTP request using page cookies/session (pageId + fetchUrl required)
28
- - eval: Evaluate JavaScript in page context (pageId + code required)
29
- - diff: Compare the current accessibility snapshot to the previous one (pageId required)
30
- - screenshot: Capture page or element screenshot (pageId required)
31
- - dialog: Accept or dismiss next browser dialog (pageId + accept required)
32
- - session: List pages, close a page, or export cookies (sessionAction required)`,inputSchema:Et,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},async e=>jt(e,r,n))}export{O as BrowserEngine,D as DEFAULT_BROWSER_CONFIG,_e as SessionRegistry,he as autoSelectMode,ve as closeEngine,me as detectDisplayAvailable,E as ensureBrowserInstalled,C as getDefaultBrowsersPath,A as getEngine,ge as getLaunchArgs,T as isBrowserInstalled,N as isCookieAccessAllowed,M as isUrlAllowed,we as redactPasswordFields,Mt as registerBrowserTools,w as resolveBrowsersPath,P as validateEvalResult};
1
+ import{execFile as e,execFileSync as t,execSync as n}from"node:child_process";import{existsSync as r,mkdirSync as i,readFileSync as a,unlinkSync as o,writeFileSync as s}from"node:fs";import{homedir as c,platform as l,tmpdir as u}from"node:os";import{join as d}from"node:path";import{z as f}from"zod";const p=`0.26.0`,m=`.agent-browser-version`;function ee(e){return typeof e==`string`?e.trim():e instanceof Buffer?e.toString(`utf8`).trim():``}function h(){return d(c(),`.aikit`,`browsers`)}function g(e){return e.browsersPath??process.env.AIKIT_BROWSER_PATH??process.env.AIKIT_BROWSER_BROWSERS_PATH??h()}function _(){try{return n(`${l()===`win32`?`where`:`which`} agent-browser`,{encoding:`utf8`,stdio:`pipe`,timeout:3e3}).trim().split(`
2
+ `)[0]}catch{return null}}function v(e){return d(e,`agent-browser`,te())}function te(){switch(l()){case`win32`:return`agent-browser.exe`;default:return`agent-browser`}}function y(e,t){try{s(d(e,m),t,`utf8`)}catch{}}function ne(e){try{return a(d(e,m),`utf8`).trim()}catch{return null}}function re(e){let t=ne(e);return t?ie(t,p)>=0:!1}function ie(e,t){let n=e.split(`.`).map(Number),r=t.split(`.`).map(Number);for(let e=0;e<Math.max(n.length,r.length);e++){let t=n[e]||0,i=r[e]||0;if(t!==i)return t-i}return 0}function ae(e){return!!(r(d(e,`chrome`))||r(d(c(),`.cache`,`agent-browser`,`chrome`)))}async function b(e,t){let a=_();if(a)return t?.(`Using globally installed agent-browser`),a;let o=g(e);r(o)||i(o,{recursive:!0});let s=v(o);if(r(s)&&re(o))return t?.(`Using cached agent-browser at ${s}`),s;t?.(`Downloading agent-browser (first use)...`);try{n(`npx -y agent-browser@latest install --install-dir "${o}"`,{stdio:`pipe`,timeout:12e4,encoding:`utf8`,env:{...process.env,AGENT_BROWSER_CACHE_DIR:o}})}catch(e){let t=e,n=ee(t.stderr)||ee(t.stdout)||t.message||`Unknown agent-browser install failure`;throw Error(`Failed to install agent-browser: ${n}`)}let c=s;try{let e=n(`npx -y -p agent-browser which agent-browser 2>/dev/null || true`,{encoding:`utf8`,timeout:1e4,stdio:`pipe`}).trim();e&&r(e)&&(c=e,y(o,p))}catch{try{let e=n(`npx -y agent-browser --version`,{encoding:`utf8`,timeout:1e4,stdio:`pipe`}).trim();e&&y(o,e)}catch{}}return t?.(`agent-browser ready`),c}async function oe(e){try{return t(e,[`--version`],{encoding:`utf8`,stdio:`pipe`,timeout:5e3}).trim()}catch{return null}}function se(e){if(_())return!0;let t=g(e);return ae(t)||r(v(t))}const ce=[`ui`,`headless`];function x(e){return ce.includes(e)?e:`headless`}function S(e){let t=[];return e===`headless`&&t.push(`--headless`),t}function le(e){return{headless:e===`headless`,args:S(e)}}var C=class{tabs=new Map;labelToId=new Map;register(e,t,n,r){return this.tabs.set(e,{tabId:e,url:t,title:n??t,label:r,createdAt:new Date}),r&&this.labelToId.set(r,e),e}resolve(e){return this.tabs.has(e)?e:this.labelToId.get(e)||e}getTab(e){let t=this.tabs.get(e);if(!t)throw Error(`Tab not found: ${e}`);return t}updateTabInfo(e,t,n){let r=this.tabs.get(e);r&&this.tabs.set(e,{...r,url:t,title:n})}setSnapshot(e,t){let n=this.tabs.get(e);n&&this.tabs.set(e,{...n,snapshot:t})}getSnapshot(e){return this.tabs.get(e)?.snapshot}async removeTab(e){let t=this.tabs.get(e);t&&(t.label&&this.labelToId.delete(t.label),this.tabs.delete(e))}listTabs(){return[...this.tabs.values()]}listPages(){return this.listTabs().map(e=>({pageId:e.tabId,url:e.url,title:e.title,label:e.label,createdAt:e.createdAt}))}async closeAll(){this.tabs.clear(),this.labelToId.clear()}clear(){this.tabs.clear(),this.labelToId.clear()}get size(){return this.tabs.size}},ue=class{tabs=new C;registerPage(e,t,n,r){return this.tabs.register(e,t,n,r)}getPage(e){throw Error(`Direct Playwright Page access no longer supported with agent-browser. Use engine.exec() or engine.tabExec() for browser operations.`)}getPageInfo(e){let t=this.tabs.getTab(e);return{pageId:t.tabId,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}}resolvePageId(e){return this.tabs.resolve(e)}setSnapshot(e,t){this.tabs.setSnapshot(e,t)}getSnapshot(e){return this.tabs.getSnapshot(e)}updatePageInfo(e,t,n){this.tabs.updateTabInfo(e,t,n)}async removePage(e){await this.tabs.removeTab(e)}listPages(){return this.tabs.listPages()}async closeAll(){await this.tabs.closeAll()}get size(){return this.tabs.size}};const w={defaultMode:`ui`,browsersPath:null,userDataDirRoot:null,idleShutdownMinutes:10,allowInternalSchemes:!1,allowLoopback:!1,evalTimeoutMs:1e4,evalMaxResultBytes:262144,redactPasswordFieldsInScreenshots:!0,engine:`chrome`};var de=class{binaryPath=null;_currentMode=null;_isLaunched=!1;idleTimer=null;tabs=new C;config;constructor(e={}){this.config={...w,...e}}get currentMode(){return this._currentMode}async launch(e,t){if(!this._isLaunched){this._currentMode=x(e??this.config.defaultMode),this.binaryPath=await b(this.config,t);try{let e=await this.exec(`--version`);t?.(`agent-browser ${e.stdout.trim()}`)}catch(e){throw Error(`agent-browser binary failed: ${e instanceof Error?e.message:String(e)}`)}this._isLaunched=!0,this.resetIdleTimer()}}async exec(t,...n){if(!this.binaryPath)throw Error(`agent-browser not launched. Call launch() first, or use getEngine() singleton.`);let r=[t,...n];return new Promise((t,n)=>{e(this.binaryPath,r,{timeout:this.config.evalTimeoutMs,env:{...process.env,AGENT_BROWSER_IDLE_TIMEOUT_MS:String(this.config.idleShutdownMinutes*60*1e3)}},(e,r,i)=>{if(e&&e.code===`ENOENT`){n(Error(`agent-browser binary not found at ${this.binaryPath}`));return}t({stdout:r??``,stderr:i??``,exitCode:e?.code?Number(e.code):0})}).stdin?.end()})}async tabExec(e,t,...n){return this.exec(`tab`,e,t,...n)}async open(e,t,n){await this.launch(t);let r=await this.exec(`open`,e);return this.tabs.register(`t0`,e,r.stdout.trim(),n),this.resetIdleTimer(),`t0`}async screenshot(e,t){let n=t?.format??`png`,r=n===`jpeg`?`image/jpeg`:`image/png`,i=n===`jpeg`?`jpg`:`png`,s=d(u(),`aikit-screenshot-${Date.now()}.${i}`),c=[s];t?.fullPage&&c.push(`--full`),t?.format===`jpeg`&&c.push(`--screenshot-format`,`jpeg`),t?.quality&&c.push(`--screenshot-quality`,String(t.quality)),await this.tabExec(e,`screenshot`,...c);let l=a(s),f=l.toString(`base64`);try{o(s)}catch{}return this.resetIdleTimer(),{base64:f,mimeType:r,bytes:l.length}}async readSnapshot(e,t=!1){let n=t?[`-c`,`--json`]:[`-i`,`--json`],r=await this.tabExec(e,`snapshot`,...n);return this.resetIdleTimer(),r.stdout}async navigate(e,t){let n=await this.tabExec(e,`goto`,t);return this.resetIdleTimer(),n}async close(){if(this.stopIdleTimer(),this._isLaunched){try{await this.exec(`close`).catch(()=>{})}catch{}this.tabs.clear(),this._isLaunched=!1,this._currentMode=null}}isLaunched(){return this._isLaunched}getConfig(){return this.config}resolvePageId(e){return this.tabs.resolve(e)}resetIdleTimer(){this.stopIdleTimer();let e=setTimeout(()=>{this.close()},this.config.idleShutdownMinutes*6e4);e.unref?.(),this.idleTimer=e}stopIdleTimer(){this.idleTimer&&=(clearTimeout(this.idleTimer),null)}};let T=null;function E(e){return T||=new de(e),T}async function fe(){T&&=(await T.close(),null)}function D(e,t){try{let n=new URL(e),r=n.hostname.toLowerCase(),i=n.protocol.toLowerCase();return[`file:`,`chrome:`,`chrome-extension:`,`data:`,`javascript:`].includes(i)?t?.allowInternalSchemes?{allowed:!0}:{allowed:!1,reason:`Scheme '${i}' is blocked for security`}:[`169.254.169.254`,`metadata.google.internal`,`metadata.instance`].includes(r)?{allowed:!1,reason:`Cloud metadata endpoint '${r}' is blocked`}:n.hostname===`localhost`||r===`127.0.0.1`||r===`::1`||r.startsWith(`10.`)||r.startsWith(`172.`)||r.startsWith(`192.168.`)?t?.allowLoopback?{allowed:!0}:{allowed:!1,reason:`Internal host '${r}' requires allowLoopback=true`}:{allowed:!0}}catch{return{allowed:!1,reason:`Invalid URL`}}}function pe(e,t){return Buffer.byteLength(e,`utf8`)<=t?{valid:!0,result:e,truncated:!1}:{valid:!0,result:`${Buffer.from(e).subarray(0,t).toString(`utf8`)}\n... [truncated]`,truncated:!0,reason:`Result exceeded ${t} bytes, truncated to ${t} bytes`}}const me=[`ui`,`headless`];function he(e){return typeof e==`object`&&e?e:null}function O(e){return typeof e==`string`&&e.length>0?e:void 0}function k(e){return typeof e==`boolean`?e:void 0}function A(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function j(e){return typeof e==`string`&&me.includes(e)?e:void 0}function M(e){let t=process.env[e];if(!t)return;let n=Number(t);return Number.isFinite(n)?n:void 0}function ge(e){let t=he(e.browser)??{};return{...w,defaultMode:j(process.env.AIKIT_BROWSER_DEFAULT_MODE)??j(t.defaultMode)??w.defaultMode,browsersPath:O(process.env.AIKIT_BROWSER_PATH)??O(process.env.AIKIT_BROWSER_BROWSERS_PATH)??O(t.browsersPath)??w.browsersPath,userDataDirRoot:O(t.userDataDirRoot)??w.userDataDirRoot,idleShutdownMinutes:M(`AIKIT_BROWSER_IDLE_MINUTES`)??A(t.idleShutdownMinutes)??w.idleShutdownMinutes,allowInternalSchemes:k(t.allowInternalSchemes)??w.allowInternalSchemes,allowLoopback:k(t.allowLoopback)??w.allowLoopback,evalTimeoutMs:M(`AIKIT_BROWSER_EVAL_TIMEOUT_MS`)??A(t.evalTimeoutMs)??w.evalTimeoutMs,evalMaxResultBytes:A(t.evalMaxResultBytes)??w.evalMaxResultBytes,redactPasswordFieldsInScreenshots:k(t.redactPasswordFieldsInScreenshots)??w.redactPasswordFieldsInScreenshots,engine:O(t.engine)??w.engine,proxy:O(t.proxy),viewport:O(t.viewport)}}function N(e,t){return{content:[{type:`text`,text:e}],structuredContent:t}}function P(e,t){let n=e.selector??e.ref??(e.element?`text=${e.element}`:void 0);if(!n)throw Error(`${t} requires selector, ref, or element`);return n}function _e(e){return async({pageId:t,kind:n,ref:r,selector:i,element:a,text:o,key:s,value:c,fromRef:l,fromSelector:u,toRef:d,toSelector:f})=>{let p=E(e),m=p.resolvePageId(t);switch(n){case`click`:{let e=P({ref:r,selector:i,element:a},`browser_act(click)`);await p.tabExec(m,`click`,e);break}case`type`:{let e=P({ref:r,selector:i,element:a},`browser_act(type)`);await p.tabExec(m,`fill`,e,o??``);break}case`press`:{let e=P({ref:r,selector:i,element:a},`browser_act(press)`);if(!s)throw Error(`browser_act(press) requires key`);await p.tabExec(m,`press`,e,s);break}case`hover`:{let e=P({ref:r,selector:i,element:a},`browser_act(hover)`);await p.tabExec(m,`hover`,e);break}case`drag`:{let e=P({ref:l??r,selector:u??i,element:a},`browser_act(drag) source`),t=P({ref:d,selector:f??c,element:a},`browser_act(drag) target`);await p.tabExec(m,`drag`,e,t);break}case`select`:{let e=P({ref:r,selector:i,element:a},`browser_act(select)`);if(c===void 0)throw Error(`browser_act(select) requires value`);await p.tabExec(m,`select`,e,c);break}case`scroll`:{if(i||r||a){let e=P({ref:r,selector:i,element:a},`browser_act(scroll)`);await p.tabExec(m,`scrollintoview`,e);break}let e=c??`down 500`;await p.tabExec(m,`scroll`,e);break}case`upload`:{let e=P({ref:r,selector:i,element:a},`browser_act(upload)`);if(!c)throw Error(`value (file path) required for upload`);await p.tabExec(m,`upload`,e,c);break}}return p.resetIdleTimer(),N(`ok`,{ok:!0})}}function ve(e){return async({pageId:t,subAction:n,level:r,bufferSize:i})=>{let a=E(e),o=a.resolvePageId(t);switch(n){case`enable`:{let e=[`--enable`];return i&&e.push(`--buffer-size`,String(i)),r&&e.push(`--level`,r),await a.tabExec(o,`console`,...e),a.resetIdleTimer(),N(`Console capture enabled`,{enabled:!0})}case`get`:{let e=[`--json`];r&&e.push(`--level`,r);let t=await a.tabExec(o,`console`,...e);a.resetIdleTimer();let n=[];try{n=JSON.parse(t.stdout)}catch{n=[{raw:t.stdout}]}return N(`Console has ${n.length} entries`,{entries:n,count:n.length})}case`clear`:return await a.tabExec(o,`console`,`clear`),a.resetIdleTimer(),N(`Console cleared`,{cleared:!0});default:return N(`Unknown console sub-action`,{error:`Unknown console sub-action`})}}}function ye(e){return async({pageId:t,accept:n,promptText:r})=>{let i=E(e),a=i.resolvePageId(t);if(n){let e=r?[`accept`,`--text`,r]:[`accept`];await i.tabExec(a,`dialog`,...e)}else await i.tabExec(a,`dialog`,`dismiss`);return i.resetIdleTimer(),N(`Dialog ${n?`accepted`:`dismissed`}`,{accepted:n})}}function be(e){return async({pageId:t})=>{let n=E(e),r=n.resolvePageId(t),i=await n.tabExec(r,`diff`,`snapshot`);return n.resetIdleTimer(),N(i.stdout,{pageId:r,diff:i.stdout})}}function xe(e){return async({pageId:t,code:n,timeoutMs:r})=>{let i=E(e),a=i.resolvePageId(t),o=r??e.evalTimeoutMs,s=await i.tabExec(a,`evaluate`,`--timeout=${o}`,`--`,n);i.resetIdleTimer();let c=pe(s.stdout,e.evalMaxResultBytes);return N(c.result,{pageId:a,result:c.result,truncated:c.truncated})}}function Se(e){return async({pageId:t,url:n,method:r,headers:i,body:a,timeoutMs:o,includeHeaders:s})=>{let c=E(e),l=c.resolvePageId(t),u=o??e.evalTimeoutMs,d=Ce(n,r??`GET`,s??!0,i,a),f=await c.tabExec(l,`evaluate`,`--timeout=${u}`,`--`,d);return c.resetIdleTimer(),N(f.stdout,{pageId:l,response:f.stdout})}}function Ce(e,t,n,r,i){let a={method:t,headers:r??{}};i&&[`POST`,`PUT`,`PATCH`].includes(t.toUpperCase())&&(a.body=i);let o=JSON.stringify(a);return n?`fetch(${JSON.stringify(e)}, ${o}).then(async r => {
3
+ const text = await r.text();
4
+ const headers = {};
5
+ r.headers.forEach((v, k) => { headers[k] = v; });
6
+ return JSON.stringify({ status: r.status, statusText: r.statusText, headers, body: text.slice(0, 100000) });
7
+ })`:`fetch(${JSON.stringify(e)}, ${o}).then(async r => {
8
+ const text = await r.text();
9
+ return JSON.stringify({ status: r.status, statusText: r.statusText, body: text.slice(0, 100000) });
10
+ })`}function we(e){return async({pageId:t,url:n,type:r,selector:i,timeoutMs:a})=>{let o=E(e),s=o.resolvePageId(t);if(n){let t=D(n,e);if(!t.allowed)return N(`Navigation blocked: ${t.reason}`,{blocked:!0,pageId:s,reason:t.reason});await o.tabExec(s,`goto`,n)}else if(!r)return N(`Navigation requires url or type`,{error:`Navigation requires url or type`,pageId:s});else if(r===`back`)await o.tabExec(s,`back`);else if(r===`forward`)await o.tabExec(s,`forward`);else if(r===`reload`)await o.tabExec(s,`reload`);else if(r===`waitFor`){if(!i)throw Error(`browser_navigate(waitFor) requires selector`);let e=a?[i,String(a)]:[i];await o.tabExec(s,`wait`,...e)}let c=await o.tabExec(s,`get`,`text`).catch(()=>({stdout:n??``})),l=await o.tabExec(s,`get`,`attr`,`title`).catch(()=>({stdout:``})),u=c.stdout.trim()||n||``,d=l.stdout.trim()||u;return o.tabs.updateTabInfo(s,u,d),o.resetIdleTimer(),N(`${d||u}\n${u}`,{pageId:s,url:u,title:d})}}function Te(e){return async({pageId:t,subAction:n,filter:r,includeBody:i,bufferSize:a})=>{let o=E(e),s=o.resolvePageId(t);switch(n){case`enable`:{let e=[`--enable`];return r?.resourceTypes&&e.push(`--filter`,r.resourceTypes.join(`,`)),i&&e.push(`--include-body`),a&&e.push(`--buffer-size`,String(a)),await o.tabExec(s,`network`,...e),o.resetIdleTimer(),N(`Network capture enabled`,{enabled:!0,filter:r})}case`get`:{let e=await o.tabExec(s,`network`,`requests`,`--json`);o.resetIdleTimer();let t=[];try{t=JSON.parse(e.stdout)}catch{t=[{raw:e.stdout}]}return N(`Captured ${t.length} network requests`,{entries:t,count:t.length})}case`clear`:return await o.tabExec(s,`network`,`clear`),o.resetIdleTimer(),N(`Network capture cleared`,{cleared:!0});case`export-har`:{let e=await o.tabExec(s,`network`,`har`);return o.resetIdleTimer(),N(e.stdout,{har:e.stdout})}default:return N(`Unknown network sub-action`,{error:`Unknown network sub-action`})}}}function Ee(e){return async({url:t,mode:n,label:r,waitUntil:i,forceNew:a,autoDialog:o})=>{let s=D(t,e);if(!s.allowed)return N(`Navigation blocked: ${s.reason}`,{blocked:!0,reason:s.reason});let c=E(e);await c.launch(n);let l=await c.exec(`open`,t);c.tabs.register(`t0`,t,r??l.stdout.trim(),r),c.resetIdleTimer();let u=[];return i===`networkidle`&&u.push(`--wait-until`,`networkidle`),i===`domcontentloaded`&&u.push(`--wait-until`,`domcontentloaded`),u.length>0&&await c.tabExec(`t0`,`wait`,...u).catch(()=>{}),N(`Opened ${t}\nTab: t0`,{pageId:`t0`,url:t,tabId:`t0`,label:r})}}function De(e){return async({pageId:t,mode:n,selector:r})=>{let i=E(e),a=i.resolvePageId(t);switch(n??`snapshot`){case`snapshot`:{let e=[`-i`,`--json`];r&&e.push(`-s`,r);let t=await i.tabExec(a,`snapshot`,...e);return i.tabs.setSnapshot(a,t.stdout),i.resetIdleTimer(),N(t.stdout,{pageId:a,mode:`snapshot`,content:t.stdout})}case`dom`:{let e=await i.tabExec(a,`get`,`html`);return i.resetIdleTimer(),N(e.stdout,{pageId:a,mode:`dom`,content:e.stdout})}case`markdown`:{let e=await i.tabExec(a,`snapshot`,`-c`);return i.resetIdleTimer(),N(e.stdout,{pageId:a,mode:`markdown`,content:e.stdout})}case`text`:{let e=await i.tabExec(a,`get`,`text`);return i.resetIdleTimer(),N(e.stdout,{pageId:a,mode:`text`,content:e.stdout})}default:return N(`Unknown read mode`,{error:`Unknown read mode`,pageId:a})}}}function Oe(e){return async({pageId:t,ref:n,selector:r,fullPage:i,clip:a,format:o,quality:s})=>{let c=E(e),l=c.resolvePageId(t);if(r||n){let e=r??n,t=await c.tabExec(l,`screenshot`,`--element`,e);return c.resetIdleTimer(),N(`Screenshot captured for element ${e}`,{element:e,pageId:l,screenshot:t.stdout})}let u=await c.screenshot(l,{fullPage:i,format:o,quality:s});return c.resetIdleTimer(),N(`Screenshot captured (${u.bytes} bytes)`,{pageId:l,base64:u.base64,mimeType:u.mimeType,bytes:u.bytes})}}function ke(e){return async({sessionAction:t,pageId:n,confirm:r,cookies:i,name:a,storageType:o,storageKey:s,storageValue:c})=>{let l=E(e),u=n?l.resolvePageId(n):void 0;switch(t){case`list`:{let e=l.tabs.listPages().map(e=>({pageId:e.pageId,url:e.url,title:e.title,label:e.label,createdAt:e.createdAt.toISOString()}));return N(`Open tabs: ${e.length}\n${e.map(e=>` ${e.pageId}: ${e.title||e.url}`).join(`
11
+ `)}`,{pages:e})}case`close`:return u?(await l.tabExec(u,`close`).catch(()=>{}),await l.tabs.removeTab(u),l.resetIdleTimer(),N(`Closed tab ${u}`,{closed:u})):N(`close requires pageId`,{error:`pageId required`});case`cookies`:{if(!r)return N(`Cookie export requires explicit confirmation. Set confirm: true to proceed.`,{confirmRequired:!0});let e=await l.tabExec(u??`t0`,`cookies`),t=[];try{t=JSON.parse(e.stdout)}catch{t=[{raw:e.stdout}]}return N(`Exported ${t.length} cookies`,{cookies:t,count:t.length})}case`set-cookie`:{if(!i||i.length===0)return N(`set-cookie requires cookies array`,{error:`cookies array required`});let e=i.map(e=>`${e.name}=${e.value}${e.domain?`; domain=${e.domain}`:``}${e.path?`; path=${e.path}`:``}`).join(` `);return await l.tabExec(u??`t0`,`cookies`,`set`,e),l.resetIdleTimer(),N(`Set ${i.length} cookies`,{count:i.length})}case`delete-cookie`:return a?(await l.tabExec(u??`t0`,`cookies`,`clear`,a),l.resetIdleTimer(),N(`Deleted cookie ${a}`,{deleted:a})):N(`delete-cookie requires name`,{error:`name required`});case`clear-cookies`:return await l.tabExec(u??`t0`,`cookies`,`clear`),l.resetIdleTimer(),N(`Cookies cleared`,{cleared:!0});case`get-storage`:{if(!o||!s)return N(`get-storage requires storageType and storageKey`,{error:`storageType and storageKey required`});let e=await l.tabExec(u??`t0`,`storage`,o===`localStorage`?`local`:`session`,`get`,s);return l.resetIdleTimer(),N(e.stdout,{value:e.stdout})}case`set-storage`:return!o||!s||c===void 0?N(`set-storage requires storageType, storageKey, and storageValue`,{error:`storageType, storageKey, storageValue required`}):(await l.tabExec(u??`t0`,`storage`,o===`localStorage`?`local`:`session`,`set`,s,c),l.resetIdleTimer(),N(`Storage key ${s} set`,{key:s}));case`clear-storage`:return o?(await l.tabExec(u??`t0`,`storage`,o===`localStorage`?`local`:`session`,`clear`),l.resetIdleTimer(),N(`Storage cleared`,{cleared:!0})):N(`clear-storage requires storageType`,{error:`storageType required`});default:return N(`Unknown session action`,{error:`Unknown session action`})}}}const F=[`open`,`batch`,`read`,`act`,`navigate`,`network`,`console`,`fetch`,`eval`,`diff`,`screenshot`,`dialog`,`session`],I=[`ui`,`headless`],L=[`load`,`domcontentloaded`,`networkidle`],R=[`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`],z=[`back`,`forward`,`reload`,`waitFor`],B=[`enable`,`get`,`clear`,`export-har`],V=[`enable`,`get`,`clear`],H=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],U=[`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`],Ae={action:f.enum(F).describe(`Browser action to perform`),pageId:f.string().optional().describe(`Tracked browser page identifier`),label:f.string().optional().describe(`Human-readable label for the page (used as alternative to pageId)`),url:f.string().url().optional().describe(`URL to open or navigate to`),mode:f.enum(I).optional().describe(`Browser launch mode (open only)`),forceNew:f.boolean().optional().describe(`Reserved for future tab reuse`),autoDialog:f.boolean().optional().describe(`Auto-accept alert/beforeunload dialogs (default: true). Set false to handle all dialogs manually.`),waitUntil:f.enum(L).optional().describe(`Navigation readiness event`),kind:f.enum(R).optional().describe(`Interaction kind (act only)`),ref:f.string().optional().describe(`Target ref alias`),selector:f.string().optional().describe(`CSS selector for targeting`),element:f.string().optional().describe(`Human-readable element label. Prefer selector for precision.`),text:f.string().optional().describe(`Text to type`),key:f.string().optional().describe(`Key to press`),value:f.string().optional().describe(`Option value or drag target`),fromRef:f.string().optional().describe(`Drag source ref`),fromSelector:f.string().optional().describe(`Drag source selector`),toRef:f.string().optional().describe(`Drag target ref`),toSelector:f.string().optional().describe(`Drag target selector`),type:f.enum(z).optional().describe(`Navigation type`),subAction:f.enum(B).optional().describe(`Network sub-action: enable, get, clear, or export-har`),code:f.string().optional().describe(`JavaScript expression or IIFE evaluated in page context.`),timeoutMs:f.number().min(1).max(6e4).optional().describe(`Timeout in milliseconds`),filter:f.object({resourceTypes:f.array(f.string()).optional(),urlPattern:f.string().optional(),excludeUrls:f.array(f.string()).optional()}).optional().describe(`Network capture filter for resource types and URL patterns`),showSensitive:f.boolean().optional().describe(`Include sensitive headers instead of redacting them`),includeBody:f.boolean().optional().describe(`Include truncated request bodies in captured entries`),bufferSize:f.number().int().min(1).max(1e4).optional().describe(`Buffer capacity (1-10000, default varies by action)`),consoleSubAction:f.enum(V).optional().describe(`Console sub-action: enable, get, or clear`),level:f.string().optional().describe(`Filter console entries by level (log/info/warn/error/debug)`),fetchUrl:f.string().optional().describe(`URL for fetch action (http/https only). Uses page cookies/session.`),fetchMethod:f.enum(H).optional().describe(`HTTP method for fetch action (default: GET)`),fetchHeaders:f.record(f.string(),f.string()).optional().describe(`Custom headers for fetch action`),fetchBody:f.string().optional().describe(`Request body for fetch action (POST/PUT/PATCH)`),includeResponseHeaders:f.boolean().optional().describe(`Include response headers in fetch output (default: true)`),fullPage:f.boolean().optional().describe(`Capture full page`),redactPasswords:f.boolean().optional().describe(`Mask password fields`),readMode:f.enum([`snapshot`,`dom`,`markdown`,`text`]).optional().describe(`Extraction mode for read action: snapshot (ARIA tree), dom (HTML), markdown (clean MD), text (plain text)`),clip:f.object({x:f.number(),y:f.number(),width:f.number(),height:f.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),format:f.enum([`png`,`jpeg`]).optional().describe(`Screenshot image format (default: png)`),quality:f.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`),cookies:f.array(f.object({name:f.string(),value:f.string(),url:f.string().optional(),domain:f.string().optional(),path:f.string().optional(),expires:f.number().optional(),httpOnly:f.boolean().optional(),secure:f.boolean().optional(),sameSite:f.enum([`Lax`,`None`,`Strict`]).optional()})).optional().describe(`Cookies to set (for session set-cookie action)`),name:f.string().optional().describe(`Cookie name for delete-cookie action`),storageType:f.enum([`localStorage`,`sessionStorage`]).optional().describe(`Storage type for get/set/clear-storage actions`),storageKey:f.string().optional().describe(`Storage key to get or set`),storageValue:f.string().optional().describe(`Value to set in storage`),accept:f.boolean().optional().describe(`Accept or dismiss dialog`),promptText:f.string().optional().describe(`Text for prompt dialog`),sessionAction:f.enum(U).optional().describe(`Session sub-action (session only)`),confirm:f.boolean().optional().describe(`Explicit confirmation for cookie export`),steps:f.array(f.record(f.string(),f.unknown())).optional().describe(`Array of action steps for batch execution. Each step is an object with action + action-specific params.`)};function W(e,t){return typeof e==`string`&&t.includes(e)}function G(e,t,n){let r=e[t];if(typeof r!=`string`)throw Error(`${n} requires ${t}`);return r}function je(e,t,n){let r=e[t];if(typeof r!=`boolean`)throw Error(`${n} requires ${t}`);return r}function K(e,t){let n=e[t];return typeof n==`string`?n:void 0}function q(e,t){let n=e[t];return typeof n==`boolean`?n:void 0}function J(e,t){let n=e[t];return typeof n==`number`?n:void 0}function Y(e,t){let n=e[t];return typeof n==`object`&&n?n:void 0}function Me(e,t){let n=e.steps;if(!Array.isArray(n)||n.length===0)throw Error(`${t} requires non-empty steps`);return n.map((e,n)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`${t} step ${n+1} must be an object`);return e})}function X(e,t,n,r){let i=e[t];if(!W(i,n))throw Error(`${r} requires ${t}`);return i}function Z(e,t,n,r){let i=e[t];if(i!==void 0){if(!W(i,n))throw Error(`${r} received invalid ${t}`);return i}}function Ne(e,t){let n=K(e,`pageId`);if(n)return E(t).resolvePageId(n)}function Q(e,t,n){let r=K(e,`pageId`);if(!r)throw Error(`${n} requires pageId`);return E(t).resolvePageId(r)}async function $(e,t,n){let r=X(e,`action`,F,`browser`);switch(r){case`open`:return t.openHandler({url:G(e,`url`,`browser(open)`),mode:Z(e,`mode`,I,`browser(open)`),forceNew:q(e,`forceNew`),label:K(e,`label`),autoDialog:q(e,`autoDialog`),waitUntil:Z(e,`waitUntil`,L,`browser(open)`)});case`batch`:{let r=Me(e,`browser(batch)`),i=[],a=0;for(let e=0;e<r.length;e+=1){let o=r[e],s=K(o,`action`)??`unknown`;try{let r=await $(o,t,n);a+=1,i.push({index:e,action:s,ok:!0,result:r})}catch(t){i.push({index:e,action:s,ok:!1,error:t instanceof Error?t.message:String(t)})}}return N(`Batch completed: ${a}/${r.length} steps succeeded`,{total:r.length,succeeded:a,steps:i})}case`read`:return t.readHandler({pageId:Q(e,n,`browser(read)`),mode:Z(e,`readMode`,[`snapshot`,`dom`,`markdown`,`text`],`browser(read)`),selector:K(e,`selector`)});case`act`:return t.actHandler({pageId:Q(e,n,`browser(act)`),kind:X(e,`kind`,R,`browser(act)`),ref:K(e,`ref`),selector:K(e,`selector`),element:K(e,`element`),text:K(e,`text`),key:K(e,`key`),value:K(e,`value`),fromRef:K(e,`fromRef`),fromSelector:K(e,`fromSelector`),toRef:K(e,`toRef`),toSelector:K(e,`toSelector`)});case`navigate`:return t.navigateHandler({pageId:Q(e,n,`browser(navigate)`),url:K(e,`url`),type:Z(e,`type`,z,`browser(navigate)`),selector:K(e,`selector`),timeoutMs:J(e,`timeoutMs`)});case`network`:return t.networkHandler({pageId:Q(e,n,`browser(network)`),subAction:X(e,`subAction`,B,`browser(network)`),filter:Y(e,`filter`),showSensitive:q(e,`showSensitive`),includeBody:q(e,`includeBody`),bufferSize:J(e,`bufferSize`)});case`console`:return t.consoleHandler({pageId:Q(e,n,`browser(console)`),subAction:X(e,`consoleSubAction`,V,`browser(console)`),level:K(e,`level`),bufferSize:J(e,`bufferSize`)});case`fetch`:return t.fetchHandler({pageId:Q(e,n,`browser(fetch)`),url:G(e,`fetchUrl`,`browser(fetch)`),method:Z(e,`fetchMethod`,H,`browser(fetch)`),headers:Y(e,`fetchHeaders`),body:K(e,`fetchBody`),timeoutMs:J(e,`timeoutMs`),includeHeaders:q(e,`includeResponseHeaders`)});case`eval`:return t.evalHandler({pageId:Q(e,n,`browser(eval)`),code:G(e,`code`,`browser(eval)`),timeoutMs:J(e,`timeoutMs`)});case`diff`:return t.diffHandler({pageId:Q(e,n,`browser(diff)`)});case`screenshot`:{let r=Y(e,`clip`);return t.screenshotHandler({pageId:Q(e,n,`browser(screenshot)`),ref:K(e,`ref`),selector:K(e,`selector`),fullPage:q(e,`fullPage`),redactPasswords:q(e,`redactPasswords`),clip:r,format:Z(e,`format`,[`png`,`jpeg`],`browser(screenshot)`),quality:J(e,`quality`)})}case`dialog`:return t.dialogHandler({pageId:Q(e,n,`browser(dialog)`),accept:je(e,`accept`,`browser(dialog)`),promptText:K(e,`promptText`)});case`session`:return t.sessionHandler({sessionAction:X(e,`sessionAction`,U,`browser(session)`),pageId:K(e,`pageId`)&&Ne(e,n),confirm:q(e,`confirm`),cookies:Y(e,`cookies`),name:K(e,`name`),storageType:Z(e,`storageType`,[`localStorage`,`sessionStorage`],`browser(session)`),storageKey:K(e,`storageKey`),storageValue:K(e,`storageValue`)});default:return N(`Unknown action: ${r}`,{error:`Unknown action: ${r}`})}}function Pe(e,t){let n=ge(t),r={openHandler:Ee(n),readHandler:De(n),actHandler:_e(n),navigateHandler:we(n),networkHandler:Te(n),consoleHandler:ve(n),fetchHandler:Se(n),evalHandler:xe(n),diffHandler:be(n),screenshotHandler:Oe(n),dialogHandler:ye(n),sessionHandler:ke(n)};e.tool(`browser`,`Unified browser automation tool using agent-browser (Chrome DevTools Protocol). Provides open, read, act, navigate, screenshot, eval, dialog, network, console, fetch, diff, session actions.`,Ae,async e=>{try{let t=await $(e,r,n);return{content:t.content,structuredContent:t.structuredContent}}catch(e){return{content:[{type:`text`,text:`Browser error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}})}export{de as BrowserEngine,w as DEFAULT_BROWSER_CONFIG,ue as SessionRegistry,C as TabRegistry,x as autoSelectMode,fe as closeEngine,b as ensureAgentBrowserInstalled,oe as getAgentBrowserVersion,S as getDaemonArgs,h as getDefaultBrowsersPath,E as getEngine,le as getLaunchArgs,se as isBrowserReady,D as isUrlAllowed,Pe as registerBrowserTools,g as resolveBrowsersPath,pe as validateEvalResult};
@@ -17,7 +17,6 @@
17
17
  "test:watch": "vitest"
18
18
  },
19
19
  "dependencies": {
20
- "playwright-core": "^1.x",
21
20
  "zod": "^4.x"
22
21
  },
23
22
  "devDependencies": {