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.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.4.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,