agentkernel 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +314 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +162 -1
- package/dist/index.d.ts +162 -1
- package/dist/index.js +312 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -115,6 +115,138 @@ interface ApiResponse<T> {
|
|
|
115
115
|
data?: T;
|
|
116
116
|
error?: string;
|
|
117
117
|
}
|
|
118
|
+
/** Response from extending a sandbox's TTL. */
|
|
119
|
+
interface ExtendTtlResponse {
|
|
120
|
+
expires_at?: string;
|
|
121
|
+
}
|
|
122
|
+
/** Options for extending TTL. */
|
|
123
|
+
interface ExtendTtlOptions {
|
|
124
|
+
/** Duration string like "1h", "30m", "2d". */
|
|
125
|
+
by: string;
|
|
126
|
+
}
|
|
127
|
+
/** Metadata for a sandbox snapshot. */
|
|
128
|
+
interface SnapshotMeta {
|
|
129
|
+
name: string;
|
|
130
|
+
sandbox: string;
|
|
131
|
+
image_tag: string;
|
|
132
|
+
backend: string;
|
|
133
|
+
base_image?: string;
|
|
134
|
+
vcpus?: number;
|
|
135
|
+
memory_mb?: number;
|
|
136
|
+
created_at: string;
|
|
137
|
+
}
|
|
138
|
+
/** Options for taking a snapshot. */
|
|
139
|
+
interface TakeSnapshotOptions {
|
|
140
|
+
/** Name of the sandbox to snapshot. */
|
|
141
|
+
sandbox: string;
|
|
142
|
+
/** Optional snapshot name (auto-generated if not provided). */
|
|
143
|
+
name?: string;
|
|
144
|
+
}
|
|
145
|
+
/** A link extracted from a web page. */
|
|
146
|
+
interface PageLink {
|
|
147
|
+
text: string;
|
|
148
|
+
href: string;
|
|
149
|
+
}
|
|
150
|
+
/** Result of navigating to a web page. */
|
|
151
|
+
interface PageResult {
|
|
152
|
+
/** Page title. */
|
|
153
|
+
title: string;
|
|
154
|
+
/** Final URL after redirects. */
|
|
155
|
+
url: string;
|
|
156
|
+
/** Page body text (truncated to ~8KB). */
|
|
157
|
+
text: string;
|
|
158
|
+
/** First 50 links on the page. */
|
|
159
|
+
links: PageLink[];
|
|
160
|
+
}
|
|
161
|
+
/** ARIA accessibility tree snapshot of a web page (v2). */
|
|
162
|
+
interface AriaSnapshot {
|
|
163
|
+
/** ARIA tree as YAML. */
|
|
164
|
+
snapshot: string;
|
|
165
|
+
/** Current page URL. */
|
|
166
|
+
url: string;
|
|
167
|
+
/** Page title. */
|
|
168
|
+
title: string;
|
|
169
|
+
/** Available ref IDs (e.g. ["e1", "e2", ...]). */
|
|
170
|
+
refs: string[];
|
|
171
|
+
}
|
|
172
|
+
/** A browser interaction event for debugging and context recovery. */
|
|
173
|
+
interface BrowserEvent {
|
|
174
|
+
/** Monotonic sequence number. */
|
|
175
|
+
seq: number;
|
|
176
|
+
/** Event type (e.g. "page.navigated", "page.clicked"). */
|
|
177
|
+
type: string;
|
|
178
|
+
/** Page name. */
|
|
179
|
+
page: string;
|
|
180
|
+
/** ISO 8601 timestamp. */
|
|
181
|
+
ts: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Browser session for orchestrating headless browsers in sandboxes.
|
|
186
|
+
*
|
|
187
|
+
* Each method generates a self-contained Python/Playwright script,
|
|
188
|
+
* runs it inside the sandbox, and parses the JSON result.
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
type RunInSandboxFn = (name: string, command: string[]) => Promise<RunOutput>;
|
|
192
|
+
type RemoveSandboxFn = (name: string) => Promise<void>;
|
|
193
|
+
/** Command to install Playwright + Chromium inside the sandbox. */
|
|
194
|
+
declare const BROWSER_SETUP_CMD: string[];
|
|
195
|
+
/**
|
|
196
|
+
* A sandboxed headless browser controlled from outside.
|
|
197
|
+
*
|
|
198
|
+
* The browser (Chromium via Playwright) runs inside an agentkernel sandbox.
|
|
199
|
+
* You call high-level methods; the SDK generates and runs scripts internally.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* await using browser = await client.browser("my-browser");
|
|
204
|
+
* const page = await browser.goto("https://example.com");
|
|
205
|
+
* console.log(page.title, page.links);
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare class BrowserSession implements AsyncDisposable {
|
|
209
|
+
readonly name: string;
|
|
210
|
+
private _removed;
|
|
211
|
+
private _lastUrl;
|
|
212
|
+
private readonly _run;
|
|
213
|
+
private readonly _remove;
|
|
214
|
+
/** @internal */
|
|
215
|
+
constructor(name: string, runFn: RunInSandboxFn, removeFn: RemoveSandboxFn);
|
|
216
|
+
/** Navigate to a URL and return page data (title, text, links). */
|
|
217
|
+
goto(url: string): Promise<PageResult>;
|
|
218
|
+
/** Take a PNG screenshot. Returns a base64-encoded string. */
|
|
219
|
+
screenshot(url?: string): Promise<string>;
|
|
220
|
+
/** Run a JavaScript expression on a page and return the result. */
|
|
221
|
+
evaluate(expression: string, url?: string): Promise<unknown>;
|
|
222
|
+
/** Navigate to a URL and return an ARIA snapshot. */
|
|
223
|
+
open(url: string, page?: string): Promise<AriaSnapshot>;
|
|
224
|
+
/** Get the current ARIA snapshot without navigating. */
|
|
225
|
+
snapshot(page?: string): Promise<AriaSnapshot>;
|
|
226
|
+
/** Click an element by ref ID or CSS selector. Returns new snapshot. */
|
|
227
|
+
click(opts: {
|
|
228
|
+
ref?: string;
|
|
229
|
+
selector?: string;
|
|
230
|
+
page?: string;
|
|
231
|
+
}): Promise<AriaSnapshot>;
|
|
232
|
+
/** Fill an input by ref ID or CSS selector. Returns new snapshot. */
|
|
233
|
+
fill(value: string, opts: {
|
|
234
|
+
ref?: string;
|
|
235
|
+
selector?: string;
|
|
236
|
+
page?: string;
|
|
237
|
+
}): Promise<AriaSnapshot>;
|
|
238
|
+
/** Close a named page. */
|
|
239
|
+
closePage(page?: string): Promise<void>;
|
|
240
|
+
/** List active page names. */
|
|
241
|
+
listPages(): Promise<string[]>;
|
|
242
|
+
private _serverStarted;
|
|
243
|
+
private _ensureServer;
|
|
244
|
+
private _browserRequest;
|
|
245
|
+
/** Remove the sandbox. Idempotent. */
|
|
246
|
+
remove(): Promise<void>;
|
|
247
|
+
/** Auto-cleanup for `await using`. */
|
|
248
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
249
|
+
}
|
|
118
250
|
|
|
119
251
|
type ExecFn = (name: string, command: string[], opts?: ExecOptions) => Promise<RunOutput>;
|
|
120
252
|
type RemoveFn = (name: string) => Promise<void>;
|
|
@@ -230,6 +362,35 @@ declare class AgentKernel {
|
|
|
230
362
|
* ```
|
|
231
363
|
*/
|
|
232
364
|
sandbox(name: string, opts?: CreateSandboxOptions): Promise<SandboxSession>;
|
|
365
|
+
/**
|
|
366
|
+
* Create a sandboxed browser session with automatic cleanup.
|
|
367
|
+
*
|
|
368
|
+
* Creates a sandbox with Chromium pre-installed via Playwright.
|
|
369
|
+
* Use `goto()`, `screenshot()`, and `evaluate()` to interact with web pages.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* await using browser = await client.browser("my-browser");
|
|
374
|
+
* const page = await browser.goto("https://example.com");
|
|
375
|
+
* console.log(page.title, page.links);
|
|
376
|
+
* // sandbox auto-removed when scope exits
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
browser(name: string, opts?: {
|
|
380
|
+
memory_mb?: number;
|
|
381
|
+
}): Promise<BrowserSession>;
|
|
382
|
+
/** Extend a sandbox's TTL. Returns the new expiry time. */
|
|
383
|
+
extendTtl(name: string, opts: ExtendTtlOptions): Promise<ExtendTtlResponse>;
|
|
384
|
+
/** List all snapshots. */
|
|
385
|
+
listSnapshots(): Promise<SnapshotMeta[]>;
|
|
386
|
+
/** Take a snapshot of a sandbox. */
|
|
387
|
+
takeSnapshot(opts: TakeSnapshotOptions): Promise<SnapshotMeta>;
|
|
388
|
+
/** Get info about a snapshot. */
|
|
389
|
+
getSnapshot(name: string): Promise<SnapshotMeta>;
|
|
390
|
+
/** Delete a snapshot. */
|
|
391
|
+
deleteSnapshot(name: string): Promise<void>;
|
|
392
|
+
/** Restore a sandbox from a snapshot. */
|
|
393
|
+
restoreSnapshot(name: string): Promise<SandboxInfo>;
|
|
233
394
|
private headers;
|
|
234
395
|
private fetch;
|
|
235
396
|
private request;
|
|
@@ -268,4 +429,4 @@ declare class StreamError extends AgentKernelError {
|
|
|
268
429
|
constructor(message?: string);
|
|
269
430
|
}
|
|
270
431
|
|
|
271
|
-
export { AgentKernel, AgentKernelError, type AgentKernelOptions, type ApiResponse, AuthError, type BatchFileWriteResponse, type CreateSandboxOptions, type DetachedCommand, type DetachedLogsResponse, type DetachedStatus, type ExecOptions, NetworkError, NotFoundError, type RunOptions, type RunOutput, type SandboxInfo, SandboxSession, type SandboxStatus, type SecurityProfile, ServerError, StreamError, type StreamEvent, type StreamEventType, ValidationError };
|
|
432
|
+
export { AgentKernel, AgentKernelError, type AgentKernelOptions, type ApiResponse, type AriaSnapshot, AuthError, BROWSER_SETUP_CMD, type BatchFileWriteResponse, type BrowserEvent, BrowserSession, type CreateSandboxOptions, type DetachedCommand, type DetachedLogsResponse, type DetachedStatus, type ExecOptions, type ExtendTtlOptions, type ExtendTtlResponse, NetworkError, NotFoundError, type PageLink, type PageResult, type RunOptions, type RunOutput, type SandboxInfo, SandboxSession, type SandboxStatus, type SecurityProfile, ServerError, type SnapshotMeta, StreamError, type StreamEvent, type StreamEventType, type TakeSnapshotOptions, ValidationError };
|
package/dist/index.d.ts
CHANGED
|
@@ -115,6 +115,138 @@ interface ApiResponse<T> {
|
|
|
115
115
|
data?: T;
|
|
116
116
|
error?: string;
|
|
117
117
|
}
|
|
118
|
+
/** Response from extending a sandbox's TTL. */
|
|
119
|
+
interface ExtendTtlResponse {
|
|
120
|
+
expires_at?: string;
|
|
121
|
+
}
|
|
122
|
+
/** Options for extending TTL. */
|
|
123
|
+
interface ExtendTtlOptions {
|
|
124
|
+
/** Duration string like "1h", "30m", "2d". */
|
|
125
|
+
by: string;
|
|
126
|
+
}
|
|
127
|
+
/** Metadata for a sandbox snapshot. */
|
|
128
|
+
interface SnapshotMeta {
|
|
129
|
+
name: string;
|
|
130
|
+
sandbox: string;
|
|
131
|
+
image_tag: string;
|
|
132
|
+
backend: string;
|
|
133
|
+
base_image?: string;
|
|
134
|
+
vcpus?: number;
|
|
135
|
+
memory_mb?: number;
|
|
136
|
+
created_at: string;
|
|
137
|
+
}
|
|
138
|
+
/** Options for taking a snapshot. */
|
|
139
|
+
interface TakeSnapshotOptions {
|
|
140
|
+
/** Name of the sandbox to snapshot. */
|
|
141
|
+
sandbox: string;
|
|
142
|
+
/** Optional snapshot name (auto-generated if not provided). */
|
|
143
|
+
name?: string;
|
|
144
|
+
}
|
|
145
|
+
/** A link extracted from a web page. */
|
|
146
|
+
interface PageLink {
|
|
147
|
+
text: string;
|
|
148
|
+
href: string;
|
|
149
|
+
}
|
|
150
|
+
/** Result of navigating to a web page. */
|
|
151
|
+
interface PageResult {
|
|
152
|
+
/** Page title. */
|
|
153
|
+
title: string;
|
|
154
|
+
/** Final URL after redirects. */
|
|
155
|
+
url: string;
|
|
156
|
+
/** Page body text (truncated to ~8KB). */
|
|
157
|
+
text: string;
|
|
158
|
+
/** First 50 links on the page. */
|
|
159
|
+
links: PageLink[];
|
|
160
|
+
}
|
|
161
|
+
/** ARIA accessibility tree snapshot of a web page (v2). */
|
|
162
|
+
interface AriaSnapshot {
|
|
163
|
+
/** ARIA tree as YAML. */
|
|
164
|
+
snapshot: string;
|
|
165
|
+
/** Current page URL. */
|
|
166
|
+
url: string;
|
|
167
|
+
/** Page title. */
|
|
168
|
+
title: string;
|
|
169
|
+
/** Available ref IDs (e.g. ["e1", "e2", ...]). */
|
|
170
|
+
refs: string[];
|
|
171
|
+
}
|
|
172
|
+
/** A browser interaction event for debugging and context recovery. */
|
|
173
|
+
interface BrowserEvent {
|
|
174
|
+
/** Monotonic sequence number. */
|
|
175
|
+
seq: number;
|
|
176
|
+
/** Event type (e.g. "page.navigated", "page.clicked"). */
|
|
177
|
+
type: string;
|
|
178
|
+
/** Page name. */
|
|
179
|
+
page: string;
|
|
180
|
+
/** ISO 8601 timestamp. */
|
|
181
|
+
ts: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Browser session for orchestrating headless browsers in sandboxes.
|
|
186
|
+
*
|
|
187
|
+
* Each method generates a self-contained Python/Playwright script,
|
|
188
|
+
* runs it inside the sandbox, and parses the JSON result.
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
type RunInSandboxFn = (name: string, command: string[]) => Promise<RunOutput>;
|
|
192
|
+
type RemoveSandboxFn = (name: string) => Promise<void>;
|
|
193
|
+
/** Command to install Playwright + Chromium inside the sandbox. */
|
|
194
|
+
declare const BROWSER_SETUP_CMD: string[];
|
|
195
|
+
/**
|
|
196
|
+
* A sandboxed headless browser controlled from outside.
|
|
197
|
+
*
|
|
198
|
+
* The browser (Chromium via Playwright) runs inside an agentkernel sandbox.
|
|
199
|
+
* You call high-level methods; the SDK generates and runs scripts internally.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* await using browser = await client.browser("my-browser");
|
|
204
|
+
* const page = await browser.goto("https://example.com");
|
|
205
|
+
* console.log(page.title, page.links);
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare class BrowserSession implements AsyncDisposable {
|
|
209
|
+
readonly name: string;
|
|
210
|
+
private _removed;
|
|
211
|
+
private _lastUrl;
|
|
212
|
+
private readonly _run;
|
|
213
|
+
private readonly _remove;
|
|
214
|
+
/** @internal */
|
|
215
|
+
constructor(name: string, runFn: RunInSandboxFn, removeFn: RemoveSandboxFn);
|
|
216
|
+
/** Navigate to a URL and return page data (title, text, links). */
|
|
217
|
+
goto(url: string): Promise<PageResult>;
|
|
218
|
+
/** Take a PNG screenshot. Returns a base64-encoded string. */
|
|
219
|
+
screenshot(url?: string): Promise<string>;
|
|
220
|
+
/** Run a JavaScript expression on a page and return the result. */
|
|
221
|
+
evaluate(expression: string, url?: string): Promise<unknown>;
|
|
222
|
+
/** Navigate to a URL and return an ARIA snapshot. */
|
|
223
|
+
open(url: string, page?: string): Promise<AriaSnapshot>;
|
|
224
|
+
/** Get the current ARIA snapshot without navigating. */
|
|
225
|
+
snapshot(page?: string): Promise<AriaSnapshot>;
|
|
226
|
+
/** Click an element by ref ID or CSS selector. Returns new snapshot. */
|
|
227
|
+
click(opts: {
|
|
228
|
+
ref?: string;
|
|
229
|
+
selector?: string;
|
|
230
|
+
page?: string;
|
|
231
|
+
}): Promise<AriaSnapshot>;
|
|
232
|
+
/** Fill an input by ref ID or CSS selector. Returns new snapshot. */
|
|
233
|
+
fill(value: string, opts: {
|
|
234
|
+
ref?: string;
|
|
235
|
+
selector?: string;
|
|
236
|
+
page?: string;
|
|
237
|
+
}): Promise<AriaSnapshot>;
|
|
238
|
+
/** Close a named page. */
|
|
239
|
+
closePage(page?: string): Promise<void>;
|
|
240
|
+
/** List active page names. */
|
|
241
|
+
listPages(): Promise<string[]>;
|
|
242
|
+
private _serverStarted;
|
|
243
|
+
private _ensureServer;
|
|
244
|
+
private _browserRequest;
|
|
245
|
+
/** Remove the sandbox. Idempotent. */
|
|
246
|
+
remove(): Promise<void>;
|
|
247
|
+
/** Auto-cleanup for `await using`. */
|
|
248
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
249
|
+
}
|
|
118
250
|
|
|
119
251
|
type ExecFn = (name: string, command: string[], opts?: ExecOptions) => Promise<RunOutput>;
|
|
120
252
|
type RemoveFn = (name: string) => Promise<void>;
|
|
@@ -230,6 +362,35 @@ declare class AgentKernel {
|
|
|
230
362
|
* ```
|
|
231
363
|
*/
|
|
232
364
|
sandbox(name: string, opts?: CreateSandboxOptions): Promise<SandboxSession>;
|
|
365
|
+
/**
|
|
366
|
+
* Create a sandboxed browser session with automatic cleanup.
|
|
367
|
+
*
|
|
368
|
+
* Creates a sandbox with Chromium pre-installed via Playwright.
|
|
369
|
+
* Use `goto()`, `screenshot()`, and `evaluate()` to interact with web pages.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* await using browser = await client.browser("my-browser");
|
|
374
|
+
* const page = await browser.goto("https://example.com");
|
|
375
|
+
* console.log(page.title, page.links);
|
|
376
|
+
* // sandbox auto-removed when scope exits
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
browser(name: string, opts?: {
|
|
380
|
+
memory_mb?: number;
|
|
381
|
+
}): Promise<BrowserSession>;
|
|
382
|
+
/** Extend a sandbox's TTL. Returns the new expiry time. */
|
|
383
|
+
extendTtl(name: string, opts: ExtendTtlOptions): Promise<ExtendTtlResponse>;
|
|
384
|
+
/** List all snapshots. */
|
|
385
|
+
listSnapshots(): Promise<SnapshotMeta[]>;
|
|
386
|
+
/** Take a snapshot of a sandbox. */
|
|
387
|
+
takeSnapshot(opts: TakeSnapshotOptions): Promise<SnapshotMeta>;
|
|
388
|
+
/** Get info about a snapshot. */
|
|
389
|
+
getSnapshot(name: string): Promise<SnapshotMeta>;
|
|
390
|
+
/** Delete a snapshot. */
|
|
391
|
+
deleteSnapshot(name: string): Promise<void>;
|
|
392
|
+
/** Restore a sandbox from a snapshot. */
|
|
393
|
+
restoreSnapshot(name: string): Promise<SandboxInfo>;
|
|
233
394
|
private headers;
|
|
234
395
|
private fetch;
|
|
235
396
|
private request;
|
|
@@ -268,4 +429,4 @@ declare class StreamError extends AgentKernelError {
|
|
|
268
429
|
constructor(message?: string);
|
|
269
430
|
}
|
|
270
431
|
|
|
271
|
-
export { AgentKernel, AgentKernelError, type AgentKernelOptions, type ApiResponse, AuthError, type BatchFileWriteResponse, type CreateSandboxOptions, type DetachedCommand, type DetachedLogsResponse, type DetachedStatus, type ExecOptions, NetworkError, NotFoundError, type RunOptions, type RunOutput, type SandboxInfo, SandboxSession, type SandboxStatus, type SecurityProfile, ServerError, StreamError, type StreamEvent, type StreamEventType, ValidationError };
|
|
432
|
+
export { AgentKernel, AgentKernelError, type AgentKernelOptions, type ApiResponse, type AriaSnapshot, AuthError, BROWSER_SETUP_CMD, type BatchFileWriteResponse, type BrowserEvent, BrowserSession, type CreateSandboxOptions, type DetachedCommand, type DetachedLogsResponse, type DetachedStatus, type ExecOptions, type ExtendTtlOptions, type ExtendTtlResponse, NetworkError, NotFoundError, type PageLink, type PageResult, type RunOptions, type RunOutput, type SandboxInfo, SandboxSession, type SandboxStatus, type SecurityProfile, ServerError, type SnapshotMeta, StreamError, type StreamEvent, type StreamEventType, type TakeSnapshotOptions, ValidationError };
|
package/dist/index.js
CHANGED
|
@@ -79,6 +79,248 @@ function errorFromStatus(status, body) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// src/browser.ts
|
|
83
|
+
var GOTO_SCRIPT = `
|
|
84
|
+
import asyncio, json, sys
|
|
85
|
+
from playwright.async_api import async_playwright
|
|
86
|
+
async def main():
|
|
87
|
+
url = sys.argv[1]
|
|
88
|
+
async with async_playwright() as p:
|
|
89
|
+
b = await p.chromium.launch()
|
|
90
|
+
page = await b.new_page()
|
|
91
|
+
await page.goto(url, timeout=30000)
|
|
92
|
+
title = await page.title()
|
|
93
|
+
url_final = page.url
|
|
94
|
+
text = await page.evaluate("() => document.body.innerText.slice(0, 8000)")
|
|
95
|
+
links = await page.evaluate('''() =>
|
|
96
|
+
Array.from(document.querySelectorAll('a[href]'))
|
|
97
|
+
.slice(0, 50)
|
|
98
|
+
.map(a => ({text: a.textContent.trim(), href: a.href}))
|
|
99
|
+
.filter(l => l.href.startsWith("http"))
|
|
100
|
+
''')
|
|
101
|
+
print(json.dumps({"title": title, "url": url_final, "text": text, "links": links}))
|
|
102
|
+
await b.close()
|
|
103
|
+
asyncio.run(main())
|
|
104
|
+
`;
|
|
105
|
+
var SCREENSHOT_SCRIPT = `
|
|
106
|
+
import asyncio, base64, sys
|
|
107
|
+
from playwright.async_api import async_playwright
|
|
108
|
+
async def main():
|
|
109
|
+
url = sys.argv[1]
|
|
110
|
+
async with async_playwright() as p:
|
|
111
|
+
b = await p.chromium.launch()
|
|
112
|
+
page = await b.new_page()
|
|
113
|
+
await page.goto(url, timeout=30000)
|
|
114
|
+
data = await page.screenshot()
|
|
115
|
+
print(base64.b64encode(data).decode())
|
|
116
|
+
await b.close()
|
|
117
|
+
asyncio.run(main())
|
|
118
|
+
`;
|
|
119
|
+
var EVALUATE_SCRIPT = `
|
|
120
|
+
import asyncio, json, sys
|
|
121
|
+
from playwright.async_api import async_playwright
|
|
122
|
+
async def main():
|
|
123
|
+
url = sys.argv[1]
|
|
124
|
+
expr = sys.argv[2]
|
|
125
|
+
async with async_playwright() as p:
|
|
126
|
+
b = await p.chromium.launch()
|
|
127
|
+
page = await b.new_page()
|
|
128
|
+
await page.goto(url, timeout=30000)
|
|
129
|
+
result = await page.evaluate(expr)
|
|
130
|
+
print(json.dumps(result))
|
|
131
|
+
await b.close()
|
|
132
|
+
asyncio.run(main())
|
|
133
|
+
`;
|
|
134
|
+
var BROWSER_SETUP_CMD = [
|
|
135
|
+
"sh",
|
|
136
|
+
"-c",
|
|
137
|
+
"pip install -q playwright && playwright install --with-deps chromium"
|
|
138
|
+
];
|
|
139
|
+
var BROWSER_HEALTH_SCRIPT = `
|
|
140
|
+
import json, urllib.request, sys
|
|
141
|
+
port = sys.argv[1] if len(sys.argv) > 1 else "9222"
|
|
142
|
+
try:
|
|
143
|
+
req = urllib.request.urlopen(f"http://127.0.0.1:{port}/health", timeout=5)
|
|
144
|
+
print(req.read().decode())
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(json.dumps({"status": "down", "error": str(e)}))
|
|
147
|
+
sys.exit(1)
|
|
148
|
+
`;
|
|
149
|
+
var BROWSER_REQUEST_SCRIPT = `
|
|
150
|
+
import json, urllib.request, sys
|
|
151
|
+
port = sys.argv[1] if len(sys.argv) > 1 else "9222"
|
|
152
|
+
method = sys.argv[2] if len(sys.argv) > 2 else "GET"
|
|
153
|
+
path = sys.argv[3] if len(sys.argv) > 3 else "/health"
|
|
154
|
+
body_str = sys.argv[4] if len(sys.argv) > 4 else None
|
|
155
|
+
url = f"http://127.0.0.1:{port}{path}"
|
|
156
|
+
data = body_str.encode() if body_str else None
|
|
157
|
+
req = urllib.request.Request(url, data=data, method=method)
|
|
158
|
+
if data:
|
|
159
|
+
req.add_header("Content-Type", "application/json")
|
|
160
|
+
try:
|
|
161
|
+
resp = urllib.request.urlopen(req, timeout=60)
|
|
162
|
+
print(resp.read().decode())
|
|
163
|
+
except urllib.error.HTTPError as e:
|
|
164
|
+
print(e.read().decode())
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
print(json.dumps({"error": str(e)}))
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
`;
|
|
170
|
+
var BrowserSession = class {
|
|
171
|
+
name;
|
|
172
|
+
_removed = false;
|
|
173
|
+
_lastUrl = null;
|
|
174
|
+
_run;
|
|
175
|
+
_remove;
|
|
176
|
+
/** @internal */
|
|
177
|
+
constructor(name, runFn, removeFn) {
|
|
178
|
+
this.name = name;
|
|
179
|
+
this._run = runFn;
|
|
180
|
+
this._remove = removeFn;
|
|
181
|
+
}
|
|
182
|
+
/** Navigate to a URL and return page data (title, text, links). */
|
|
183
|
+
async goto(url) {
|
|
184
|
+
const result = await this._run(this.name, [
|
|
185
|
+
"python3",
|
|
186
|
+
"-c",
|
|
187
|
+
GOTO_SCRIPT,
|
|
188
|
+
url
|
|
189
|
+
]);
|
|
190
|
+
this._lastUrl = url;
|
|
191
|
+
return JSON.parse(result.output);
|
|
192
|
+
}
|
|
193
|
+
/** Take a PNG screenshot. Returns a base64-encoded string. */
|
|
194
|
+
async screenshot(url) {
|
|
195
|
+
const target = url ?? this._lastUrl;
|
|
196
|
+
if (!target) {
|
|
197
|
+
throw new Error("No URL specified and no previous goto() call");
|
|
198
|
+
}
|
|
199
|
+
const result = await this._run(this.name, [
|
|
200
|
+
"python3",
|
|
201
|
+
"-c",
|
|
202
|
+
SCREENSHOT_SCRIPT,
|
|
203
|
+
target
|
|
204
|
+
]);
|
|
205
|
+
return result.output.trim();
|
|
206
|
+
}
|
|
207
|
+
/** Run a JavaScript expression on a page and return the result. */
|
|
208
|
+
async evaluate(expression, url) {
|
|
209
|
+
const target = url ?? this._lastUrl;
|
|
210
|
+
if (!target) {
|
|
211
|
+
throw new Error("No URL specified and no previous goto() call");
|
|
212
|
+
}
|
|
213
|
+
const result = await this._run(this.name, [
|
|
214
|
+
"python3",
|
|
215
|
+
"-c",
|
|
216
|
+
EVALUATE_SCRIPT,
|
|
217
|
+
target,
|
|
218
|
+
expression
|
|
219
|
+
]);
|
|
220
|
+
return JSON.parse(result.output);
|
|
221
|
+
}
|
|
222
|
+
// --- v2 methods (ARIA snapshots, persistent pages, ref-based interaction) ---
|
|
223
|
+
/** Navigate to a URL and return an ARIA snapshot. */
|
|
224
|
+
async open(url, page = "default") {
|
|
225
|
+
await this._ensureServer();
|
|
226
|
+
const result = await this._browserRequest(
|
|
227
|
+
"POST",
|
|
228
|
+
`/pages/${page}/goto`,
|
|
229
|
+
JSON.stringify({ url })
|
|
230
|
+
);
|
|
231
|
+
return JSON.parse(result.output);
|
|
232
|
+
}
|
|
233
|
+
/** Get the current ARIA snapshot without navigating. */
|
|
234
|
+
async snapshot(page = "default") {
|
|
235
|
+
await this._ensureServer();
|
|
236
|
+
const result = await this._browserRequest(
|
|
237
|
+
"GET",
|
|
238
|
+
`/pages/${page}/snapshot`
|
|
239
|
+
);
|
|
240
|
+
return JSON.parse(result.output);
|
|
241
|
+
}
|
|
242
|
+
/** Click an element by ref ID or CSS selector. Returns new snapshot. */
|
|
243
|
+
async click(opts) {
|
|
244
|
+
const page = opts.page ?? "default";
|
|
245
|
+
await this._ensureServer();
|
|
246
|
+
const body = {};
|
|
247
|
+
if (opts.ref) body.ref = opts.ref;
|
|
248
|
+
if (opts.selector) body.selector = opts.selector;
|
|
249
|
+
const result = await this._browserRequest(
|
|
250
|
+
"POST",
|
|
251
|
+
`/pages/${page}/click`,
|
|
252
|
+
JSON.stringify(body)
|
|
253
|
+
);
|
|
254
|
+
const data = JSON.parse(result.output);
|
|
255
|
+
if (data.error) throw new Error(data.error);
|
|
256
|
+
return data;
|
|
257
|
+
}
|
|
258
|
+
/** Fill an input by ref ID or CSS selector. Returns new snapshot. */
|
|
259
|
+
async fill(value, opts) {
|
|
260
|
+
const page = opts.page ?? "default";
|
|
261
|
+
await this._ensureServer();
|
|
262
|
+
const body = { value };
|
|
263
|
+
if (opts.ref) body.ref = opts.ref;
|
|
264
|
+
if (opts.selector) body.selector = opts.selector;
|
|
265
|
+
const result = await this._browserRequest(
|
|
266
|
+
"POST",
|
|
267
|
+
`/pages/${page}/fill`,
|
|
268
|
+
JSON.stringify(body)
|
|
269
|
+
);
|
|
270
|
+
const data = JSON.parse(result.output);
|
|
271
|
+
if (data.error) throw new Error(data.error);
|
|
272
|
+
return data;
|
|
273
|
+
}
|
|
274
|
+
/** Close a named page. */
|
|
275
|
+
async closePage(page = "default") {
|
|
276
|
+
await this._ensureServer();
|
|
277
|
+
await this._browserRequest("DELETE", `/pages/${page}`);
|
|
278
|
+
}
|
|
279
|
+
/** List active page names. */
|
|
280
|
+
async listPages() {
|
|
281
|
+
await this._ensureServer();
|
|
282
|
+
const result = await this._browserRequest("GET", "/pages");
|
|
283
|
+
const data = JSON.parse(result.output);
|
|
284
|
+
return data.pages ?? [];
|
|
285
|
+
}
|
|
286
|
+
_serverStarted = false;
|
|
287
|
+
async _ensureServer() {
|
|
288
|
+
if (this._serverStarted) return;
|
|
289
|
+
try {
|
|
290
|
+
const result = await this._run(this.name, [
|
|
291
|
+
"python3",
|
|
292
|
+
"-c",
|
|
293
|
+
BROWSER_HEALTH_SCRIPT,
|
|
294
|
+
"9222"
|
|
295
|
+
]);
|
|
296
|
+
if (result.output.includes('"status":"ok"') || result.output.includes('"status": "ok"')) {
|
|
297
|
+
this._serverStarted = true;
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
302
|
+
throw new Error(
|
|
303
|
+
"Browser server not running. Use browser_create to set up the sandbox, or use the MCP browser_open tool which auto-starts the server."
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
async _browserRequest(method, path, body) {
|
|
307
|
+
const cmd = ["python3", "-c", BROWSER_REQUEST_SCRIPT, "9222", method, path];
|
|
308
|
+
if (body) cmd.push(body);
|
|
309
|
+
return this._run(this.name, cmd);
|
|
310
|
+
}
|
|
311
|
+
// --- v1 methods (backward compatible) ---
|
|
312
|
+
/** Remove the sandbox. Idempotent. */
|
|
313
|
+
async remove() {
|
|
314
|
+
if (this._removed) return;
|
|
315
|
+
this._removed = true;
|
|
316
|
+
await this._remove(this.name);
|
|
317
|
+
}
|
|
318
|
+
/** Auto-cleanup for `await using`. */
|
|
319
|
+
async [Symbol.asyncDispose]() {
|
|
320
|
+
await this.remove();
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
82
324
|
// src/sandbox.ts
|
|
83
325
|
var SandboxSession = class {
|
|
84
326
|
name;
|
|
@@ -189,7 +431,7 @@ async function* parseSSE(body) {
|
|
|
189
431
|
}
|
|
190
432
|
|
|
191
433
|
// src/client.ts
|
|
192
|
-
var SDK_VERSION = "0.
|
|
434
|
+
var SDK_VERSION = "0.3.0";
|
|
193
435
|
var AgentKernel = class {
|
|
194
436
|
baseUrl;
|
|
195
437
|
apiKey;
|
|
@@ -259,7 +501,8 @@ var AgentKernel = class {
|
|
|
259
501
|
memory_mb: opts?.memory_mb,
|
|
260
502
|
profile: opts?.profile,
|
|
261
503
|
source_url: opts?.source_url,
|
|
262
|
-
source_ref: opts?.source_ref
|
|
504
|
+
source_ref: opts?.source_ref,
|
|
505
|
+
volumes: opts?.volumes
|
|
263
506
|
});
|
|
264
507
|
}
|
|
265
508
|
/** Get info about a sandbox. */
|
|
@@ -389,6 +632,71 @@ var AgentKernel = class {
|
|
|
389
632
|
(n, f) => this.writeFiles(n, f)
|
|
390
633
|
);
|
|
391
634
|
}
|
|
635
|
+
/**
|
|
636
|
+
* Create a sandboxed browser session with automatic cleanup.
|
|
637
|
+
*
|
|
638
|
+
* Creates a sandbox with Chromium pre-installed via Playwright.
|
|
639
|
+
* Use `goto()`, `screenshot()`, and `evaluate()` to interact with web pages.
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```ts
|
|
643
|
+
* await using browser = await client.browser("my-browser");
|
|
644
|
+
* const page = await browser.goto("https://example.com");
|
|
645
|
+
* console.log(page.title, page.links);
|
|
646
|
+
* // sandbox auto-removed when scope exits
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
async browser(name, opts) {
|
|
650
|
+
await this.createSandbox(name, {
|
|
651
|
+
image: "python:3.12-slim",
|
|
652
|
+
memory_mb: opts?.memory_mb ?? 2048,
|
|
653
|
+
profile: "moderate"
|
|
654
|
+
});
|
|
655
|
+
await this.execInSandbox(name, BROWSER_SETUP_CMD);
|
|
656
|
+
return new BrowserSession(
|
|
657
|
+
name,
|
|
658
|
+
(n, cmd) => this.execInSandbox(n, cmd),
|
|
659
|
+
(n) => this.removeSandbox(n)
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
// -- TTL & Snapshot methods --
|
|
663
|
+
/** Extend a sandbox's TTL. Returns the new expiry time. */
|
|
664
|
+
async extendTtl(name, opts) {
|
|
665
|
+
return this.request(
|
|
666
|
+
"POST",
|
|
667
|
+
`/sandboxes/${encodeURIComponent(name)}/extend`,
|
|
668
|
+
{ by: opts.by }
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
/** List all snapshots. */
|
|
672
|
+
async listSnapshots() {
|
|
673
|
+
return this.request("GET", "/snapshots");
|
|
674
|
+
}
|
|
675
|
+
/** Take a snapshot of a sandbox. */
|
|
676
|
+
async takeSnapshot(opts) {
|
|
677
|
+
return this.request("POST", "/snapshots", opts);
|
|
678
|
+
}
|
|
679
|
+
/** Get info about a snapshot. */
|
|
680
|
+
async getSnapshot(name) {
|
|
681
|
+
return this.request(
|
|
682
|
+
"GET",
|
|
683
|
+
`/snapshots/${encodeURIComponent(name)}`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
/** Delete a snapshot. */
|
|
687
|
+
async deleteSnapshot(name) {
|
|
688
|
+
await this.request(
|
|
689
|
+
"DELETE",
|
|
690
|
+
`/snapshots/${encodeURIComponent(name)}`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
/** Restore a sandbox from a snapshot. */
|
|
694
|
+
async restoreSnapshot(name) {
|
|
695
|
+
return this.request(
|
|
696
|
+
"POST",
|
|
697
|
+
`/snapshots/${encodeURIComponent(name)}/restore`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
392
700
|
// -- Internal helpers --
|
|
393
701
|
headers(contentType) {
|
|
394
702
|
const h = {
|
|
@@ -439,6 +747,8 @@ export {
|
|
|
439
747
|
AgentKernel,
|
|
440
748
|
AgentKernelError,
|
|
441
749
|
AuthError,
|
|
750
|
+
BROWSER_SETUP_CMD,
|
|
751
|
+
BrowserSession,
|
|
442
752
|
NetworkError,
|
|
443
753
|
NotFoundError,
|
|
444
754
|
SandboxSession,
|