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