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/LICENSE +21 -0
- package/README.md +285 -810
- package/dist/index.d.mts +436 -2
- package/dist/index.d.ts +436 -2
- package/dist/index.js +1024 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1014 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
# InstaVM JavaScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
13
|
+
**Requirements:** Node.js 18+, TypeScript 4.7+ (optional)
|
|
23
14
|
|
|
24
|
-
|
|
15
|
+
## Quick Start
|
|
25
16
|
|
|
26
17
|
```typescript
|
|
27
|
-
import { InstaVM
|
|
18
|
+
import { InstaVM } from 'instavm';
|
|
28
19
|
|
|
29
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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
|
-
|
|
26
|
+
await client.dispose();
|
|
49
27
|
}
|
|
50
28
|
```
|
|
51
29
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
- ✅ Code execution (`execute()`)
|
|
81
|
-
- ✅ Browser navigation (`browser.navigate()`)
|
|
82
|
-
- ✅ Content extraction (`browser.extractContent()`)
|
|
57
|
+
## Code Execution
|
|
83
58
|
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
77
|
+
### Local Mode
|
|
191
78
|
|
|
192
|
-
|
|
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('
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
230
|
-
|
|
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
|
-
###
|
|
93
|
+
### File Operations
|
|
248
94
|
|
|
249
95
|
```typescript
|
|
250
|
-
|
|
251
|
-
const
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
99
|
+
// Upload
|
|
100
|
+
await client.upload(
|
|
101
|
+
[{ name: 'script.py', content: "print('uploaded')", path: '/app/script.py' }],
|
|
102
|
+
{ sessionId }
|
|
103
|
+
);
|
|
266
104
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
});
|
|
105
|
+
// Execute
|
|
106
|
+
await client.execute('python /app/script.py', { language: 'bash', sessionId });
|
|
270
107
|
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
108
|
+
// Download
|
|
109
|
+
const download = await client.download('output.json', { sessionId });
|
|
110
|
+
console.log(download.filename, download.size);
|
|
275
111
|
```
|
|
276
112
|
|
|
277
|
-
###
|
|
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
|
-
|
|
284
|
-
|
|
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
|
-
|
|
307
|
-
console.log('Form submission result:', result);
|
|
121
|
+
console.log(result.stdout);
|
|
308
122
|
```
|
|
309
123
|
|
|
310
|
-
|
|
124
|
+
---
|
|
311
125
|
|
|
312
|
-
|
|
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
|
-
//
|
|
320
|
-
const
|
|
321
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
144
|
+
---
|
|
375
145
|
|
|
376
|
-
|
|
146
|
+
## VMs & Snapshots
|
|
377
147
|
|
|
378
148
|
```typescript
|
|
379
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
426
|
-
console.log(result.stdout); // Output: Total: 15, Average: 3.0
|
|
427
|
-
```
|
|
189
|
+
## Volumes
|
|
428
190
|
|
|
429
|
-
###
|
|
191
|
+
### Volume CRUD & Files
|
|
430
192
|
|
|
431
193
|
```typescript
|
|
432
|
-
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
//
|
|
460
|
-
await
|
|
461
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
474
|
-
|
|
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
|
-
//
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
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
|
-
//
|
|
493
|
-
|
|
494
|
-
// Save to file if needed
|
|
232
|
+
// Cleanup
|
|
233
|
+
await client.volumes.delete(volumeId);
|
|
495
234
|
```
|
|
496
235
|
|
|
497
|
-
###
|
|
236
|
+
### VM Volume Attachments
|
|
498
237
|
|
|
499
238
|
```typescript
|
|
500
|
-
|
|
501
|
-
const
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
242
|
+
await client.vms.mountVolume(vmId, {
|
|
243
|
+
volume_id: volumeId,
|
|
244
|
+
mount_path: '/data',
|
|
245
|
+
mode: 'rw',
|
|
246
|
+
}, true);
|
|
510
247
|
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
283
|
+
## Browser Automation
|
|
600
284
|
|
|
601
|
-
###
|
|
285
|
+
### Basic Browser Flow
|
|
602
286
|
|
|
603
287
|
```typescript
|
|
604
|
-
|
|
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
|
-
|
|
629
|
-
|
|
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
|
-
###
|
|
297
|
+
### Content Extraction
|
|
634
298
|
|
|
635
|
-
|
|
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
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
-
|
|
305
|
+
const content = await browser.extractContent({
|
|
306
|
+
includeInteractive: true,
|
|
307
|
+
includeAnchors: true,
|
|
308
|
+
maxAnchors: 30,
|
|
309
|
+
});
|
|
653
310
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
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
|
-
|
|
319
|
+
---
|
|
673
320
|
|
|
674
|
-
|
|
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
|
-
|
|
323
|
+
Control a full desktop environment inside a VM session:
|
|
703
324
|
|
|
704
325
|
```typescript
|
|
705
|
-
|
|
706
|
-
|
|
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
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
|
|
725
|
-
|
|
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
|
-
|
|
732
|
-
|
|
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
|
-
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Platform APIs
|
|
746
343
|
|
|
747
|
-
|
|
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
|
-
//
|
|
759
|
-
|
|
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
|
-
|
|
769
|
-
|
|
770
|
-
console.log(result.stdout);
|
|
771
|
-
});
|
|
772
|
-
```
|
|
352
|
+
// Audit log
|
|
353
|
+
const events = await client.audit.events({ status: 'success', limit: 25 });
|
|
773
354
|
|
|
774
|
-
|
|
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
|
-
|
|
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
|
-
|
|
825
|
-
|
|
826
|
-
```typescript
|
|
827
|
-
// Batch operations when possible
|
|
828
|
-
const session = await client.browser.createSession();
|
|
364
|
+
---
|
|
829
365
|
|
|
830
|
-
|
|
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
|
-
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
370
|
+
```typescript
|
|
371
|
+
import {
|
|
372
|
+
InstaVM,
|
|
373
|
+
AuthenticationError,
|
|
374
|
+
ExecutionError,
|
|
375
|
+
NetworkError,
|
|
376
|
+
RateLimitError,
|
|
377
|
+
SessionError,
|
|
378
|
+
} from 'instavm';
|
|
843
379
|
|
|
844
|
-
|
|
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
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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
|
-
|
|
401
|
+
---
|
|
878
402
|
|
|
879
|
-
|
|
403
|
+
## Development & Testing
|
|
880
404
|
|
|
881
405
|
```bash
|
|
882
|
-
# Install dependencies
|
|
883
|
-
npm
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
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
|
-
|
|
414
|
+
## Further Reading
|
|
915
415
|
|
|
916
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
429
|
+
### 0.13.0
|
|
953
430
|
|
|
954
|
-
-
|
|
955
|
-
-
|
|
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
|
-
###
|
|
438
|
+
### 0.12.0
|
|
958
439
|
|
|
959
|
-
-
|
|
960
|
-
-
|
|
961
|
-
-
|
|
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
|
-
|
|
444
|
+
For detailed release history, see [GitHub Releases](https://github.com/instavm/js/releases).
|