instavm 0.2.2 → 0.3.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 +84 -5
- package/dist/index.d.mts +73 -2
- package/dist/index.d.ts +73 -2
- package/dist/index.js +114 -101
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +114 -102
- package/dist/index.mjs.map +1 -1
- package/dist/integrations/openai/index.d.mts +2 -1
- package/dist/integrations/openai/index.d.ts +2 -1
- package/dist/integrations/openai/index.js +5 -0
- package/dist/integrations/openai/index.js.map +1 -1
- package/dist/integrations/openai/index.mjs +4 -0
- package/dist/integrations/openai/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -75,6 +75,29 @@ const execution = await client.execute('python /remote/path/script.py', {
|
|
|
75
75
|
console.log(execution.output);
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### File Download
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { InstaVM } from 'instavm';
|
|
82
|
+
import fs from 'fs';
|
|
83
|
+
|
|
84
|
+
const client = new InstaVM('your_api_key');
|
|
85
|
+
|
|
86
|
+
// Create a file in the remote environment
|
|
87
|
+
await client.execute(`
|
|
88
|
+
import pandas as pd
|
|
89
|
+
df = pd.DataFrame({'name': ['Alice', 'Bob'], 'age': [25, 30]})
|
|
90
|
+
df.to_csv('data.csv', index=False)
|
|
91
|
+
`);
|
|
92
|
+
|
|
93
|
+
// Download the file
|
|
94
|
+
const result = await client.download('data.csv');
|
|
95
|
+
console.log(`Downloaded ${result.size} bytes`);
|
|
96
|
+
|
|
97
|
+
// Save to local file
|
|
98
|
+
fs.writeFileSync('local-data.csv', result.content);
|
|
99
|
+
```
|
|
100
|
+
|
|
78
101
|
### Error Handling
|
|
79
102
|
|
|
80
103
|
```typescript
|
|
@@ -432,6 +455,39 @@ console.log('Articles:', articles);
|
|
|
432
455
|
console.log('Form fields:', formData);
|
|
433
456
|
```
|
|
434
457
|
|
|
458
|
+
### LLM-Friendly Content Extraction
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
// Extract clean, LLM-optimized content from a webpage
|
|
462
|
+
const content = await session.extractContent({
|
|
463
|
+
includeInteractive: true, // Include clickable/typeable elements
|
|
464
|
+
includeAnchors: true, // Include content-to-selector mappings
|
|
465
|
+
maxAnchors: 50 // Limit number of anchors
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Get clean article text (no ads, no navigation, no scripts)
|
|
469
|
+
console.log('Title:', content.readableContent.title);
|
|
470
|
+
console.log('Article:', content.readableContent.content);
|
|
471
|
+
console.log('Author:', content.readableContent.byline);
|
|
472
|
+
|
|
473
|
+
// Find interactive elements (buttons, links, inputs)
|
|
474
|
+
const loginButton = content.interactiveElements?.find(
|
|
475
|
+
el => el.text?.toLowerCase().includes('login')
|
|
476
|
+
);
|
|
477
|
+
if (loginButton) {
|
|
478
|
+
await session.click(loginButton.selector);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Use content anchors to map text to selectors
|
|
482
|
+
// Perfect for LLM agents that need to "read then click"
|
|
483
|
+
const signupLink = content.contentAnchors?.find(
|
|
484
|
+
anchor => anchor.text.toLowerCase().includes('sign up')
|
|
485
|
+
);
|
|
486
|
+
if (signupLink) {
|
|
487
|
+
await session.click(signupLink.selector);
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
435
491
|
## Error Handling Reference
|
|
436
492
|
|
|
437
493
|
### Error Types
|
|
@@ -495,7 +551,8 @@ class InstaVM {
|
|
|
495
551
|
|
|
496
552
|
// File operations
|
|
497
553
|
upload(files: FileUpload[], options?: UploadOptions): Promise<UploadResult>
|
|
498
|
-
|
|
554
|
+
download(filename: string, options?: DownloadOptions): Promise<DownloadResult>
|
|
555
|
+
|
|
499
556
|
// Session management
|
|
500
557
|
createSession(): Promise<string>
|
|
501
558
|
closeSession(sessionId?: string): Promise<void>
|
|
@@ -565,7 +622,8 @@ class BrowserSession extends EventEmitter {
|
|
|
565
622
|
// Data extraction
|
|
566
623
|
screenshot(options?: ScreenshotOptions): Promise<string>
|
|
567
624
|
extractElements(selector: string, attributes?: string[]): Promise<ExtractedElement[]>
|
|
568
|
-
|
|
625
|
+
extractContent(options?: ExtractContentOptions): Promise<ExtractedContent>
|
|
626
|
+
|
|
569
627
|
// Utilities
|
|
570
628
|
wait(condition: WaitCondition, timeout?: number): Promise<void>
|
|
571
629
|
close(): Promise<void>
|
|
@@ -750,11 +808,14 @@ main().catch(console.error);
|
|
|
750
808
|
# Install dependencies
|
|
751
809
|
npm install
|
|
752
810
|
|
|
753
|
-
# Run unit tests
|
|
754
|
-
npm test
|
|
811
|
+
# Run unit tests (no API key required)
|
|
812
|
+
npm run test:unit
|
|
755
813
|
|
|
756
814
|
# Run integration tests (requires API key)
|
|
757
|
-
INSTAVM_API_KEY=
|
|
815
|
+
INSTAVM_API_KEY=your_api_key npm run test:integration
|
|
816
|
+
|
|
817
|
+
# Run all tests
|
|
818
|
+
npm test
|
|
758
819
|
|
|
759
820
|
# Build the package
|
|
760
821
|
npm run build
|
|
@@ -763,6 +824,8 @@ npm run build
|
|
|
763
824
|
npm run type-check
|
|
764
825
|
```
|
|
765
826
|
|
|
827
|
+
**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.
|
|
828
|
+
|
|
766
829
|
### Contributing
|
|
767
830
|
|
|
768
831
|
This is an official SDK. For issues and feature requests, please use the GitHub repository.
|
|
@@ -782,6 +845,22 @@ All rights reserved. No redistribution or modification permitted.
|
|
|
782
845
|
|
|
783
846
|
## Changelog
|
|
784
847
|
|
|
848
|
+
### Version 0.3.0
|
|
849
|
+
|
|
850
|
+
- ✅ **NEW**: File download functionality - Download files from remote VM
|
|
851
|
+
- ✅ **NEW**: LLM-friendly content extraction - Extract clean, readable content with interactive element mapping
|
|
852
|
+
- ✅ Enhanced browser automation with content anchors for intelligent LLM agents
|
|
853
|
+
- ✅ Full API parity with Python SDK
|
|
854
|
+
|
|
855
|
+
### Version 0.2.1
|
|
856
|
+
|
|
857
|
+
- ✅ Bug fixes and improvements
|
|
858
|
+
|
|
859
|
+
### Version 0.2.0
|
|
860
|
+
|
|
861
|
+
- ✅ Enhanced session management
|
|
862
|
+
- ✅ Improved error handling
|
|
863
|
+
|
|
785
864
|
### Version 0.1.0
|
|
786
865
|
|
|
787
866
|
- ✅ Code execution fully functional (Python, Bash)
|
package/dist/index.d.mts
CHANGED
|
@@ -46,7 +46,7 @@ declare class HTTPClient {
|
|
|
46
46
|
*/
|
|
47
47
|
post<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T>;
|
|
48
48
|
/**
|
|
49
|
-
* POST request for code execution (X-API-Key header
|
|
49
|
+
* POST request for code execution (uses X-API-Key header like Python client)
|
|
50
50
|
*/
|
|
51
51
|
postExecution<T = any>(url: string, data: any, headers?: Record<string, string>): Promise<T>;
|
|
52
52
|
/**
|
|
@@ -61,6 +61,10 @@ declare class HTTPClient {
|
|
|
61
61
|
* DELETE request
|
|
62
62
|
*/
|
|
63
63
|
delete<T = any>(url: string, headers?: Record<string, string>): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* POST request that returns raw binary data (for file downloads)
|
|
66
|
+
*/
|
|
67
|
+
postRaw(url: string, data?: any, headers?: Record<string, string>): Promise<ArrayBuffer>;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
interface BrowserSessionOptions {
|
|
@@ -146,6 +150,34 @@ interface ExtractOptions {
|
|
|
146
150
|
attributes?: string[];
|
|
147
151
|
maxResults?: number;
|
|
148
152
|
}
|
|
153
|
+
interface ExtractContentOptions {
|
|
154
|
+
includeInteractive?: boolean;
|
|
155
|
+
includeAnchors?: boolean;
|
|
156
|
+
maxAnchors?: number;
|
|
157
|
+
}
|
|
158
|
+
interface InteractiveElement {
|
|
159
|
+
type: 'button' | 'link' | 'input' | 'select' | 'textarea';
|
|
160
|
+
selector: string;
|
|
161
|
+
text?: string;
|
|
162
|
+
label?: string;
|
|
163
|
+
placeholder?: string;
|
|
164
|
+
value?: string;
|
|
165
|
+
}
|
|
166
|
+
interface ContentAnchor {
|
|
167
|
+
text: string;
|
|
168
|
+
selector: string;
|
|
169
|
+
}
|
|
170
|
+
interface ExtractedContent {
|
|
171
|
+
readableContent: {
|
|
172
|
+
content: string;
|
|
173
|
+
title?: string;
|
|
174
|
+
byline?: string;
|
|
175
|
+
excerpt?: string;
|
|
176
|
+
siteName?: string;
|
|
177
|
+
};
|
|
178
|
+
interactiveElements?: InteractiveElement[];
|
|
179
|
+
contentAnchors?: ContentAnchor[];
|
|
180
|
+
}
|
|
149
181
|
|
|
150
182
|
/**
|
|
151
183
|
* Browser session for automation
|
|
@@ -183,6 +215,32 @@ declare class BrowserSession extends EventEmitter {
|
|
|
183
215
|
* Extract elements from the page
|
|
184
216
|
*/
|
|
185
217
|
extractElements(selector: string, attributes?: string[], options?: ExtractOptions): Promise<ExtractedElement[]>;
|
|
218
|
+
/**
|
|
219
|
+
* Extract LLM-friendly content from the current page
|
|
220
|
+
*
|
|
221
|
+
* Returns clean article content, interactive elements, and content anchors
|
|
222
|
+
* for intelligent browser automation with LLMs.
|
|
223
|
+
*
|
|
224
|
+
* @param options - Content extraction options
|
|
225
|
+
* @returns Extracted content with readable text, interactive elements, and content anchors
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const content = await session.extractContent();
|
|
230
|
+
*
|
|
231
|
+
* // LLM reads clean content
|
|
232
|
+
* const article = content.readableContent.content;
|
|
233
|
+
*
|
|
234
|
+
* // LLM finds "Sign Up" in content and uses anchors to get selector
|
|
235
|
+
* const signUpAnchor = content.contentAnchors?.find(
|
|
236
|
+
* anchor => anchor.text.toLowerCase().includes('sign up')
|
|
237
|
+
* );
|
|
238
|
+
* if (signUpAnchor) {
|
|
239
|
+
* await session.click(signUpAnchor.selector);
|
|
240
|
+
* }
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
extractContent(options?: ExtractContentOptions): Promise<ExtractedContent>;
|
|
186
244
|
/**
|
|
187
245
|
* Wait for a condition
|
|
188
246
|
*/
|
|
@@ -280,6 +338,15 @@ interface UsageStats {
|
|
|
280
338
|
quotaRemaining: number;
|
|
281
339
|
quotaLimit: number;
|
|
282
340
|
}
|
|
341
|
+
interface DownloadOptions {
|
|
342
|
+
sessionId?: string;
|
|
343
|
+
}
|
|
344
|
+
interface DownloadResult {
|
|
345
|
+
success: boolean;
|
|
346
|
+
filename: string;
|
|
347
|
+
content: Buffer;
|
|
348
|
+
size: number;
|
|
349
|
+
}
|
|
283
350
|
|
|
284
351
|
interface InstaVMOptions {
|
|
285
352
|
baseURL?: string;
|
|
@@ -319,6 +386,10 @@ declare class InstaVM {
|
|
|
319
386
|
* Get usage statistics for a session
|
|
320
387
|
*/
|
|
321
388
|
getUsage(sessionId?: string): Promise<UsageStats>;
|
|
389
|
+
/**
|
|
390
|
+
* Download a file from the remote VM
|
|
391
|
+
*/
|
|
392
|
+
download(filename: string, options?: DownloadOptions): Promise<DownloadResult>;
|
|
322
393
|
/**
|
|
323
394
|
* Get the current session ID
|
|
324
395
|
*/
|
|
@@ -421,4 +492,4 @@ declare class ElementNotFoundError extends BrowserError {
|
|
|
421
492
|
constructor(message?: string, selector?: string, options?: any);
|
|
422
493
|
}
|
|
423
494
|
|
|
424
|
-
export { type ApiResponse, type AsyncExecutionResult, AuthenticationError, BrowserError, BrowserInteractionError, BrowserManager, BrowserNavigationError, BrowserSession, BrowserSessionError, type BrowserSessionInfo, type BrowserSessionOptions, BrowserTimeoutError, type ClickOptions, ElementNotFoundError, type ExecuteOptions, ExecutionError, type ExecutionResult, type ExtractOptions, type ExtractedElement, type FileUpload, type FillOptions, type HttpClientConfig, InstaVM, InstaVMError, type InstaVMOptions, type NavigateOptions, type NavigationResult, NetworkError, QuotaExceededError, RateLimitError, type RequestConfig, type RetryConfig, type ScreenshotOptions, type ScrollOptions, SessionError, type TypeOptions, type UploadOptions, type UploadResult, type UsageStats, type WaitCondition };
|
|
495
|
+
export { type ApiResponse, type AsyncExecutionResult, AuthenticationError, BrowserError, BrowserInteractionError, BrowserManager, BrowserNavigationError, BrowserSession, BrowserSessionError, type BrowserSessionInfo, type BrowserSessionOptions, BrowserTimeoutError, type ClickOptions, type ContentAnchor, type DownloadOptions, type DownloadResult, ElementNotFoundError, type ExecuteOptions, ExecutionError, type ExecutionResult, type ExtractContentOptions, type ExtractOptions, type ExtractedContent, type ExtractedElement, type FileUpload, type FillOptions, type HttpClientConfig, InstaVM, InstaVMError, type InstaVMOptions, type InteractiveElement, type NavigateOptions, type NavigationResult, NetworkError, QuotaExceededError, RateLimitError, type RequestConfig, type RetryConfig, type ScreenshotOptions, type ScrollOptions, SessionError, type TypeOptions, type UploadOptions, type UploadResult, type UsageStats, type WaitCondition };
|
package/dist/index.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ declare class HTTPClient {
|
|
|
46
46
|
*/
|
|
47
47
|
post<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T>;
|
|
48
48
|
/**
|
|
49
|
-
* POST request for code execution (X-API-Key header
|
|
49
|
+
* POST request for code execution (uses X-API-Key header like Python client)
|
|
50
50
|
*/
|
|
51
51
|
postExecution<T = any>(url: string, data: any, headers?: Record<string, string>): Promise<T>;
|
|
52
52
|
/**
|
|
@@ -61,6 +61,10 @@ declare class HTTPClient {
|
|
|
61
61
|
* DELETE request
|
|
62
62
|
*/
|
|
63
63
|
delete<T = any>(url: string, headers?: Record<string, string>): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* POST request that returns raw binary data (for file downloads)
|
|
66
|
+
*/
|
|
67
|
+
postRaw(url: string, data?: any, headers?: Record<string, string>): Promise<ArrayBuffer>;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
interface BrowserSessionOptions {
|
|
@@ -146,6 +150,34 @@ interface ExtractOptions {
|
|
|
146
150
|
attributes?: string[];
|
|
147
151
|
maxResults?: number;
|
|
148
152
|
}
|
|
153
|
+
interface ExtractContentOptions {
|
|
154
|
+
includeInteractive?: boolean;
|
|
155
|
+
includeAnchors?: boolean;
|
|
156
|
+
maxAnchors?: number;
|
|
157
|
+
}
|
|
158
|
+
interface InteractiveElement {
|
|
159
|
+
type: 'button' | 'link' | 'input' | 'select' | 'textarea';
|
|
160
|
+
selector: string;
|
|
161
|
+
text?: string;
|
|
162
|
+
label?: string;
|
|
163
|
+
placeholder?: string;
|
|
164
|
+
value?: string;
|
|
165
|
+
}
|
|
166
|
+
interface ContentAnchor {
|
|
167
|
+
text: string;
|
|
168
|
+
selector: string;
|
|
169
|
+
}
|
|
170
|
+
interface ExtractedContent {
|
|
171
|
+
readableContent: {
|
|
172
|
+
content: string;
|
|
173
|
+
title?: string;
|
|
174
|
+
byline?: string;
|
|
175
|
+
excerpt?: string;
|
|
176
|
+
siteName?: string;
|
|
177
|
+
};
|
|
178
|
+
interactiveElements?: InteractiveElement[];
|
|
179
|
+
contentAnchors?: ContentAnchor[];
|
|
180
|
+
}
|
|
149
181
|
|
|
150
182
|
/**
|
|
151
183
|
* Browser session for automation
|
|
@@ -183,6 +215,32 @@ declare class BrowserSession extends EventEmitter {
|
|
|
183
215
|
* Extract elements from the page
|
|
184
216
|
*/
|
|
185
217
|
extractElements(selector: string, attributes?: string[], options?: ExtractOptions): Promise<ExtractedElement[]>;
|
|
218
|
+
/**
|
|
219
|
+
* Extract LLM-friendly content from the current page
|
|
220
|
+
*
|
|
221
|
+
* Returns clean article content, interactive elements, and content anchors
|
|
222
|
+
* for intelligent browser automation with LLMs.
|
|
223
|
+
*
|
|
224
|
+
* @param options - Content extraction options
|
|
225
|
+
* @returns Extracted content with readable text, interactive elements, and content anchors
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const content = await session.extractContent();
|
|
230
|
+
*
|
|
231
|
+
* // LLM reads clean content
|
|
232
|
+
* const article = content.readableContent.content;
|
|
233
|
+
*
|
|
234
|
+
* // LLM finds "Sign Up" in content and uses anchors to get selector
|
|
235
|
+
* const signUpAnchor = content.contentAnchors?.find(
|
|
236
|
+
* anchor => anchor.text.toLowerCase().includes('sign up')
|
|
237
|
+
* );
|
|
238
|
+
* if (signUpAnchor) {
|
|
239
|
+
* await session.click(signUpAnchor.selector);
|
|
240
|
+
* }
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
extractContent(options?: ExtractContentOptions): Promise<ExtractedContent>;
|
|
186
244
|
/**
|
|
187
245
|
* Wait for a condition
|
|
188
246
|
*/
|
|
@@ -280,6 +338,15 @@ interface UsageStats {
|
|
|
280
338
|
quotaRemaining: number;
|
|
281
339
|
quotaLimit: number;
|
|
282
340
|
}
|
|
341
|
+
interface DownloadOptions {
|
|
342
|
+
sessionId?: string;
|
|
343
|
+
}
|
|
344
|
+
interface DownloadResult {
|
|
345
|
+
success: boolean;
|
|
346
|
+
filename: string;
|
|
347
|
+
content: Buffer;
|
|
348
|
+
size: number;
|
|
349
|
+
}
|
|
283
350
|
|
|
284
351
|
interface InstaVMOptions {
|
|
285
352
|
baseURL?: string;
|
|
@@ -319,6 +386,10 @@ declare class InstaVM {
|
|
|
319
386
|
* Get usage statistics for a session
|
|
320
387
|
*/
|
|
321
388
|
getUsage(sessionId?: string): Promise<UsageStats>;
|
|
389
|
+
/**
|
|
390
|
+
* Download a file from the remote VM
|
|
391
|
+
*/
|
|
392
|
+
download(filename: string, options?: DownloadOptions): Promise<DownloadResult>;
|
|
322
393
|
/**
|
|
323
394
|
* Get the current session ID
|
|
324
395
|
*/
|
|
@@ -421,4 +492,4 @@ declare class ElementNotFoundError extends BrowserError {
|
|
|
421
492
|
constructor(message?: string, selector?: string, options?: any);
|
|
422
493
|
}
|
|
423
494
|
|
|
424
|
-
export { type ApiResponse, type AsyncExecutionResult, AuthenticationError, BrowserError, BrowserInteractionError, BrowserManager, BrowserNavigationError, BrowserSession, BrowserSessionError, type BrowserSessionInfo, type BrowserSessionOptions, BrowserTimeoutError, type ClickOptions, ElementNotFoundError, type ExecuteOptions, ExecutionError, type ExecutionResult, type ExtractOptions, type ExtractedElement, type FileUpload, type FillOptions, type HttpClientConfig, InstaVM, InstaVMError, type InstaVMOptions, type NavigateOptions, type NavigationResult, NetworkError, QuotaExceededError, RateLimitError, type RequestConfig, type RetryConfig, type ScreenshotOptions, type ScrollOptions, SessionError, type TypeOptions, type UploadOptions, type UploadResult, type UsageStats, type WaitCondition };
|
|
495
|
+
export { type ApiResponse, type AsyncExecutionResult, AuthenticationError, BrowserError, BrowserInteractionError, BrowserManager, BrowserNavigationError, BrowserSession, BrowserSessionError, type BrowserSessionInfo, type BrowserSessionOptions, BrowserTimeoutError, type ClickOptions, type ContentAnchor, type DownloadOptions, type DownloadResult, ElementNotFoundError, type ExecuteOptions, ExecutionError, type ExecutionResult, type ExtractContentOptions, type ExtractOptions, type ExtractedContent, type ExtractedElement, type FileUpload, type FillOptions, type HttpClientConfig, InstaVM, InstaVMError, type InstaVMOptions, type InteractiveElement, type NavigateOptions, type NavigationResult, NetworkError, QuotaExceededError, RateLimitError, type RequestConfig, type RetryConfig, type ScreenshotOptions, type ScrollOptions, SessionError, type TypeOptions, type UploadOptions, type UploadResult, type UsageStats, type WaitCondition };
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
-
};
|
|
12
9
|
var __export = (target, all) => {
|
|
13
10
|
for (var name in all)
|
|
14
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -31,92 +28,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
28
|
));
|
|
32
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
30
|
|
|
34
|
-
// package.json
|
|
35
|
-
var require_package = __commonJS({
|
|
36
|
-
"package.json"(exports2, module2) {
|
|
37
|
-
module2.exports = {
|
|
38
|
-
name: "instavm",
|
|
39
|
-
version: "0.2.2",
|
|
40
|
-
description: "Official JavaScript SDK for InstaVM API",
|
|
41
|
-
main: "dist/index.js",
|
|
42
|
-
module: "dist/index.mjs",
|
|
43
|
-
types: "dist/index.d.ts",
|
|
44
|
-
exports: {
|
|
45
|
-
".": {
|
|
46
|
-
types: "./dist/index.d.ts",
|
|
47
|
-
import: "./dist/index.mjs",
|
|
48
|
-
require: "./dist/index.js"
|
|
49
|
-
},
|
|
50
|
-
"./integrations/openai": {
|
|
51
|
-
types: "./dist/integrations/openai.d.ts",
|
|
52
|
-
import: "./dist/integrations/openai.mjs",
|
|
53
|
-
require: "./dist/integrations/openai.js"
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
files: [
|
|
57
|
-
"dist"
|
|
58
|
-
],
|
|
59
|
-
scripts: {
|
|
60
|
-
dev: "tsup --watch",
|
|
61
|
-
build: "tsup",
|
|
62
|
-
"type-check": "tsc --noEmit",
|
|
63
|
-
test: "vitest",
|
|
64
|
-
"test:unit": "vitest run tests/unit",
|
|
65
|
-
"test:integration": "vitest run tests/integration",
|
|
66
|
-
"test:watch": "vitest --watch",
|
|
67
|
-
"test:coverage": "vitest --coverage",
|
|
68
|
-
lint: "eslint src --ext .ts,.tsx",
|
|
69
|
-
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
70
|
-
format: "prettier --write src/**/*.ts",
|
|
71
|
-
prepublishOnly: "npm run build && npm run test:unit",
|
|
72
|
-
clean: "rm -rf dist"
|
|
73
|
-
},
|
|
74
|
-
keywords: [
|
|
75
|
-
"instavm",
|
|
76
|
-
"api",
|
|
77
|
-
"sdk",
|
|
78
|
-
"browser-automation",
|
|
79
|
-
"code-execution",
|
|
80
|
-
"microvm",
|
|
81
|
-
"cloud"
|
|
82
|
-
],
|
|
83
|
-
author: "InstaVM",
|
|
84
|
-
license: "UNLICENSED",
|
|
85
|
-
repository: {
|
|
86
|
-
type: "git",
|
|
87
|
-
url: "https://github.com/instavm/js.git"
|
|
88
|
-
},
|
|
89
|
-
bugs: {
|
|
90
|
-
url: "https://github.com/instavm/js/issues"
|
|
91
|
-
},
|
|
92
|
-
homepage: "https://github.com/instavm/js#readme",
|
|
93
|
-
engines: {
|
|
94
|
-
node: ">=16.0.0"
|
|
95
|
-
},
|
|
96
|
-
dependencies: {
|
|
97
|
-
axios: "^1.7.9",
|
|
98
|
-
eventemitter3: "^5.0.1",
|
|
99
|
-
"form-data": "^4.0.1",
|
|
100
|
-
ws: "^8.18.0"
|
|
101
|
-
},
|
|
102
|
-
devDependencies: {
|
|
103
|
-
"@types/node": "^22.10.5",
|
|
104
|
-
"@types/ws": "^8.5.13",
|
|
105
|
-
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
|
106
|
-
"@typescript-eslint/parser": "^8.18.2",
|
|
107
|
-
"@vitest/coverage-v8": "^2.1.8",
|
|
108
|
-
eslint: "^9.18.0",
|
|
109
|
-
"eslint-config-prettier": "^9.1.0",
|
|
110
|
-
"eslint-plugin-prettier": "^5.2.1",
|
|
111
|
-
prettier: "^3.4.2",
|
|
112
|
-
tsup: "^8.3.5",
|
|
113
|
-
typescript: "^5.7.2",
|
|
114
|
-
vitest: "^2.1.8"
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
31
|
// src/index.ts
|
|
121
32
|
var index_exports = {};
|
|
122
33
|
__export(index_exports, {
|
|
@@ -262,14 +173,6 @@ async function withRetry(fn, options) {
|
|
|
262
173
|
}
|
|
263
174
|
|
|
264
175
|
// src/client/HTTPClient.ts
|
|
265
|
-
function getPackageVersion() {
|
|
266
|
-
try {
|
|
267
|
-
const packageJson = require_package();
|
|
268
|
-
return packageJson.version || "0.1.0";
|
|
269
|
-
} catch {
|
|
270
|
-
return "0.1.0";
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
176
|
var HTTPClient = class {
|
|
274
177
|
get apiKey() {
|
|
275
178
|
return this.config.apiKey;
|
|
@@ -281,7 +184,7 @@ var HTTPClient = class {
|
|
|
281
184
|
timeout: config.timeout,
|
|
282
185
|
headers: {
|
|
283
186
|
"Content-Type": "application/json",
|
|
284
|
-
"User-Agent":
|
|
187
|
+
"User-Agent": "instavm-js-sdk/0.1.0"
|
|
285
188
|
}
|
|
286
189
|
});
|
|
287
190
|
this.setupInterceptors();
|
|
@@ -289,7 +192,7 @@ var HTTPClient = class {
|
|
|
289
192
|
setupInterceptors() {
|
|
290
193
|
this.client.interceptors.request.use(
|
|
291
194
|
(config) => {
|
|
292
|
-
if (
|
|
195
|
+
if (config.url?.includes("/browser/")) {
|
|
293
196
|
config.headers["X-API-Key"] = this.config.apiKey;
|
|
294
197
|
}
|
|
295
198
|
return config;
|
|
@@ -378,14 +281,18 @@ var HTTPClient = class {
|
|
|
378
281
|
});
|
|
379
282
|
}
|
|
380
283
|
/**
|
|
381
|
-
* POST request for code execution (X-API-Key header
|
|
284
|
+
* POST request for code execution (uses X-API-Key header like Python client)
|
|
382
285
|
*/
|
|
383
286
|
async postExecution(url, data, headers) {
|
|
287
|
+
const requestHeaders = {
|
|
288
|
+
"X-API-Key": this.config.apiKey,
|
|
289
|
+
...headers
|
|
290
|
+
};
|
|
384
291
|
return this.request({
|
|
385
292
|
method: "POST",
|
|
386
293
|
url,
|
|
387
294
|
data,
|
|
388
|
-
headers
|
|
295
|
+
headers: requestHeaders
|
|
389
296
|
});
|
|
390
297
|
}
|
|
391
298
|
/**
|
|
@@ -424,6 +331,31 @@ var HTTPClient = class {
|
|
|
424
331
|
headers
|
|
425
332
|
});
|
|
426
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* POST request that returns raw binary data (for file downloads)
|
|
336
|
+
*/
|
|
337
|
+
async postRaw(url, data, headers) {
|
|
338
|
+
const requestHeaders = {
|
|
339
|
+
"X-API-Key": this.config.apiKey,
|
|
340
|
+
...headers
|
|
341
|
+
};
|
|
342
|
+
const axiosConfig = {
|
|
343
|
+
method: "POST",
|
|
344
|
+
url,
|
|
345
|
+
data,
|
|
346
|
+
headers: requestHeaders,
|
|
347
|
+
responseType: "arraybuffer",
|
|
348
|
+
timeout: this.config.timeout
|
|
349
|
+
};
|
|
350
|
+
const makeRequest = async () => {
|
|
351
|
+
const response = await this.client.request(axiosConfig);
|
|
352
|
+
return response.data;
|
|
353
|
+
};
|
|
354
|
+
return withRetry(makeRequest, {
|
|
355
|
+
retries: this.config.maxRetries,
|
|
356
|
+
retryDelay: this.config.retryDelay
|
|
357
|
+
});
|
|
358
|
+
}
|
|
427
359
|
};
|
|
428
360
|
|
|
429
361
|
// src/client/BrowserSession.ts
|
|
@@ -650,6 +582,59 @@ var BrowserSession = class extends import_eventemitter3.EventEmitter {
|
|
|
650
582
|
);
|
|
651
583
|
}
|
|
652
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* Extract LLM-friendly content from the current page
|
|
587
|
+
*
|
|
588
|
+
* Returns clean article content, interactive elements, and content anchors
|
|
589
|
+
* for intelligent browser automation with LLMs.
|
|
590
|
+
*
|
|
591
|
+
* @param options - Content extraction options
|
|
592
|
+
* @returns Extracted content with readable text, interactive elements, and content anchors
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```typescript
|
|
596
|
+
* const content = await session.extractContent();
|
|
597
|
+
*
|
|
598
|
+
* // LLM reads clean content
|
|
599
|
+
* const article = content.readableContent.content;
|
|
600
|
+
*
|
|
601
|
+
* // LLM finds "Sign Up" in content and uses anchors to get selector
|
|
602
|
+
* const signUpAnchor = content.contentAnchors?.find(
|
|
603
|
+
* anchor => anchor.text.toLowerCase().includes('sign up')
|
|
604
|
+
* );
|
|
605
|
+
* if (signUpAnchor) {
|
|
606
|
+
* await session.click(signUpAnchor.selector);
|
|
607
|
+
* }
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
async extractContent(options = {}) {
|
|
611
|
+
this.ensureActive();
|
|
612
|
+
const requestData = {
|
|
613
|
+
session_id: this.sessionId,
|
|
614
|
+
include_interactive: options.includeInteractive !== false,
|
|
615
|
+
include_anchors: options.includeAnchors !== false,
|
|
616
|
+
max_anchors: options.maxAnchors ?? 50
|
|
617
|
+
};
|
|
618
|
+
try {
|
|
619
|
+
const response = await this.httpClient.post(
|
|
620
|
+
"/v1/browser/interactions/content",
|
|
621
|
+
requestData
|
|
622
|
+
);
|
|
623
|
+
return {
|
|
624
|
+
readableContent: {
|
|
625
|
+
...response.readable_content || {},
|
|
626
|
+
content: response.readable_content?.content || ""
|
|
627
|
+
},
|
|
628
|
+
interactiveElements: response.interactive_elements,
|
|
629
|
+
contentAnchors: response.content_anchors
|
|
630
|
+
};
|
|
631
|
+
} catch (error) {
|
|
632
|
+
throw new BrowserInteractionError(
|
|
633
|
+
`Content extraction failed: ${getErrorMessage(error)}`,
|
|
634
|
+
{ cause: error }
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
653
638
|
/**
|
|
654
639
|
* Wait for a condition
|
|
655
640
|
*/
|
|
@@ -1041,6 +1026,34 @@ var InstaVM = class {
|
|
|
1041
1026
|
);
|
|
1042
1027
|
}
|
|
1043
1028
|
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Download a file from the remote VM
|
|
1031
|
+
*/
|
|
1032
|
+
async download(filename, options = {}) {
|
|
1033
|
+
const targetSessionId = options.sessionId || this._sessionId;
|
|
1034
|
+
if (!targetSessionId) {
|
|
1035
|
+
throw new SessionError("No active session to download from");
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
const response = await this.httpClient.postRaw("/download", {
|
|
1039
|
+
filename,
|
|
1040
|
+
session_id: targetSessionId
|
|
1041
|
+
});
|
|
1042
|
+
const content = Buffer.from(response);
|
|
1043
|
+
return {
|
|
1044
|
+
success: true,
|
|
1045
|
+
filename,
|
|
1046
|
+
content,
|
|
1047
|
+
size: content.length
|
|
1048
|
+
};
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1051
|
+
throw new SessionError(
|
|
1052
|
+
`File download failed: ${errorMessage}`,
|
|
1053
|
+
{ cause: error }
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1044
1057
|
/**
|
|
1045
1058
|
* Get the current session ID
|
|
1046
1059
|
*/
|