instavm 0.8.3 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,8 @@
1
1
  # InstaVM JavaScript SDK
2
2
 
3
- A comprehensive JavaScript/TypeScript client library for InstaVM's code execution and browser automation APIs.
3
+ ![Build Status](https://github.com/instavm/js/actions/workflows/ci.yml/badge.svg)
4
4
 
5
- ## Features
6
-
7
- - **Code Execution**: Run Python, Bash, and other languages in secure cloud environments
8
- - **Browser Automation**: Control web browsers for testing, scraping, and automation
9
- - **Session Management**: Automatic session creation and persistent execution contexts
10
- - **File Operations**: Upload files to execution environments
11
- - **Async Support**: Execute commands asynchronously for long-running tasks
12
- - **Error Handling**: Comprehensive exception hierarchy for different failure modes
13
- - **TypeScript Support**: Full type safety with comprehensive TypeScript definitions
14
- - **Modern JavaScript**: ES modules, CommonJS, and UMD support
5
+ Official JavaScript/TypeScript client for [InstaVM](https://instavm.io) — secure code execution in ephemeral microVMs with browser automation, networking controls, and platform APIs.
15
6
 
16
7
  ## Installation
17
8
 
@@ -19,951 +10,435 @@ A comprehensive JavaScript/TypeScript client library for InstaVM's code executio
19
10
  npm install instavm
20
11
  ```
21
12
 
22
- ## Quick Start
13
+ **Requirements:** Node.js 18+, TypeScript 4.7+ (optional)
23
14
 
24
- ### Code Execution (Cloud Mode)
15
+ ## Quick Start
25
16
 
26
17
  ```typescript
27
- import { InstaVM, ExecutionError, NetworkError } from 'instavm';
18
+ import { InstaVM } from 'instavm';
28
19
 
29
- // Create client with automatic session management
30
- const client = new InstaVM('your_api_key');
20
+ const client = new InstaVM(process.env.INSTAVM_API_KEY || 'your_api_key');
31
21
 
32
22
  try {
33
- // Execute a command
34
- const result = await client.execute("print(100**100)");
35
- console.log(result);
36
-
37
- // Get usage info for the session
38
- const usage = await client.getUsage();
39
- console.log(usage);
40
-
41
- } catch (error) {
42
- if (error instanceof ExecutionError) {
43
- console.log(`Code execution failed: ${error.message}`);
44
- } else if (error instanceof NetworkError) {
45
- console.log(`Network issue: ${error.message}`);
46
- }
23
+ const result = await client.execute("print('hello from instavm')");
24
+ console.log(result.stdout);
47
25
  } finally {
48
- await client.dispose();
26
+ await client.dispose();
49
27
  }
50
28
  ```
51
29
 
52
- ### Local Execution Mode
53
-
54
- Run code execution against a local container (e.g., [coderunner](https://github.com/instavm/coderunner)) instead of the cloud API:
30
+ ## Table of Contents
31
+
32
+ - [Quick Start](#quick-start)
33
+ - [Code Execution](#code-execution)
34
+ - [Cloud Mode](#cloud-mode)
35
+ - [Local Mode](#local-mode)
36
+ - [File Operations](#file-operations)
37
+ - [Async Execution](#async-execution)
38
+ - [Sessions & Sandboxes](#sessions--sandboxes)
39
+ - [VMs & Snapshots](#vms--snapshots)
40
+ - [Volumes](#volumes)
41
+ - [Volume CRUD & Files](#volume-crud--files)
42
+ - [VM Volume Attachments](#vm-volume-attachments)
43
+ - [Networking](#networking)
44
+ - [Egress, Shares, SSH](#egress-shares-ssh)
45
+ - [Browser Automation](#browser-automation)
46
+ - [Basic Browser Flow](#basic-browser-flow)
47
+ - [Content Extraction](#content-extraction)
48
+ - [Computer Use](#computer-use)
49
+ - [Platform APIs](#platform-apis)
50
+ - [Error Handling](#error-handling)
51
+ - [Development & Testing](#development--testing)
52
+ - [Further Reading](#further-reading)
53
+ - [Changelog](#changelog)
55
54
 
56
- ```typescript
57
- import { InstaVM } from 'instavm';
58
-
59
- // Create client in local mode (no API key required)
60
- const client = new InstaVM('', {
61
- local: true,
62
- localURL: 'http://coderunner.local:8222' // Optional, defaults to this URL
63
- });
64
-
65
- // Execute code locally without session management
66
- const result = await client.execute("print('Hello from local container!')");
67
- console.log(result.stdout);
68
-
69
- // Browser automation in local mode (no session required)
70
- const content = await client.browser.extractContent({
71
- url: 'https://example.com',
72
- includeInteractive: true,
73
- includeAnchors: true
74
- });
75
- console.log('Page title:', content.readableContent.title);
76
- console.log('Clean content:', content.readableContent.content);
77
- ```
55
+ ---
78
56
 
79
- **Note:** Local mode supports:
80
- - ✅ Code execution (`execute()`)
81
- - ✅ Browser navigation (`browser.navigate()`)
82
- - ✅ Content extraction (`browser.extractContent()`)
57
+ ## Code Execution
83
58
 
84
- Local mode does NOT support (cloud-only features):
85
- - ❌ Session management (`createSession()`, `closeSession()`, `getUsage()`)
86
- - ❌ File upload/download
87
- - ❌ Async execution
88
- - ❌ Browser session creation and complex interactions
59
+ ### Cloud Mode
89
60
 
90
- ### File Upload
61
+ Cloud mode gives you sessions, VM/network controls, platform APIs, and browser sessions.
91
62
 
92
63
  ```typescript
93
64
  import { InstaVM } from 'instavm';
94
65
 
95
- const client = new InstaVM('your_api_key');
96
-
97
- // Create a session first (required for upload)
98
- await client.createSession();
99
-
100
- // Upload files to the execution environment
101
- const files = [
102
- {
103
- name: 'script.py',
104
- content: 'print("Hello from uploaded file!")',
105
- path: '/remote/path/script.py'
106
- }
107
- ];
108
-
109
- const result = await client.upload(files);
110
- console.log(result);
111
-
112
- // Execute the uploaded file
113
- const execution = await client.execute('python /remote/path/script.py', {
114
- language: 'bash'
66
+ const client = new InstaVM('your_api_key', {
67
+ cpu_count: 2,
68
+ memory_mb: 1024,
69
+ env: { APP_ENV: 'dev' },
70
+ metadata: { team: 'platform' },
115
71
  });
116
- console.log(execution.stdout);
117
- ```
118
-
119
- ### File Download
120
-
121
- ```typescript
122
- import { InstaVM } from 'instavm';
123
- import fs from 'fs';
124
-
125
- const client = new InstaVM('your_api_key');
126
-
127
- // Create a file in the remote environment
128
- await client.execute(`
129
- import pandas as pd
130
- df = pd.DataFrame({'name': ['Alice', 'Bob'], 'age': [25, 30]})
131
- df.to_csv('data.csv', index=False)
132
- `);
133
-
134
- // Download the file
135
- const result = await client.download('data.csv');
136
- console.log(`Downloaded ${result.size} bytes`);
137
-
138
- // Save to local file
139
- fs.writeFileSync('local-data.csv', result.content);
140
- ```
141
-
142
- ### Error Handling
143
-
144
- ```typescript
145
- import {
146
- InstaVM,
147
- AuthenticationError,
148
- RateLimitError,
149
- SessionError,
150
- QuotaExceededError
151
- } from 'instavm';
152
-
153
- try {
154
- const client = new InstaVM('invalid_key');
155
- await client.execute('print("test")');
156
- } catch (error) {
157
- if (error instanceof AuthenticationError) {
158
- console.log("Invalid API key");
159
- } else if (error instanceof RateLimitError) {
160
- console.log(`Rate limit exceeded - retry after ${error.retryAfter} seconds`);
161
- } else if (error instanceof QuotaExceededError) {
162
- console.log("Usage quota exceeded");
163
- } else if (error instanceof SessionError) {
164
- console.log(`Session error: ${error.message}`);
165
- }
166
- }
167
- ```
168
-
169
- ### Async Execution
170
-
171
- ```typescript
172
- import { InstaVM } from 'instavm';
173
-
174
- const client = new InstaVM('your_api_key');
175
72
 
176
- // Execute command asynchronously (returns task ID)
177
- const result = await client.executeAsync("sleep 5 && echo 'Long task complete!'", {
178
- language: 'bash'
179
- });
180
- const taskId = result.taskId;
181
- console.log(`Task ${taskId} is running in background...`);
182
-
183
- // Poll for task result
184
- const taskResult = await client.getTaskResult(taskId, 2, 30);
185
- console.log('Task complete!');
186
- console.log(`Stdout: ${taskResult.stdout}`);
187
- console.log(`Stderr: ${taskResult.stderr}`);
73
+ const sessionId = await client.createSession();
74
+ console.log('session:', sessionId);
188
75
  ```
189
76
 
190
- ## Browser Automation
77
+ ### Local Mode
191
78
 
192
- ### Basic Browser Usage
79
+ Local mode connects to a self-hosted runner for direct execution and browser helpers.
193
80
 
194
81
  ```typescript
195
82
  import { InstaVM } from 'instavm';
196
83
 
197
- const client = new InstaVM('your_api_key');
198
-
199
- // Create browser session
200
- const session = await client.browser.createSession({
201
- viewportWidth: 1920,
202
- viewportHeight: 1080
203
- });
204
-
205
- // Navigate to website
206
- await session.navigate('https://example.com');
207
-
208
- // Take screenshot
209
- const screenshot = await session.screenshot();
210
- console.log(`Screenshot captured: ${screenshot.length} chars`);
211
-
212
- // Extract page data
213
- const elements = await session.extractElements('h1, p', ['text', 'href']);
214
- console.log('Page content:', elements);
215
-
216
- // Clean up
217
- await session.close();
218
- ```
219
-
220
- ### Advanced Browser Interactions
221
-
222
- ```typescript
223
- // Navigate with options
224
- await session.navigate('https://github.com/login', {
225
- waitTimeout: 30000,
226
- waitUntil: 'networkidle'
84
+ const client = new InstaVM('', {
85
+ local: true,
86
+ localURL: 'http://coderunner.local:8222',
227
87
  });
228
88
 
229
- // Fill login form
230
- await session.fill('input[name="login"]', 'username');
231
- await session.fill('input[name="password"]', 'password');
232
-
233
- // Click submit button
234
- await session.click('input[type="submit"]');
235
-
236
- // Wait for navigation
237
- await session.wait({ type: 'navigation' });
238
-
239
- // Scroll to load more content
240
- await session.scroll({ y: 1000 });
241
-
242
- // Extract dynamic content
243
- const repos = await session.extractElements('.repo-list-item', ['text']);
244
- console.log('Repositories found:', repos.length);
89
+ const result = await client.execute("print('hello from local mode')");
90
+ console.log(result.stdout);
245
91
  ```
246
92
 
247
- ### Browser Session Management
93
+ ### File Operations
248
94
 
249
95
  ```typescript
250
- // Create session with custom options
251
- const session = await client.browser.createSession({
252
- viewportWidth: 1280,
253
- viewportHeight: 720,
254
- userAgent: 'CustomBot/1.0'
255
- });
256
-
257
- // Session supports event listeners
258
- session.on('navigation', (result) => {
259
- console.log(`Navigated to: ${result.url}`);
260
- console.log(`Page title: ${result.title}`);
261
- });
96
+ const client = new InstaVM('your_api_key');
97
+ const sessionId = await client.createSession();
262
98
 
263
- session.on('error', (error) => {
264
- console.error('Session error:', error.message);
265
- });
99
+ // Upload
100
+ await client.upload(
101
+ [{ name: 'script.py', content: "print('uploaded')", path: '/app/script.py' }],
102
+ { sessionId }
103
+ );
266
104
 
267
- session.on('close', () => {
268
- console.log('Session closed');
269
- });
105
+ // Execute
106
+ await client.execute('python /app/script.py', { language: 'bash', sessionId });
270
107
 
271
- // Check if session is still active
272
- if (session.isActive) {
273
- await session.navigate('https://example.com');
274
- }
108
+ // Download
109
+ const download = await client.download('output.json', { sessionId });
110
+ console.log(download.filename, download.size);
275
111
  ```
276
112
 
277
- ### Context Manager Pattern
113
+ ### Async Execution
278
114
 
279
115
  ```typescript
280
- // Use browser session with automatic cleanup
281
116
  const client = new InstaVM('your_api_key');
282
117
 
283
- async function automateWebsite() {
284
- const session = await client.browser.createSession();
285
-
286
- try {
287
- await session.navigate('https://httpbin.org/forms/post');
288
-
289
- // Fill and submit form
290
- await session.fill('input[name="custname"]', 'John Doe');
291
- await session.fill('input[name="custtel"]', '555-1234');
292
- await session.click('input[type="submit"]');
293
-
294
- // Wait for result
295
- await session.wait({ type: 'visible', selector: 'pre' });
296
-
297
- // Extract result
298
- const result = await session.extractElements('pre', ['text']);
299
- return result[0]?.text;
300
-
301
- } finally {
302
- await session.close();
303
- }
304
- }
118
+ const task = await client.executeAsync("sleep 5 && echo 'done'", { language: 'bash' });
119
+ const result = await client.getTaskResult(task.taskId, 2, 60);
305
120
 
306
- const result = await automateWebsite();
307
- console.log('Form submission result:', result);
121
+ console.log(result.stdout);
308
122
  ```
309
123
 
310
- ## Mixed Code + Browser Automation
124
+ ---
311
125
 
312
- Combine code execution with browser automation for powerful workflows:
126
+ ## Sessions & Sandboxes
313
127
 
314
128
  ```typescript
315
- import { InstaVM } from 'instavm';
316
-
317
129
  const client = new InstaVM('your_api_key');
130
+ const sessionId = await client.createSession();
318
131
 
319
- // Execute Python code to prepare data
320
- const dataPrep = await client.execute(`
321
- import json
322
- import pandas as pd
323
-
324
- # Prepare search terms
325
- search_terms = ["AI", "Machine Learning", "JavaScript"]
326
- data = {"terms": search_terms, "timestamp": "2024-01-01"}
327
- print(json.dumps(data))
328
- `);
329
-
330
- const searchData = JSON.parse(dataPrep.stdout.trim());
331
-
332
- // Use browser to search and collect results
333
- const session = await client.browser.createSession();
334
- const results = [];
335
-
336
- for (const term of searchData.terms) {
337
- await session.navigate(`https://news.ycombinator.com/search?q=${encodeURIComponent(term)}`);
338
-
339
- const headlines = await session.extractElements('.titleline > a', ['text', 'href']);
340
- results.push({
341
- term,
342
- headlines: headlines.slice(0, 5) // Top 5 results
343
- });
344
- }
132
+ // Get the publicly-reachable app URL (optionally for a specific port)
133
+ const appUrl = await client.getSessionAppUrl(sessionId, 8080);
134
+ console.log(appUrl.app_url);
345
135
 
346
- await session.close();
347
-
348
- // Process results with Python
349
- const analysis = await client.execute(`
350
- import json
351
- data = ${JSON.stringify(results)}
352
-
353
- # Analyze results
354
- total_headlines = sum(len(item['headlines']) for item in data)
355
- print(f"Collected {total_headlines} headlines across {len(data)} search terms")
356
-
357
- # Find most common words
358
- all_text = ' '.join([headline['text'] for item in data for headline in item['headlines']])
359
- words = all_text.lower().split()
360
- word_counts = {}
361
- for word in words:
362
- if len(word) > 3: # Filter short words
363
- word_counts[word] = word_counts.get(word, 0) + 1
364
-
365
- # Top 10 words
366
- top_words = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)[:10]
367
- print("Top words:", top_words)
368
- `);
369
-
370
- console.log(analysis.stdout);
371
- await client.dispose();
136
+ // List sandbox records with optional metadata filter and limit
137
+ const sandboxes = await client.listSandboxes({
138
+ metadata: { env: 'production' },
139
+ limit: 50,
140
+ });
141
+ console.log(sandboxes.length);
372
142
  ```
373
143
 
374
- ## Language Support
144
+ ---
375
145
 
376
- ### Python Code Execution
146
+ ## VMs & Snapshots
377
147
 
378
148
  ```typescript
379
- // Python with libraries
380
- const result = await client.execute(`
381
- import pandas as pd
382
- import numpy as np
383
-
384
- # Create sample data
385
- data = pd.DataFrame({
386
- 'numbers': np.random.randn(100),
387
- 'categories': np.random.choice(['A', 'B', 'C'], 100)
388
- })
389
-
390
- # Basic statistics
391
- print(f"Mean: {data['numbers'].mean():.2f}")
392
- print(f"Std: {data['numbers'].std():.2f}")
393
- print(f"Categories: {data['categories'].value_counts().to_dict()}")
394
- `);
395
-
396
- console.log(result.stdout);
397
- ```
149
+ const client = new InstaVM('your_api_key');
398
150
 
399
- ### Bash Commands
151
+ // Create a basic VM
152
+ const vm = await client.vms.create({ metadata: { purpose: 'dev' } }, true);
153
+ const vmId = String(vm.vm_id);
154
+
155
+ // Create a VM with pre-attached volumes
156
+ const vmWithVols = await client.vms.create({
157
+ metadata: { purpose: 'data-processing' },
158
+ volumes: [
159
+ { volume_id: 'vol_abc', mount_path: '/data', mode: 'rw' },
160
+ ],
161
+ }, true);
162
+
163
+ // List VMs
164
+ const vmList = await client.vms.list(); // GET /v1/vms (running)
165
+ const vmAllRecords = await client.vms.listAllRecords(); // GET /v1/vms/ (all records)
166
+
167
+ // Snapshot a running VM
168
+ await client.vms.snapshot(vmId, { name: 'dev-base' }, true);
169
+
170
+ // Build a snapshot from an OCI image
171
+ await client.snapshots.create({
172
+ oci_image: 'docker.io/library/python:3.11-slim',
173
+ name: 'python-3-11-dev',
174
+ vcpu_count: 2,
175
+ memory_mb: 1024,
176
+ type: 'user',
177
+ build_args: {
178
+ git_clone_url: 'https://github.com/example/repo.git',
179
+ git_clone_branch: 'main',
180
+ envs: { NODE_ENV: 'production' },
181
+ },
182
+ });
400
183
 
401
- ```typescript
402
- // System operations
403
- const sysInfo = await client.execute(`
404
- echo "System Information:"
405
- echo "==================="
406
- uname -a
407
- echo
408
- echo "Disk Usage:"
409
- df -h
410
- echo
411
- echo "Memory Info:"
412
- free -h
413
- `, { language: 'bash' });
414
-
415
- console.log(sysInfo.stdout);
184
+ const userSnapshots = await client.snapshots.list({ type: 'user' });
416
185
  ```
417
186
 
418
- ### Session Persistence
419
-
420
- ```typescript
421
- // Variables persist across executions within the same session
422
- await client.execute('data = [1, 2, 3, 4, 5]');
423
- await client.execute('total = sum(data)');
187
+ ---
424
188
 
425
- const result = await client.execute('print(f"Total: {total}, Average: {total/len(data)}")');
426
- console.log(result.stdout); // Output: Total: 15, Average: 3.0
427
- ```
189
+ ## Volumes
428
190
 
429
- ### Session Status Check
191
+ ### Volume CRUD & Files
430
192
 
431
193
  ```typescript
432
- // Check if current session is still active
433
- const isActive = await client.isSessionActive();
434
- console.log(`Session active: ${isActive}`);
435
-
436
- // Check specific session
437
- const isOtherActive = await client.isSessionActive('session-id-123');
438
- console.log(`Other session active: ${isOtherActive}`);
439
- ```
440
-
441
- ## Advanced Features
442
-
443
- ### Wait Conditions
194
+ const client = new InstaVM('your_api_key');
444
195
 
445
- ```typescript
446
- // Wait for element to appear
447
- await session.wait({
448
- type: 'visible',
449
- selector: '.loading-complete',
450
- timeout: 30000
196
+ // Create
197
+ const volume = await client.volumes.create({
198
+ name: 'project-data',
199
+ quota_bytes: 10 * 1024 * 1024 * 1024,
451
200
  });
452
-
453
- // Wait for element to disappear
454
- await session.wait({
455
- type: 'hidden',
456
- selector: '.spinner'
201
+ const volumeId = String(volume.id);
202
+
203
+ // Read / Update
204
+ await client.volumes.list(true); // refresh_usage=true
205
+ await client.volumes.get(volumeId, true);
206
+ await client.volumes.update(volumeId, {
207
+ name: 'project-data-v2',
208
+ quota_bytes: 20 * 1024 * 1024 * 1024,
457
209
  });
458
210
 
459
- // Wait for page load
460
- await session.wait({
461
- type: 'networkidle'
211
+ // File operations
212
+ await client.volumes.uploadFile(volumeId, {
213
+ filePath: './README.md',
214
+ path: 'docs/README.md',
215
+ overwrite: true,
462
216
  });
463
217
 
464
- // Simple timeout
465
- await session.wait({
466
- type: 'timeout',
467
- ms: 5000
218
+ const files = await client.volumes.listFiles(volumeId, {
219
+ prefix: 'docs/',
220
+ recursive: true,
221
+ limit: 1000,
468
222
  });
469
- ```
470
-
471
- ### Screenshot Options
472
223
 
473
- ```typescript
474
- // Full page screenshot
475
- const fullPage = await session.screenshot({
476
- fullPage: true,
477
- format: 'png'
478
- });
224
+ const file = await client.volumes.downloadFile(volumeId, 'docs/README.md');
225
+ await client.volumes.deleteFile(volumeId, 'docs/README.md');
479
226
 
480
- // Clip specific area
481
- const clipped = await session.screenshot({
482
- clip: {
483
- x: 0,
484
- y: 0,
485
- width: 800,
486
- height: 600
487
- },
488
- format: 'jpeg',
489
- quality: 90
490
- });
227
+ // Checkpoints
228
+ const checkpoint = await client.volumes.createCheckpoint(volumeId, { name: 'pre-release' });
229
+ await client.volumes.listCheckpoints(volumeId);
230
+ await client.volumes.deleteCheckpoint(volumeId, String(checkpoint.id));
491
231
 
492
- // Screenshots return base64 encoded strings
493
- const buffer = Buffer.from(fullPage, 'base64');
494
- // Save to file if needed
232
+ // Cleanup
233
+ await client.volumes.delete(volumeId);
495
234
  ```
496
235
 
497
- ### Element Extraction
236
+ ### VM Volume Attachments
498
237
 
499
238
  ```typescript
500
- // Extract multiple attributes
501
- const links = await session.extractElements('a', ['href', 'text', 'title']);
502
-
503
- // Extract with CSS selectors
504
- const articles = await session.extractElements('article h2, .post-title', ['text']);
239
+ const vm = await client.vms.create({}, true);
240
+ const vmId = String(vm.vm_id);
505
241
 
506
- // Extract form data
507
- const formData = await session.extractElements('input, select, textarea', [
508
- 'name', 'value', 'type', 'placeholder'
509
- ]);
242
+ await client.vms.mountVolume(vmId, {
243
+ volume_id: volumeId,
244
+ mount_path: '/data',
245
+ mode: 'rw',
246
+ }, true);
510
247
 
511
- console.log('Links found:', links);
512
- console.log('Articles:', articles);
513
- console.log('Form fields:', formData);
248
+ await client.vms.listVolumes(vmId);
249
+ await client.vms.unmountVolume(vmId, volumeId, '/data', true);
514
250
  ```
515
251
 
516
- ### LLM-Friendly Content Extraction
517
-
518
- ```typescript
519
- // Extract clean, LLM-optimized content from a webpage
520
- const content = await session.extractContent({
521
- includeInteractive: true, // Include clickable/typeable elements
522
- includeAnchors: true, // Include content-to-selector mappings
523
- maxAnchors: 50 // Limit number of anchors
524
- });
252
+ ---
525
253
 
526
- // Get clean article text (no ads, no navigation, no scripts)
527
- console.log('Title:', content.readableContent.title);
528
- console.log('Article:', content.readableContent.content);
529
- console.log('Author:', content.readableContent.byline);
254
+ ## Networking
530
255
 
531
- // Find interactive elements (buttons, links, inputs)
532
- const loginButton = content.interactiveElements?.find(
533
- el => el.text?.toLowerCase().includes('login')
534
- );
535
- if (loginButton) {
536
- await session.click(loginButton.selector);
537
- }
256
+ ### Egress, Shares, SSH
538
257
 
539
- // Use content anchors to map text to selectors
540
- // Perfect for LLM agents that need to "read then click"
541
- const signupLink = content.contentAnchors?.find(
542
- anchor => anchor.text.toLowerCase().includes('sign up')
258
+ ```typescript
259
+ const client = new InstaVM('your_api_key');
260
+ const sessionId = await client.createSession();
261
+
262
+ // Egress policy
263
+ await client.setSessionEgress(
264
+ {
265
+ allowPackageManagers: true,
266
+ allowHttp: false,
267
+ allowHttps: true,
268
+ allowedDomains: ['npmjs.com', 'registry.npmjs.org'],
269
+ },
270
+ sessionId
543
271
  );
544
- if (signupLink) {
545
- await session.click(signupLink.selector);
546
- }
547
- ```
548
272
 
549
- ## Error Handling Reference
273
+ // Public/private share links
274
+ const share = await client.shares.create({ session_id: sessionId, port: 3000, is_public: false });
275
+ await client.shares.update(String(share.share_id), { is_public: true });
550
276
 
551
- ### Error Types
552
-
553
- ```typescript
554
- import {
555
- InstaVMError, // Base error class
556
- AuthenticationError, // API key issues
557
- RateLimitError, // Rate limiting (has retryAfter property)
558
- QuotaExceededError, // Usage quota exceeded
559
- NetworkError, // Connection issues
560
- ExecutionError, // Code execution failures
561
- SessionError, // Session management issues
562
- BrowserError, // General browser errors
563
- BrowserSessionError, // Browser session issues
564
- BrowserInteractionError, // Browser interaction failures
565
- BrowserTimeoutError, // Browser operation timeouts
566
- BrowserNavigationError, // Navigation failures
567
- ElementNotFoundError, // Element selection issues (has selector property)
568
- UnsupportedOperationError // Operation not supported in local mode
569
- } from 'instavm';
570
-
571
- // Specific error handling
572
- try {
573
- await session.click('.non-existent-button');
574
- } catch (error) {
575
- if (error instanceof ElementNotFoundError) {
576
- console.log(`Element not found: ${error.selector}`);
577
- } else if (error instanceof BrowserTimeoutError) {
578
- console.log('Operation timed out');
579
- }
580
- }
277
+ // SSH key registration
278
+ const key = await client.addSshKey('ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host');
581
279
  ```
582
280
 
583
- ### Retry Logic
584
-
585
- The SDK includes automatic retry logic for:
586
- - Network errors (connection issues)
587
- - Rate limiting (with exponential backoff)
588
- - Server errors (5xx status codes)
589
-
590
- ```typescript
591
- // Customize retry behavior
592
- const client = new InstaVM('your_api_key', {
593
- maxRetries: 5,
594
- retryDelay: 2000, // Base delay in milliseconds
595
- timeout: 300000 // 5 minute timeout
596
- });
597
- ```
281
+ ---
598
282
 
599
- ## API Reference
283
+ ## Browser Automation
600
284
 
601
- ### InstaVM Client
285
+ ### Basic Browser Flow
602
286
 
603
287
  ```typescript
604
- class InstaVM {
605
- constructor(apiKey: string, options?: InstaVMOptions)
606
-
607
- // Code execution
608
- execute(command: string, options?: ExecuteOptions): Promise<ExecutionResult>
609
- executeAsync(command: string, options?: ExecuteOptions): Promise<AsyncExecutionResult>
610
- getTaskResult(taskId: string, pollInterval?: number, timeout?: number): Promise<TaskResult>
611
-
612
- // File operations
613
- upload(files: FileUpload[], options?: UploadOptions): Promise<UploadResult>
614
- download(filename: string, options?: DownloadOptions): Promise<DownloadResult>
615
-
616
- // Session management
617
- createSession(): Promise<string>
618
- closeSession(sessionId?: string): Promise<void>
619
- isSessionActive(sessionId?: string): Promise<boolean>
620
- getUsage(sessionId?: string): Promise<UsageStats>
621
-
622
- // Browser automation
623
- browser: BrowserManager
624
-
625
- // Properties
626
- readonly sessionId: string | null
288
+ const client = new InstaVM('your_api_key');
627
289
 
628
- // Cleanup
629
- dispose(): Promise<void>
630
- }
290
+ const browser = await client.browser.createSession({ viewportWidth: 1366, viewportHeight: 768 });
291
+ await browser.navigate('https://example.com');
292
+ await browser.click('a');
293
+ const screenshot = await browser.screenshot({ fullPage: true });
294
+ await browser.close();
631
295
  ```
632
296
 
633
- ### Configuration Options
297
+ ### Content Extraction
634
298
 
635
- ```typescript
636
- interface InstaVMOptions {
637
- baseURL?: string; // Default: 'https://api.instavm.io' (ignored if local=true)
638
- timeout?: number; // Default: 300000 (5 minutes)
639
- maxRetries?: number; // Default: 3
640
- retryDelay?: number; // Default: 1000ms
641
- local?: boolean; // Default: false - Use local container instead of cloud
642
- localURL?: string; // Default: 'http://coderunner.local:8222' - Local container URL
643
- }
299
+ LLM-friendly extraction with optional interactive-element and anchor discovery:
644
300
 
645
- interface ExecuteOptions {
646
- language?: 'python' | 'bash'; // Default: 'python'
647
- timeout?: number; // Default: 15 seconds
648
- sessionId?: string; // Use specific session
649
- }
650
- ```
301
+ ```typescript
302
+ const browser = await client.browser.createSession();
303
+ await browser.navigate('https://example.com/docs');
651
304
 
652
- ### Browser Manager
305
+ const content = await browser.extractContent({
306
+ includeInteractive: true,
307
+ includeAnchors: true,
308
+ maxAnchors: 30,
309
+ });
653
310
 
654
- ```typescript
655
- class BrowserManager {
656
- createSession(options?: BrowserSessionOptions): Promise<BrowserSession>
657
- listSessions(): Promise<BrowserSessionInfo[]>
658
- getSession(sessionId: string): Promise<BrowserSessionInfo>
659
- getLocalSession(sessionId: string): BrowserSession | undefined
660
- getLocalSessions(): BrowserSession[]
661
- closeAllSessions(): Promise<void>
662
- dispose(): Promise<void>
311
+ console.log(content.readableContent.title);
312
+ for (const anchor of (content.contentAnchors || []).slice(0, 5)) {
313
+ console.log(anchor.text, anchor.selector);
663
314
  }
664
315
 
665
- interface BrowserSessionOptions {
666
- viewportWidth?: number; // Default: 1920
667
- viewportHeight?: number; // Default: 1080
668
- userAgent?: string; // Custom user agent
669
- }
316
+ await browser.close();
670
317
  ```
671
318
 
672
- ### Browser Session
319
+ ---
673
320
 
674
- ```typescript
675
- class BrowserSession extends EventEmitter {
676
- // Navigation
677
- navigate(url: string, options?: NavigateOptions): Promise<NavigationResult>
678
-
679
- // Interaction
680
- click(selector: string, options?: ClickOptions): Promise<void>
681
- type(selector: string, text: string, options?: TypeOptions): Promise<void>
682
- fill(selector: string, value: string, options?: FillOptions): Promise<void>
683
- scroll(options?: ScrollOptions): Promise<void>
684
-
685
- // Data extraction
686
- screenshot(options?: ScreenshotOptions): Promise<string>
687
- extractElements(selector: string, attributes?: string[]): Promise<ExtractedElement[]>
688
- extractContent(options?: ExtractContentOptions): Promise<ExtractedContent>
689
-
690
- // Utilities
691
- wait(condition: WaitCondition, timeout?: number): Promise<void>
692
- close(): Promise<void>
693
-
694
- // Properties
695
- readonly sessionId: string
696
- readonly isActive: boolean
697
-
698
- // Events: 'navigation', 'error', 'close'
699
- }
700
- ```
321
+ ## Computer Use
701
322
 
702
- ### Type Definitions
323
+ Control a full desktop environment inside a VM session:
703
324
 
704
325
  ```typescript
705
- interface ExecutionResult {
706
- stdout: string;
707
- stderr: string;
708
- success: boolean;
709
- executionTime?: number;
710
- cpuTime?: number;
711
- createdAt?: string;
712
- sessionId?: string;
713
- error?: string;
714
- }
326
+ const client = new InstaVM('your_api_key');
327
+ const sessionId = await client.createSession();
715
328
 
716
- interface TaskResult {
717
- stdout: string;
718
- stderr: string;
719
- executionTime?: number;
720
- cpuTime?: number;
721
- createdAt?: string;
722
- }
329
+ // Viewer URL and state
330
+ const viewer = await client.computerUse.viewerUrl(sessionId);
331
+ const state = await client.computerUse.get(sessionId, '/state');
723
332
 
724
- interface NavigationResult {
725
- success: boolean;
726
- url: string;
727
- title?: string;
728
- status?: number;
729
- }
333
+ // Proxy methods (GET, POST, HEAD)
334
+ const headResp = await client.computerUse.head(sessionId, '/state');
730
335
 
731
- interface ExtractedElement {
732
- text?: string;
733
- href?: string;
734
- [attribute: string]: string | undefined;
735
- }
736
-
737
- type WaitCondition =
738
- | { type: 'timeout'; ms: number }
739
- | { type: 'visible'; selector: string }
740
- | { type: 'hidden'; selector: string }
741
- | { type: 'navigation' }
742
- | { type: 'networkidle' };
336
+ // VNC websockify URL for remote desktop streaming
337
+ const vnc = await client.computerUse.vncWebsockify(sessionId);
743
338
  ```
744
339
 
745
- ## Best Practices
340
+ ---
341
+
342
+ ## Platform APIs
746
343
 
747
- ### Resource Management
344
+ API keys, audit logs, and webhooks:
748
345
 
749
346
  ```typescript
750
- // Always dispose of the client when done
751
347
  const client = new InstaVM('your_api_key');
752
- try {
753
- // Your automation code
754
- } finally {
755
- await client.dispose(); // Closes all sessions and cleans up
756
- }
757
348
 
758
- // Or use a wrapper function
759
- async function withInstaVM(apiKey: string, callback: (client: InstaVM) => Promise<void>) {
760
- const client = new InstaVM(apiKey);
761
- try {
762
- await callback(client);
763
- } finally {
764
- await client.dispose();
765
- }
766
- }
349
+ // API Keys
350
+ const apiKey = await client.apiKeys.create({ description: 'ci key' });
767
351
 
768
- await withInstaVM('your_api_key', async (client) => {
769
- const result = await client.execute('print("Hello, World!")');
770
- console.log(result.stdout);
771
- });
772
- ```
352
+ // Audit log
353
+ const events = await client.audit.events({ status: 'success', limit: 25 });
773
354
 
774
- ### Error Handling Strategy
355
+ // Webhooks
356
+ const endpoint = await client.webhooks.createEndpoint({
357
+ url: 'https://example.com/instavm/webhook',
358
+ event_patterns: ['vm.*', 'snapshot.*'],
359
+ });
775
360
 
776
- ```typescript
777
- // Implement comprehensive error handling
778
- async function robustAutomation(client: InstaVM) {
779
- let session: BrowserSession | null = null;
780
-
781
- try {
782
- session = await client.browser.createSession();
783
-
784
- // Retry navigation with timeout
785
- let retries = 3;
786
- while (retries > 0) {
787
- try {
788
- await session.navigate('https://example.com');
789
- break;
790
- } catch (error) {
791
- if (error instanceof BrowserTimeoutError && retries > 1) {
792
- console.log(`Navigation timeout, retrying... (${retries - 1} attempts left)`);
793
- retries--;
794
- await new Promise(resolve => setTimeout(resolve, 2000));
795
- } else {
796
- throw error;
797
- }
798
- }
799
- }
800
-
801
- // Safe element interaction
802
- try {
803
- await session.click('button.submit');
804
- } catch (error) {
805
- if (error instanceof ElementNotFoundError) {
806
- console.log('Submit button not found, trying alternative selector');
807
- await session.click('input[type="submit"]');
808
- } else {
809
- throw error;
810
- }
811
- }
812
-
813
- } catch (error) {
814
- console.error('Automation failed:', error.message);
815
- throw error;
816
- } finally {
817
- if (session) {
818
- await session.close();
819
- }
820
- }
821
- }
361
+ const deliveries = await client.webhooks.listDeliveries({ limit: 10 });
822
362
  ```
823
363
 
824
- ### Performance Optimization
825
-
826
- ```typescript
827
- // Batch operations when possible
828
- const session = await client.browser.createSession();
364
+ ---
829
365
 
830
- // Instead of multiple separate calls
831
- await session.navigate('https://example.com');
832
- const title = await session.extractElements('title', ['text']);
833
- const links = await session.extractElements('a', ['href']);
834
- const images = await session.extractElements('img', ['src', 'alt']);
366
+ ## Error Handling
835
367
 
836
- // Consider using a single extraction call for related elements
837
- const pageData = await session.extractElements('title, a, img', ['text', 'href', 'src', 'alt']);
368
+ All SDK errors extend a typed hierarchy for precise `catch` handling:
838
369
 
839
- // Use appropriate timeouts
840
- await session.navigate('https://slow-site.com', {
841
- waitTimeout: 60000 // Increase timeout for slow sites
842
- });
370
+ ```typescript
371
+ import {
372
+ InstaVM,
373
+ AuthenticationError,
374
+ ExecutionError,
375
+ NetworkError,
376
+ RateLimitError,
377
+ SessionError,
378
+ } from 'instavm';
843
379
 
844
- // Optimize screenshot size for performance
845
- const thumbnail = await session.screenshot({
846
- clip: { x: 0, y: 0, width: 400, height: 300 },
847
- format: 'jpeg',
848
- quality: 70
849
- });
850
- ```
380
+ const client = new InstaVM('your_api_key');
851
381
 
852
- ## CommonJS Usage
853
-
854
- For projects using CommonJS:
855
-
856
- ```javascript
857
- const { InstaVM, AuthenticationError } = require('instavm');
858
-
859
- async function main() {
860
- const client = new InstaVM('your_api_key');
861
-
862
- try {
863
- const result = await client.execute('print("Hello from CommonJS!")');
864
- console.log(result.stdout);
865
- } catch (error) {
866
- if (error instanceof AuthenticationError) {
867
- console.error('Authentication failed');
868
- }
869
- } finally {
870
- await client.dispose();
871
- }
382
+ try {
383
+ await client.execute("raise Exception('boom')");
384
+ } catch (error) {
385
+ if (error instanceof AuthenticationError) {
386
+ console.error('Invalid API key');
387
+ } else if (error instanceof RateLimitError) {
388
+ console.error('Rate limited');
389
+ } else if (error instanceof SessionError) {
390
+ console.error('Session issue:', error.message);
391
+ } else if (error instanceof ExecutionError) {
392
+ console.error('Execution failed:', error.message);
393
+ } else if (error instanceof NetworkError) {
394
+ console.error('Network issue:', error.message);
395
+ } else {
396
+ throw error;
397
+ }
872
398
  }
873
-
874
- main().catch(console.error);
875
399
  ```
876
400
 
877
- ## Development and Testing
401
+ ---
878
402
 
879
- ### Running Tests
403
+ ## Development & Testing
880
404
 
881
405
  ```bash
882
- # Install dependencies
883
- npm install
884
-
885
- # Run unit tests (no API key required)
886
- npm run test:unit
887
-
888
- # Run integration tests (requires API key)
889
- INSTAVM_API_KEY=your_api_key npm run test:integration
890
-
891
- # Run all tests
892
- npm test
893
-
894
- # Build the package
895
- npm run build
896
-
897
- # Type checking
898
- npm run type-check
406
+ npm install # Install dependencies
407
+ npm run test:unit # Unit tests
408
+ npm test # Full test suite
409
+ npm run build # Build package
899
410
  ```
900
411
 
901
- **Note:** Integration tests require a valid InstaVM API key. Set the `INSTAVM_API_KEY` environment variable before running integration tests. Unit tests do not require an API key.
902
-
903
- ### Contributing
904
-
905
- This is an official SDK. For issues and feature requests, please use the GitHub repository.
906
-
907
- ## Requirements
908
-
909
- - Node.js 16+
910
- - TypeScript 5+ (for TypeScript projects)
911
-
912
- ## License
412
+ ---
913
413
 
914
- Proprietary. This SDK is provided for use with InstaVM services only.
414
+ ## Further Reading
915
415
 
916
- All rights reserved. No redistribution or modification permitted.
416
+ - [JavaScript SDK Docs](https://instavm.io/docs/sdks/javascript)
417
+ - [VMs API](https://instavm.io/docs/api/vms/)
418
+ - [Snapshots API](https://instavm.io/docs/api/snapshots/)
419
+ - [Shares API](https://instavm.io/docs/api/shares/)
420
+ - [Computer Use API](https://instavm.io/docs/api#endpoint-categories)
421
+ - [Webhooks API](https://instavm.io/docs/api#endpoint-categories)
917
422
 
918
423
  ---
919
424
 
920
425
  ## Changelog
921
426
 
922
- ### Version 0.8.3
923
-
924
- - ✅ **NEW**: `getTaskResult()` method - Poll for async task completion with configurable intervals
925
- - ✅ **NEW**: `isSessionActive()` method - Check if session is still active by querying VM status
926
- - ✅ **IMPROVED**: Execution result format - Now returns `stdout` and `stderr` separately (matching Python SDK)
927
- - ✅ **IMPROVED**: Enhanced execution results - Added `cpuTime`, `executionTime`, and `createdAt` fields
928
- - ✅ **IMPROVED**: Better error messages - Rate limit errors now show `detail` field from API response
929
- - ✅ **FIXED**: Session status check - Now uses `/v1/sessions/status/` endpoint for accurate VM status
930
- - ✅ **UPDATED**: closeSession behavior - Sessions auto-expire on server, no longer makes DELETE API call
931
- - ✅ Full parity with Python SDK v0.8.3
932
-
933
- ### Version 0.4.0
934
-
935
- - ✅ **NEW**: Local execution mode support - Run code execution against local containers
936
- - ✅ **NEW**: Local browser automation - Navigate and extract content without sessions
937
- - ✅ **NEW**: `UnsupportedOperationError` - Better error messages for cloud-only features
938
- - ✅ Parity with Python SDK v0.4.0 local mode features
939
- - ✅ Improved flexibility for on-premise deployments
940
-
941
- ### Version 0.3.0
942
-
943
- - ✅ **NEW**: File download functionality - Download files from remote VM
944
- - ✅ **NEW**: LLM-friendly content extraction - Extract clean, readable content with interactive element mapping
945
- - ✅ Enhanced browser automation with content anchors for intelligent LLM agents
946
- - ✅ Full API parity with Python SDK
947
-
948
- ### Version 0.2.1
949
-
950
- - ✅ Bug fixes and improvements
427
+ Current package version: **0.13.0**
951
428
 
952
- ### Version 0.2.0
429
+ ### 0.13.0
953
430
 
954
- - Enhanced session management
955
- - Improved error handling
431
+ - [`getSessionAppUrl(sessionId?, port?)`](#sessions--sandboxes) session app URL with optional port
432
+ - [`listSandboxes({ metadata?, limit? })`](#sessions--sandboxes) — list sandbox records with metadata filtering
433
+ - [`computerUse.head(sessionId, path)`](#computer-use) — HEAD proxy method for computer-use sessions
434
+ - [`computerUse.vncWebsockify(sessionId)`](#computer-use) — VNC websockify URL for remote desktop streaming
435
+ - VM creation now accepts [`volumes`](#vms--snapshots) for pre-attached volume mounts
436
+ - `APIKey` type includes `key_prefix` and `full_key` fields
956
437
 
957
- ### Version 0.1.0
438
+ ### 0.12.0
958
439
 
959
- - Code execution fully functional (Python, Bash)
960
- - Browser automation complete (navigation, interaction, extraction)
961
- - Comprehensive TypeScript support
962
- - ✅ Error handling with specific error types
963
- - ✅ Session management and automatic cleanup
964
- - ✅ File upload capabilities
965
- - ✅ Async execution support
966
- - ✅ Event-driven browser sessions
967
- - ✅ Modern build system with multiple output formats
440
+ - Manager-based infrastructure APIs (VMs, volumes, snapshots, shares, custom domains, computer use, API keys, audit, webhooks)
441
+ - Snapshot build args support for env vars and Git clone inputs
442
+ - Distinct VM list helpers for `/v1/vms` and `/v1/vms/`
968
443
 
969
- The JavaScript SDK provides complete feature parity with the Python SDK and is ready for production use.
444
+ For detailed release history, see [GitHub Releases](https://github.com/instavm/js/releases).