instavm 0.8.3 → 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,949 +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);
36
-
37
- // Get usage info for the session
38
- const usage = await client.getUsage();
39
- console.log(usage);
23
+ const result = await client.execute("print('hello from instavm')");
24
+ console.log(result.stdout);
40
25
 
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!')");
43
+ const result = await client.execute("print('hello from local mode')");
67
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
-
90
- ### File Upload
91
-
92
- ```typescript
93
- import { InstaVM } from 'instavm';
94
51
 
95
- const client = new InstaVM('your_api_key');
52
+ console.log(content.readableContent.title);
53
+ ```
96
54
 
97
- // Create a session first (required for upload)
98
- await client.createSession();
55
+ ## Table of Contents
99
56
 
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
- ];
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)
108
64
 
109
- const result = await client.upload(files);
110
- console.log(result);
65
+ <a id="core-workflows"></a>
66
+ ## Core Workflows
111
67
 
112
- // Execute the uploaded file
113
- const execution = await client.execute('python /remote/path/script.py', {
114
- language: 'bash'
115
- });
116
- console.log(execution.stdout);
117
- ```
68
+ ### Cloud Quick Start
118
69
 
119
- ### File Download
70
+ Cloud mode supports sessions, VM/network controls, platform APIs, and browser sessions.
120
71
 
121
72
  ```typescript
122
73
  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
74
 
134
- // Download the file
135
- const result = await client.download('data.csv');
136
- console.log(`Downloaded ${result.size} bytes`);
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' },
80
+ });
137
81
 
138
- // Save to local file
139
- fs.writeFileSync('local-data.csv', result.content);
82
+ const sessionId = await client.createSession();
83
+ console.log('session:', sessionId);
140
84
  ```
141
85
 
142
- ### Error Handling
86
+ ### Local Mode Quick Start
143
87
 
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
88
+ Local mode is optimized for direct execution and lightweight browser helpers.
170
89
 
171
90
  ```typescript
172
91
  import { InstaVM } from 'instavm';
173
92
 
174
- const client = new InstaVM('your_api_key');
93
+ const client = new InstaVM('', { local: true });
175
94
 
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}`);
95
+ await client.execute("print('local execution works')");
96
+ await client.browser.navigate('https://example.com');
188
97
  ```
189
98
 
190
- ## Browser Automation
191
-
192
- ### Basic Browser Usage
99
+ ### File Operations
193
100
 
194
101
  ```typescript
195
102
  import { InstaVM } from 'instavm';
196
103
 
197
104
  const client = new InstaVM('your_api_key');
105
+ const sessionId = await client.createSession();
198
106
 
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'
227
- });
228
-
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' });
107
+ await client.upload(
108
+ [{ name: 'script.py', content: "print('uploaded')", path: '/app/script.py' }],
109
+ { sessionId }
110
+ );
238
111
 
239
- // Scroll to load more content
240
- await session.scroll({ y: 1000 });
112
+ await client.execute('python /app/script.py', { language: 'bash', sessionId });
241
113
 
242
- // Extract dynamic content
243
- const repos = await session.extractElements('.repo-list-item', ['text']);
244
- console.log('Repositories found:', repos.length);
114
+ const download = await client.download('output.json', { sessionId });
115
+ console.log(download.filename, download.size);
245
116
  ```
246
117
 
247
- ### Browser Session Management
118
+ ### Async Execution
248
119
 
249
120
  ```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
- });
262
-
263
- session.on('error', (error) => {
264
- console.error('Session error:', error.message);
265
- });
266
-
267
- session.on('close', () => {
268
- console.log('Session closed');
269
- });
270
-
271
- // Check if session is still active
272
- if (session.isActive) {
273
- await session.navigate('https://example.com');
274
- }
275
- ```
276
-
277
- ### Context Manager Pattern
121
+ import { InstaVM } from 'instavm';
278
122
 
279
- ```typescript
280
- // Use browser session with automatic cleanup
281
123
  const client = new InstaVM('your_api_key');
282
124
 
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
- }
125
+ const task = await client.executeAsync("sleep 5 && echo 'done'", { language: 'bash' });
126
+ const result = await client.getTaskResult(task.taskId, 2, 60);
305
127
 
306
- const result = await automateWebsite();
307
- console.log('Form submission result:', result);
128
+ console.log(result.stdout);
308
129
  ```
309
130
 
310
- ## Mixed Code + Browser Automation
311
-
312
- Combine code execution with browser automation for powerful workflows:
131
+ ### VMs + Snapshots
313
132
 
314
133
  ```typescript
315
134
  import { InstaVM } from 'instavm';
316
135
 
317
136
  const client = new InstaVM('your_api_key');
318
137
 
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
- }
345
-
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();
372
- ```
373
-
374
- ## Language Support
375
-
376
- ### Python Code Execution
377
-
378
- ```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
- ```
398
-
399
- ### Bash Commands
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
+ });
400
158
 
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);
159
+ const userSnapshots = await client.snapshots.list({ type: 'user' });
160
+ console.log(vmList.length, vmAllRecords.length, userSnapshots.length);
416
161
  ```
417
162
 
418
- ### Session Persistence
163
+ ### Networking (Egress, Shares, SSH)
419
164
 
420
165
  ```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)');
424
-
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
- ```
166
+ import { InstaVM } from 'instavm';
428
167
 
429
- ### Session Status Check
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
+ );
430
180
 
431
- ```typescript
432
- // Check if current session is still active
433
- const isActive = await client.isSessionActive();
434
- console.log(`Session active: ${isActive}`);
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 });
435
183
 
436
- // Check specific session
437
- const isOtherActive = await client.isSessionActive('session-id-123');
438
- console.log(`Other session active: ${isOtherActive}`);
184
+ const key = await client.addSshKey('ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host');
185
+ console.log(key);
439
186
  ```
440
187
 
441
- ## Advanced Features
188
+ <a id="infrastructure-platform-apis"></a>
189
+ ## Infrastructure & Platform APIs
442
190
 
443
- ### Wait Conditions
191
+ ### Platform APIs (API Keys, Audit, Webhooks)
444
192
 
445
193
  ```typescript
446
- // Wait for element to appear
447
- await session.wait({
448
- type: 'visible',
449
- selector: '.loading-complete',
450
- timeout: 30000
451
- });
452
-
453
- // Wait for element to disappear
454
- await session.wait({
455
- type: 'hidden',
456
- selector: '.spinner'
457
- });
458
-
459
- // Wait for page load
460
- await session.wait({
461
- type: 'networkidle'
462
- });
463
-
464
- // Simple timeout
465
- await session.wait({
466
- type: 'timeout',
467
- ms: 5000
468
- });
469
- ```
194
+ import { InstaVM } from 'instavm';
470
195
 
471
- ### Screenshot Options
196
+ const client = new InstaVM('your_api_key');
472
197
 
473
- ```typescript
474
- // Full page screenshot
475
- const fullPage = await session.screenshot({
476
- fullPage: true,
477
- format: 'png'
478
- });
198
+ const apiKey = await client.apiKeys.create({ description: 'ci key' });
199
+ const events = await client.audit.events({ status: 'success', limit: 25 });
479
200
 
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
201
+ const endpoint = await client.webhooks.createEndpoint({
202
+ url: 'https://example.com/instavm/webhook',
203
+ event_patterns: ['vm.*', 'snapshot.*'],
490
204
  });
491
205
 
492
- // Screenshots return base64 encoded strings
493
- const buffer = Buffer.from(fullPage, 'base64');
494
- // Save to file if needed
495
- ```
496
-
497
- ### Element Extraction
498
-
499
- ```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']);
505
-
506
- // Extract form data
507
- const formData = await session.extractElements('input, select, textarea', [
508
- 'name', 'value', 'type', 'placeholder'
509
- ]);
510
-
511
- console.log('Links found:', links);
512
- console.log('Articles:', articles);
513
- console.log('Form fields:', formData);
206
+ const deliveries = await client.webhooks.listDeliveries({ limit: 10 });
207
+ console.log(apiKey, events, endpoint, deliveries);
514
208
  ```
515
209
 
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
- });
525
-
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);
530
-
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
- }
538
-
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')
543
- );
544
- if (signupLink) {
545
- await session.click(signupLink.selector);
546
- }
547
- ```
210
+ <a id="browser-computer-use"></a>
211
+ ## Browser & Computer Use
548
212
 
549
- ## Error Handling Reference
213
+ ### Browser Automation
550
214
 
551
- ### Error Types
215
+ Basic browser session flow:
552
216
 
553
217
  ```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
- }
581
- ```
218
+ import { InstaVM } from 'instavm';
582
219
 
583
- ### Retry Logic
220
+ const client = new InstaVM('your_api_key');
584
221
 
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)
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 });
589
226
 
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
- });
227
+ console.log(screenshot.length);
228
+ await browser.close();
597
229
  ```
598
230
 
599
- ## API Reference
600
-
601
- ### InstaVM Client
231
+ Advanced flow with waits and extraction:
602
232
 
603
233
  ```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>
234
+ import { InstaVM } from 'instavm';
615
235
 
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>
236
+ const client = new InstaVM('your_api_key');
237
+ const browser = await client.browser.createSession();
621
238
 
622
- // Browser automation
623
- browser: BrowserManager
239
+ await browser.navigate('https://news.ycombinator.com');
240
+ await browser.wait({ type: 'visible', selector: 'a.storylink' }, 10000);
624
241
 
625
- // Properties
626
- readonly sessionId: string | null
242
+ const links = await browser.extractElements('a.storylink', ['text', 'href']);
243
+ console.log(links.slice(0, 5));
627
244
 
628
- // Cleanup
629
- dispose(): Promise<void>
630
- }
245
+ await browser.close();
631
246
  ```
632
247
 
633
- ### Configuration Options
248
+ ### LLM-Friendly Content Extraction (concise pattern only)
634
249
 
635
250
  ```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
- }
251
+ import { InstaVM } from 'instavm';
644
252
 
645
- interface ExecuteOptions {
646
- language?: 'python' | 'bash'; // Default: 'python'
647
- timeout?: number; // Default: 15 seconds
648
- sessionId?: string; // Use specific session
649
- }
650
- ```
253
+ const client = new InstaVM('your_api_key');
254
+ const browser = await client.browser.createSession();
651
255
 
652
- ### Browser Manager
256
+ await browser.navigate('https://example.com/docs');
653
257
 
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>
663
- }
258
+ const content = await browser.extractContent({
259
+ includeInteractive: true,
260
+ includeAnchors: true,
261
+ maxAnchors: 30,
262
+ });
664
263
 
665
- interface BrowserSessionOptions {
666
- viewportWidth?: number; // Default: 1920
667
- viewportHeight?: number; // Default: 1080
668
- 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);
669
267
  }
670
- ```
671
-
672
- ### Browser Session
673
268
 
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
- }
269
+ await browser.close();
700
270
  ```
701
271
 
702
- ### Type Definitions
272
+ ### Computer Use
703
273
 
704
274
  ```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
- }
715
-
716
- interface TaskResult {
717
- stdout: string;
718
- stderr: string;
719
- executionTime?: number;
720
- cpuTime?: number;
721
- createdAt?: string;
722
- }
723
-
724
- interface NavigationResult {
725
- success: boolean;
726
- url: string;
727
- title?: string;
728
- status?: number;
729
- }
730
-
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' };
743
- ```
744
-
745
- ## Best Practices
746
-
747
- ### Resource Management
275
+ import { InstaVM } from 'instavm';
748
276
 
749
- ```typescript
750
- // Always dispose of the client when done
751
277
  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
-
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
- }
767
-
768
- await withInstaVM('your_api_key', async (client) => {
769
- const result = await client.execute('print("Hello, World!")');
770
- console.log(result.stdout);
771
- });
772
- ```
278
+ const sessionId = await client.createSession();
773
279
 
774
- ### Error Handling Strategy
280
+ const viewer = await client.computerUse.viewerUrl(sessionId);
281
+ const state = await client.computerUse.get(sessionId, '/state');
775
282
 
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
- }
283
+ console.log(viewer, state);
822
284
  ```
823
285
 
824
- ### Performance Optimization
286
+ <a id="error-handling"></a>
287
+ ## Error Handling
825
288
 
826
289
  ```typescript
827
- // Batch operations when possible
828
- const session = await client.browser.createSession();
829
-
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']);
835
-
836
- // Consider using a single extraction call for related elements
837
- const pageData = await session.extractElements('title, a, img', ['text', 'href', 'src', 'alt']);
838
-
839
- // Use appropriate timeouts
840
- await session.navigate('https://slow-site.com', {
841
- waitTimeout: 60000 // Increase timeout for slow sites
842
- });
290
+ import {
291
+ InstaVM,
292
+ AuthenticationError,
293
+ ExecutionError,
294
+ NetworkError,
295
+ RateLimitError,
296
+ SessionError,
297
+ } from 'instavm';
843
298
 
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
- ```
299
+ const client = new InstaVM('your_api_key');
851
300
 
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
- }
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
+ }
872
317
  }
873
-
874
- main().catch(console.error);
875
318
  ```
876
319
 
877
- ## Development and Testing
878
-
879
- ### Running Tests
320
+ <a id="development-testing"></a>
321
+ ## Development & Testing
880
322
 
881
323
  ```bash
882
324
  # Install dependencies
883
325
  npm install
884
326
 
885
- # Run unit tests (no API key required)
327
+ # Run unit tests
886
328
  npm run test:unit
887
329
 
888
- # Run integration tests (requires API key)
889
- INSTAVM_API_KEY=your_api_key npm run test:integration
890
-
891
- # Run all tests
330
+ # Optional: full test run
892
331
  npm test
893
332
 
894
- # Build the package
333
+ # Build package
895
334
  npm run build
896
-
897
- # Type checking
898
- npm run type-check
899
335
  ```
900
336
 
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
913
-
914
- Proprietary. This SDK is provided for use with InstaVM services only.
915
-
916
- All rights reserved. No redistribution or modification permitted.
917
-
918
- ---
919
-
920
- ## Changelog
921
-
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
337
+ <a id="docs-map-further-reading"></a>
338
+ ## Docs Map (Further Reading)
951
339
 
952
- ### 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)
953
346
 
954
- - ✅ Enhanced session management
955
- - Improved error handling
347
+ <a id="version-changelog"></a>
348
+ ## Version / Changelog
956
349
 
957
- ### Version 0.1.0
350
+ Current package version: `0.11.0`.
958
351
 
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
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/`
968
356
 
969
- 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).