instavm 0.1.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 +795 -0
- package/dist/index.d.mts +424 -0
- package/dist/index.d.ts +424 -0
- package/dist/index.js +986 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +935 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/openai/index.d.mts +16 -0
- package/dist/integrations/openai/index.d.ts +16 -0
- package/dist/integrations/openai/index.js +39 -0
- package/dist/integrations/openai/index.js.map +1 -0
- package/dist/integrations/openai/index.mjs +14 -0
- package/dist/integrations/openai/index.mjs.map +1 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
# InstaVM JavaScript SDK
|
|
2
|
+
|
|
3
|
+
A comprehensive JavaScript/TypeScript client library for InstaVM's code execution and browser automation APIs.
|
|
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
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install instavm
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Code Execution
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { InstaVM, ExecutionError, NetworkError } from 'instavm';
|
|
28
|
+
|
|
29
|
+
// Create client with automatic session management
|
|
30
|
+
const client = new InstaVM('your_api_key');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Execute a command
|
|
34
|
+
const result = await client.execute("print(100**100)");
|
|
35
|
+
console.log(result);
|
|
36
|
+
|
|
37
|
+
// Get usage info for the session
|
|
38
|
+
const usage = await client.getUsage();
|
|
39
|
+
console.log(usage);
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof ExecutionError) {
|
|
43
|
+
console.log(`Code execution failed: ${error.message}`);
|
|
44
|
+
} else if (error instanceof NetworkError) {
|
|
45
|
+
console.log(`Network issue: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
} finally {
|
|
48
|
+
await client.dispose();
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### File Upload
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { InstaVM } from 'instavm';
|
|
56
|
+
|
|
57
|
+
const client = new InstaVM('your_api_key');
|
|
58
|
+
|
|
59
|
+
// Upload files to the execution environment
|
|
60
|
+
const files = [
|
|
61
|
+
{
|
|
62
|
+
name: 'script.py',
|
|
63
|
+
content: 'print("Hello from uploaded file!")',
|
|
64
|
+
path: '/remote/path/script.py'
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const result = await client.upload(files);
|
|
69
|
+
console.log(result);
|
|
70
|
+
|
|
71
|
+
// Execute the uploaded file
|
|
72
|
+
const execution = await client.execute('python /remote/path/script.py', {
|
|
73
|
+
language: 'bash'
|
|
74
|
+
});
|
|
75
|
+
console.log(execution.output);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Error Handling
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import {
|
|
82
|
+
InstaVM,
|
|
83
|
+
AuthenticationError,
|
|
84
|
+
RateLimitError,
|
|
85
|
+
SessionError,
|
|
86
|
+
QuotaExceededError
|
|
87
|
+
} from 'instavm';
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const client = new InstaVM('invalid_key');
|
|
91
|
+
await client.execute('print("test")');
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error instanceof AuthenticationError) {
|
|
94
|
+
console.log("Invalid API key");
|
|
95
|
+
} else if (error instanceof RateLimitError) {
|
|
96
|
+
console.log(`Rate limit exceeded - retry after ${error.retryAfter} seconds`);
|
|
97
|
+
} else if (error instanceof QuotaExceededError) {
|
|
98
|
+
console.log("Usage quota exceeded");
|
|
99
|
+
} else if (error instanceof SessionError) {
|
|
100
|
+
console.log(`Session error: ${error.message}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Async Execution
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { InstaVM } from 'instavm';
|
|
109
|
+
|
|
110
|
+
const client = new InstaVM('your_api_key');
|
|
111
|
+
|
|
112
|
+
// Execute command asynchronously (returns task info)
|
|
113
|
+
const result = await client.executeAsync("sleep 5 && echo 'Long task complete!'", {
|
|
114
|
+
language: 'bash'
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
console.log(`Task ${result.taskId} status: ${result.status}`);
|
|
118
|
+
console.log(`Output so far: ${result.output}`);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Browser Automation
|
|
122
|
+
|
|
123
|
+
### Basic Browser Usage
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { InstaVM } from 'instavm';
|
|
127
|
+
|
|
128
|
+
const client = new InstaVM('your_api_key');
|
|
129
|
+
|
|
130
|
+
// Create browser session
|
|
131
|
+
const session = await client.browser.createSession({
|
|
132
|
+
viewportWidth: 1920,
|
|
133
|
+
viewportHeight: 1080
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Navigate to website
|
|
137
|
+
await session.navigate('https://example.com');
|
|
138
|
+
|
|
139
|
+
// Take screenshot
|
|
140
|
+
const screenshot = await session.screenshot();
|
|
141
|
+
console.log(`Screenshot captured: ${screenshot.length} chars`);
|
|
142
|
+
|
|
143
|
+
// Extract page data
|
|
144
|
+
const elements = await session.extractElements('h1, p', ['text', 'href']);
|
|
145
|
+
console.log('Page content:', elements);
|
|
146
|
+
|
|
147
|
+
// Clean up
|
|
148
|
+
await session.close();
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Advanced Browser Interactions
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Navigate with options
|
|
155
|
+
await session.navigate('https://github.com/login', {
|
|
156
|
+
waitTimeout: 30000,
|
|
157
|
+
waitUntil: 'networkidle'
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Fill login form
|
|
161
|
+
await session.fill('input[name="login"]', 'username');
|
|
162
|
+
await session.fill('input[name="password"]', 'password');
|
|
163
|
+
|
|
164
|
+
// Click submit button
|
|
165
|
+
await session.click('input[type="submit"]');
|
|
166
|
+
|
|
167
|
+
// Wait for navigation
|
|
168
|
+
await session.wait({ type: 'navigation' });
|
|
169
|
+
|
|
170
|
+
// Scroll to load more content
|
|
171
|
+
await session.scroll({ y: 1000 });
|
|
172
|
+
|
|
173
|
+
// Extract dynamic content
|
|
174
|
+
const repos = await session.extractElements('.repo-list-item', ['text']);
|
|
175
|
+
console.log('Repositories found:', repos.length);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Browser Session Management
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Create session with custom options
|
|
182
|
+
const session = await client.browser.createSession({
|
|
183
|
+
viewportWidth: 1280,
|
|
184
|
+
viewportHeight: 720,
|
|
185
|
+
userAgent: 'CustomBot/1.0'
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Session supports event listeners
|
|
189
|
+
session.on('navigation', (result) => {
|
|
190
|
+
console.log(`Navigated to: ${result.url}`);
|
|
191
|
+
console.log(`Page title: ${result.title}`);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
session.on('error', (error) => {
|
|
195
|
+
console.error('Session error:', error.message);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
session.on('close', () => {
|
|
199
|
+
console.log('Session closed');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Check if session is still active
|
|
203
|
+
if (session.isActive) {
|
|
204
|
+
await session.navigate('https://example.com');
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Context Manager Pattern
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// Use browser session with automatic cleanup
|
|
212
|
+
const client = new InstaVM('your_api_key');
|
|
213
|
+
|
|
214
|
+
async function automateWebsite() {
|
|
215
|
+
const session = await client.browser.createSession();
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
await session.navigate('https://httpbin.org/forms/post');
|
|
219
|
+
|
|
220
|
+
// Fill and submit form
|
|
221
|
+
await session.fill('input[name="custname"]', 'John Doe');
|
|
222
|
+
await session.fill('input[name="custtel"]', '555-1234');
|
|
223
|
+
await session.click('input[type="submit"]');
|
|
224
|
+
|
|
225
|
+
// Wait for result
|
|
226
|
+
await session.wait({ type: 'visible', selector: 'pre' });
|
|
227
|
+
|
|
228
|
+
// Extract result
|
|
229
|
+
const result = await session.extractElements('pre', ['text']);
|
|
230
|
+
return result[0]?.text;
|
|
231
|
+
|
|
232
|
+
} finally {
|
|
233
|
+
await session.close();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const result = await automateWebsite();
|
|
238
|
+
console.log('Form submission result:', result);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Mixed Code + Browser Automation
|
|
242
|
+
|
|
243
|
+
Combine code execution with browser automation for powerful workflows:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { InstaVM } from 'instavm';
|
|
247
|
+
|
|
248
|
+
const client = new InstaVM('your_api_key');
|
|
249
|
+
|
|
250
|
+
// Execute Python code to prepare data
|
|
251
|
+
const dataPrep = await client.execute(`
|
|
252
|
+
import json
|
|
253
|
+
import pandas as pd
|
|
254
|
+
|
|
255
|
+
# Prepare search terms
|
|
256
|
+
search_terms = ["AI", "Machine Learning", "JavaScript"]
|
|
257
|
+
data = {"terms": search_terms, "timestamp": "2024-01-01"}
|
|
258
|
+
print(json.dumps(data))
|
|
259
|
+
`);
|
|
260
|
+
|
|
261
|
+
const searchData = JSON.parse(dataPrep.output.trim());
|
|
262
|
+
|
|
263
|
+
// Use browser to search and collect results
|
|
264
|
+
const session = await client.browser.createSession();
|
|
265
|
+
const results = [];
|
|
266
|
+
|
|
267
|
+
for (const term of searchData.terms) {
|
|
268
|
+
await session.navigate(`https://news.ycombinator.com/search?q=${encodeURIComponent(term)}`);
|
|
269
|
+
|
|
270
|
+
const headlines = await session.extractElements('.titleline > a', ['text', 'href']);
|
|
271
|
+
results.push({
|
|
272
|
+
term,
|
|
273
|
+
headlines: headlines.slice(0, 5) // Top 5 results
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await session.close();
|
|
278
|
+
|
|
279
|
+
// Process results with Python
|
|
280
|
+
const analysis = await client.execute(`
|
|
281
|
+
import json
|
|
282
|
+
data = ${JSON.stringify(results)}
|
|
283
|
+
|
|
284
|
+
# Analyze results
|
|
285
|
+
total_headlines = sum(len(item['headlines']) for item in data)
|
|
286
|
+
print(f"Collected {total_headlines} headlines across {len(data)} search terms")
|
|
287
|
+
|
|
288
|
+
# Find most common words
|
|
289
|
+
all_text = ' '.join([headline['text'] for item in data for headline in item['headlines']])
|
|
290
|
+
words = all_text.lower().split()
|
|
291
|
+
word_counts = {}
|
|
292
|
+
for word in words:
|
|
293
|
+
if len(word) > 3: # Filter short words
|
|
294
|
+
word_counts[word] = word_counts.get(word, 0) + 1
|
|
295
|
+
|
|
296
|
+
# Top 10 words
|
|
297
|
+
top_words = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)[:10]
|
|
298
|
+
print("Top words:", top_words)
|
|
299
|
+
`);
|
|
300
|
+
|
|
301
|
+
console.log(analysis.output);
|
|
302
|
+
await client.dispose();
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Language Support
|
|
306
|
+
|
|
307
|
+
### Python Code Execution
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Python with libraries
|
|
311
|
+
const result = await client.execute(`
|
|
312
|
+
import pandas as pd
|
|
313
|
+
import numpy as np
|
|
314
|
+
|
|
315
|
+
# Create sample data
|
|
316
|
+
data = pd.DataFrame({
|
|
317
|
+
'numbers': np.random.randn(100),
|
|
318
|
+
'categories': np.random.choice(['A', 'B', 'C'], 100)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
# Basic statistics
|
|
322
|
+
print(f"Mean: {data['numbers'].mean():.2f}")
|
|
323
|
+
print(f"Std: {data['numbers'].std():.2f}")
|
|
324
|
+
print(f"Categories: {data['categories'].value_counts().to_dict()}")
|
|
325
|
+
`);
|
|
326
|
+
|
|
327
|
+
console.log(result.output);
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Bash Commands
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// System operations
|
|
334
|
+
const sysInfo = await client.execute(`
|
|
335
|
+
echo "System Information:"
|
|
336
|
+
echo "==================="
|
|
337
|
+
uname -a
|
|
338
|
+
echo
|
|
339
|
+
echo "Disk Usage:"
|
|
340
|
+
df -h
|
|
341
|
+
echo
|
|
342
|
+
echo "Memory Info:"
|
|
343
|
+
free -h
|
|
344
|
+
`, { language: 'bash' });
|
|
345
|
+
|
|
346
|
+
console.log(sysInfo.output);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Session Persistence
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// Variables persist across executions within the same session
|
|
353
|
+
await client.execute('data = [1, 2, 3, 4, 5]');
|
|
354
|
+
await client.execute('total = sum(data)');
|
|
355
|
+
|
|
356
|
+
const result = await client.execute('print(f"Total: {total}, Average: {total/len(data)}")');
|
|
357
|
+
console.log(result.output); // Output: Total: 15, Average: 3.0
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Advanced Features
|
|
361
|
+
|
|
362
|
+
### Wait Conditions
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
// Wait for element to appear
|
|
366
|
+
await session.wait({
|
|
367
|
+
type: 'visible',
|
|
368
|
+
selector: '.loading-complete',
|
|
369
|
+
timeout: 30000
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Wait for element to disappear
|
|
373
|
+
await session.wait({
|
|
374
|
+
type: 'hidden',
|
|
375
|
+
selector: '.spinner'
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Wait for page load
|
|
379
|
+
await session.wait({
|
|
380
|
+
type: 'networkidle'
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Simple timeout
|
|
384
|
+
await session.wait({
|
|
385
|
+
type: 'timeout',
|
|
386
|
+
ms: 5000
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Screenshot Options
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// Full page screenshot
|
|
394
|
+
const fullPage = await session.screenshot({
|
|
395
|
+
fullPage: true,
|
|
396
|
+
format: 'png'
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Clip specific area
|
|
400
|
+
const clipped = await session.screenshot({
|
|
401
|
+
clip: {
|
|
402
|
+
x: 0,
|
|
403
|
+
y: 0,
|
|
404
|
+
width: 800,
|
|
405
|
+
height: 600
|
|
406
|
+
},
|
|
407
|
+
format: 'jpeg',
|
|
408
|
+
quality: 90
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Screenshots return base64 encoded strings
|
|
412
|
+
const buffer = Buffer.from(fullPage, 'base64');
|
|
413
|
+
// Save to file if needed
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Element Extraction
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// Extract multiple attributes
|
|
420
|
+
const links = await session.extractElements('a', ['href', 'text', 'title']);
|
|
421
|
+
|
|
422
|
+
// Extract with CSS selectors
|
|
423
|
+
const articles = await session.extractElements('article h2, .post-title', ['text']);
|
|
424
|
+
|
|
425
|
+
// Extract form data
|
|
426
|
+
const formData = await session.extractElements('input, select, textarea', [
|
|
427
|
+
'name', 'value', 'type', 'placeholder'
|
|
428
|
+
]);
|
|
429
|
+
|
|
430
|
+
console.log('Links found:', links);
|
|
431
|
+
console.log('Articles:', articles);
|
|
432
|
+
console.log('Form fields:', formData);
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Error Handling Reference
|
|
436
|
+
|
|
437
|
+
### Error Types
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import {
|
|
441
|
+
InstaVMError, // Base error class
|
|
442
|
+
AuthenticationError, // API key issues
|
|
443
|
+
RateLimitError, // Rate limiting (has retryAfter property)
|
|
444
|
+
QuotaExceededError, // Usage quota exceeded
|
|
445
|
+
NetworkError, // Connection issues
|
|
446
|
+
ExecutionError, // Code execution failures
|
|
447
|
+
SessionError, // Session management issues
|
|
448
|
+
BrowserError, // General browser errors
|
|
449
|
+
BrowserSessionError, // Browser session issues
|
|
450
|
+
BrowserInteractionError, // Browser interaction failures
|
|
451
|
+
BrowserTimeoutError, // Browser operation timeouts
|
|
452
|
+
BrowserNavigationError, // Navigation failures
|
|
453
|
+
ElementNotFoundError // Element selection issues (has selector property)
|
|
454
|
+
} from 'instavm';
|
|
455
|
+
|
|
456
|
+
// Specific error handling
|
|
457
|
+
try {
|
|
458
|
+
await session.click('.non-existent-button');
|
|
459
|
+
} catch (error) {
|
|
460
|
+
if (error instanceof ElementNotFoundError) {
|
|
461
|
+
console.log(`Element not found: ${error.selector}`);
|
|
462
|
+
} else if (error instanceof BrowserTimeoutError) {
|
|
463
|
+
console.log('Operation timed out');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Retry Logic
|
|
469
|
+
|
|
470
|
+
The SDK includes automatic retry logic for:
|
|
471
|
+
- Network errors (connection issues)
|
|
472
|
+
- Rate limiting (with exponential backoff)
|
|
473
|
+
- Server errors (5xx status codes)
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// Customize retry behavior
|
|
477
|
+
const client = new InstaVM('your_api_key', {
|
|
478
|
+
maxRetries: 5,
|
|
479
|
+
retryDelay: 2000, // Base delay in milliseconds
|
|
480
|
+
timeout: 300000 // 5 minute timeout
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## API Reference
|
|
485
|
+
|
|
486
|
+
### InstaVM Client
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
class InstaVM {
|
|
490
|
+
constructor(apiKey: string, options?: InstaVMOptions)
|
|
491
|
+
|
|
492
|
+
// Code execution
|
|
493
|
+
execute(command: string, options?: ExecuteOptions): Promise<ExecutionResult>
|
|
494
|
+
executeAsync(command: string, options?: ExecuteOptions): Promise<AsyncExecutionResult>
|
|
495
|
+
|
|
496
|
+
// File operations
|
|
497
|
+
upload(files: FileUpload[], options?: UploadOptions): Promise<UploadResult>
|
|
498
|
+
|
|
499
|
+
// Session management
|
|
500
|
+
createSession(): Promise<string>
|
|
501
|
+
closeSession(sessionId?: string): Promise<void>
|
|
502
|
+
getUsage(sessionId?: string): Promise<UsageStats>
|
|
503
|
+
|
|
504
|
+
// Browser automation
|
|
505
|
+
browser: BrowserManager
|
|
506
|
+
|
|
507
|
+
// Properties
|
|
508
|
+
readonly sessionId: string | null
|
|
509
|
+
|
|
510
|
+
// Cleanup
|
|
511
|
+
dispose(): Promise<void>
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Configuration Options
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
interface InstaVMOptions {
|
|
519
|
+
baseURL?: string; // Default: 'https://api.instavm.io'
|
|
520
|
+
timeout?: number; // Default: 300000 (5 minutes)
|
|
521
|
+
maxRetries?: number; // Default: 3
|
|
522
|
+
retryDelay?: number; // Default: 1000ms
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
interface ExecuteOptions {
|
|
526
|
+
language?: 'python' | 'bash'; // Default: 'python'
|
|
527
|
+
timeout?: number; // Default: 15 seconds
|
|
528
|
+
sessionId?: string; // Use specific session
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Browser Manager
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
class BrowserManager {
|
|
536
|
+
createSession(options?: BrowserSessionOptions): Promise<BrowserSession>
|
|
537
|
+
listSessions(): Promise<BrowserSessionInfo[]>
|
|
538
|
+
getSession(sessionId: string): Promise<BrowserSessionInfo>
|
|
539
|
+
getLocalSession(sessionId: string): BrowserSession | undefined
|
|
540
|
+
getLocalSessions(): BrowserSession[]
|
|
541
|
+
closeAllSessions(): Promise<void>
|
|
542
|
+
dispose(): Promise<void>
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
interface BrowserSessionOptions {
|
|
546
|
+
viewportWidth?: number; // Default: 1920
|
|
547
|
+
viewportHeight?: number; // Default: 1080
|
|
548
|
+
userAgent?: string; // Custom user agent
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Browser Session
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
class BrowserSession extends EventEmitter {
|
|
556
|
+
// Navigation
|
|
557
|
+
navigate(url: string, options?: NavigateOptions): Promise<NavigationResult>
|
|
558
|
+
|
|
559
|
+
// Interaction
|
|
560
|
+
click(selector: string, options?: ClickOptions): Promise<void>
|
|
561
|
+
type(selector: string, text: string, options?: TypeOptions): Promise<void>
|
|
562
|
+
fill(selector: string, value: string, options?: FillOptions): Promise<void>
|
|
563
|
+
scroll(options?: ScrollOptions): Promise<void>
|
|
564
|
+
|
|
565
|
+
// Data extraction
|
|
566
|
+
screenshot(options?: ScreenshotOptions): Promise<string>
|
|
567
|
+
extractElements(selector: string, attributes?: string[]): Promise<ExtractedElement[]>
|
|
568
|
+
|
|
569
|
+
// Utilities
|
|
570
|
+
wait(condition: WaitCondition, timeout?: number): Promise<void>
|
|
571
|
+
close(): Promise<void>
|
|
572
|
+
|
|
573
|
+
// Properties
|
|
574
|
+
readonly sessionId: string
|
|
575
|
+
readonly isActive: boolean
|
|
576
|
+
|
|
577
|
+
// Events: 'navigation', 'error', 'close'
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Type Definitions
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
interface ExecutionResult {
|
|
585
|
+
output: string;
|
|
586
|
+
success: boolean;
|
|
587
|
+
executionTime: number;
|
|
588
|
+
sessionId?: string;
|
|
589
|
+
error?: string;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
interface NavigationResult {
|
|
593
|
+
success: boolean;
|
|
594
|
+
url: string;
|
|
595
|
+
title?: string;
|
|
596
|
+
status?: number;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
interface ExtractedElement {
|
|
600
|
+
text?: string;
|
|
601
|
+
href?: string;
|
|
602
|
+
[attribute: string]: string | undefined;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
type WaitCondition =
|
|
606
|
+
| { type: 'timeout'; ms: number }
|
|
607
|
+
| { type: 'visible'; selector: string }
|
|
608
|
+
| { type: 'hidden'; selector: string }
|
|
609
|
+
| { type: 'navigation' }
|
|
610
|
+
| { type: 'networkidle' };
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
## Best Practices
|
|
614
|
+
|
|
615
|
+
### Resource Management
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
// Always dispose of the client when done
|
|
619
|
+
const client = new InstaVM('your_api_key');
|
|
620
|
+
try {
|
|
621
|
+
// Your automation code
|
|
622
|
+
} finally {
|
|
623
|
+
await client.dispose(); // Closes all sessions and cleans up
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Or use a wrapper function
|
|
627
|
+
async function withInstaVM(apiKey: string, callback: (client: InstaVM) => Promise<void>) {
|
|
628
|
+
const client = new InstaVM(apiKey);
|
|
629
|
+
try {
|
|
630
|
+
await callback(client);
|
|
631
|
+
} finally {
|
|
632
|
+
await client.dispose();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
await withInstaVM('your_api_key', async (client) => {
|
|
637
|
+
const result = await client.execute('print("Hello, World!")');
|
|
638
|
+
console.log(result.output);
|
|
639
|
+
});
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Error Handling Strategy
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
// Implement comprehensive error handling
|
|
646
|
+
async function robustAutomation(client: InstaVM) {
|
|
647
|
+
let session: BrowserSession | null = null;
|
|
648
|
+
|
|
649
|
+
try {
|
|
650
|
+
session = await client.browser.createSession();
|
|
651
|
+
|
|
652
|
+
// Retry navigation with timeout
|
|
653
|
+
let retries = 3;
|
|
654
|
+
while (retries > 0) {
|
|
655
|
+
try {
|
|
656
|
+
await session.navigate('https://example.com');
|
|
657
|
+
break;
|
|
658
|
+
} catch (error) {
|
|
659
|
+
if (error instanceof BrowserTimeoutError && retries > 1) {
|
|
660
|
+
console.log(`Navigation timeout, retrying... (${retries - 1} attempts left)`);
|
|
661
|
+
retries--;
|
|
662
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
663
|
+
} else {
|
|
664
|
+
throw error;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Safe element interaction
|
|
670
|
+
try {
|
|
671
|
+
await session.click('button.submit');
|
|
672
|
+
} catch (error) {
|
|
673
|
+
if (error instanceof ElementNotFoundError) {
|
|
674
|
+
console.log('Submit button not found, trying alternative selector');
|
|
675
|
+
await session.click('input[type="submit"]');
|
|
676
|
+
} else {
|
|
677
|
+
throw error;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
} catch (error) {
|
|
682
|
+
console.error('Automation failed:', error.message);
|
|
683
|
+
throw error;
|
|
684
|
+
} finally {
|
|
685
|
+
if (session) {
|
|
686
|
+
await session.close();
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Performance Optimization
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
// Batch operations when possible
|
|
696
|
+
const session = await client.browser.createSession();
|
|
697
|
+
|
|
698
|
+
// Instead of multiple separate calls
|
|
699
|
+
await session.navigate('https://example.com');
|
|
700
|
+
const title = await session.extractElements('title', ['text']);
|
|
701
|
+
const links = await session.extractElements('a', ['href']);
|
|
702
|
+
const images = await session.extractElements('img', ['src', 'alt']);
|
|
703
|
+
|
|
704
|
+
// Consider using a single extraction call for related elements
|
|
705
|
+
const pageData = await session.extractElements('title, a, img', ['text', 'href', 'src', 'alt']);
|
|
706
|
+
|
|
707
|
+
// Use appropriate timeouts
|
|
708
|
+
await session.navigate('https://slow-site.com', {
|
|
709
|
+
waitTimeout: 60000 // Increase timeout for slow sites
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
// Optimize screenshot size for performance
|
|
713
|
+
const thumbnail = await session.screenshot({
|
|
714
|
+
clip: { x: 0, y: 0, width: 400, height: 300 },
|
|
715
|
+
format: 'jpeg',
|
|
716
|
+
quality: 70
|
|
717
|
+
});
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
## CommonJS Usage
|
|
721
|
+
|
|
722
|
+
For projects using CommonJS:
|
|
723
|
+
|
|
724
|
+
```javascript
|
|
725
|
+
const { InstaVM, AuthenticationError } = require('instavm');
|
|
726
|
+
|
|
727
|
+
async function main() {
|
|
728
|
+
const client = new InstaVM('your_api_key');
|
|
729
|
+
|
|
730
|
+
try {
|
|
731
|
+
const result = await client.execute('print("Hello from CommonJS!")');
|
|
732
|
+
console.log(result.output);
|
|
733
|
+
} catch (error) {
|
|
734
|
+
if (error instanceof AuthenticationError) {
|
|
735
|
+
console.error('Authentication failed');
|
|
736
|
+
}
|
|
737
|
+
} finally {
|
|
738
|
+
await client.dispose();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
main().catch(console.error);
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
## Development and Testing
|
|
746
|
+
|
|
747
|
+
### Running Tests
|
|
748
|
+
|
|
749
|
+
```bash
|
|
750
|
+
# Install dependencies
|
|
751
|
+
npm install
|
|
752
|
+
|
|
753
|
+
# Run unit tests
|
|
754
|
+
npm test
|
|
755
|
+
|
|
756
|
+
# Run integration tests (requires API key)
|
|
757
|
+
INSTAVM_API_KEY=your_key npm run test:integration
|
|
758
|
+
|
|
759
|
+
# Build the package
|
|
760
|
+
npm run build
|
|
761
|
+
|
|
762
|
+
# Type checking
|
|
763
|
+
npm run type-check
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### Contributing
|
|
767
|
+
|
|
768
|
+
This is an official SDK. For issues and feature requests, please use the GitHub repository.
|
|
769
|
+
|
|
770
|
+
## Requirements
|
|
771
|
+
|
|
772
|
+
- Node.js 16+
|
|
773
|
+
- TypeScript 5+ (for TypeScript projects)
|
|
774
|
+
|
|
775
|
+
## License
|
|
776
|
+
|
|
777
|
+
MIT
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## Changelog
|
|
782
|
+
|
|
783
|
+
### Version 0.1.0
|
|
784
|
+
|
|
785
|
+
- ✅ Code execution fully functional (Python, Bash)
|
|
786
|
+
- ✅ Browser automation complete (navigation, interaction, extraction)
|
|
787
|
+
- ✅ Comprehensive TypeScript support
|
|
788
|
+
- ✅ Error handling with specific error types
|
|
789
|
+
- ✅ Session management and automatic cleanup
|
|
790
|
+
- ✅ File upload capabilities
|
|
791
|
+
- ✅ Async execution support
|
|
792
|
+
- ✅ Event-driven browser sessions
|
|
793
|
+
- ✅ Modern build system with multiple output formats
|
|
794
|
+
|
|
795
|
+
The JavaScript SDK provides complete feature parity with the Python SDK and is ready for production use.
|