instavm 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,7 +21,7 @@ npm install instavm
21
21
 
22
22
  ## Quick Start
23
23
 
24
- ### Code Execution
24
+ ### Code Execution (Cloud Mode)
25
25
 
26
26
  ```typescript
27
27
  import { InstaVM, ExecutionError, NetworkError } from 'instavm';
@@ -30,7 +30,7 @@ import { InstaVM, ExecutionError, NetworkError } from 'instavm';
30
30
  const client = new InstaVM('your_api_key');
31
31
 
32
32
  try {
33
- // Execute a command
33
+ // Execute a command
34
34
  const result = await client.execute("print(100**100)");
35
35
  console.log(result);
36
36
 
@@ -49,6 +49,44 @@ try {
49
49
  }
50
50
  ```
51
51
 
52
+ ### Local Execution Mode
53
+
54
+ Run code execution against a local container (e.g., [coderunner](https://github.com/instavm/coderunner)) instead of the cloud API:
55
+
56
+ ```typescript
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.output);
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
+ ```
78
+
79
+ **Note:** Local mode supports:
80
+ - ✅ Code execution (`execute()`)
81
+ - ✅ Browser navigation (`browser.navigate()`)
82
+ - ✅ Content extraction (`browser.extractContent()`)
83
+
84
+ Local mode does NOT support (cloud-only features):
85
+ - ❌ Session management (`createSession()`, `closeSession()`, `getUsage()`)
86
+ - ❌ File upload/download
87
+ - ❌ Async execution
88
+ - ❌ Browser session creation and complex interactions
89
+
52
90
  ### File Upload
53
91
 
54
92
  ```typescript
@@ -494,19 +532,20 @@ if (signupLink) {
494
532
 
495
533
  ```typescript
496
534
  import {
497
- InstaVMError, // Base error class
498
- AuthenticationError, // API key issues
499
- RateLimitError, // Rate limiting (has retryAfter property)
500
- QuotaExceededError, // Usage quota exceeded
501
- NetworkError, // Connection issues
502
- ExecutionError, // Code execution failures
503
- SessionError, // Session management issues
504
- BrowserError, // General browser errors
505
- BrowserSessionError, // Browser session issues
506
- BrowserInteractionError, // Browser interaction failures
507
- BrowserTimeoutError, // Browser operation timeouts
508
- BrowserNavigationError, // Navigation failures
509
- ElementNotFoundError // Element selection issues (has selector property)
535
+ InstaVMError, // Base error class
536
+ AuthenticationError, // API key issues
537
+ RateLimitError, // Rate limiting (has retryAfter property)
538
+ QuotaExceededError, // Usage quota exceeded
539
+ NetworkError, // Connection issues
540
+ ExecutionError, // Code execution failures
541
+ SessionError, // Session management issues
542
+ BrowserError, // General browser errors
543
+ BrowserSessionError, // Browser session issues
544
+ BrowserInteractionError, // Browser interaction failures
545
+ BrowserTimeoutError, // Browser operation timeouts
546
+ BrowserNavigationError, // Navigation failures
547
+ ElementNotFoundError, // Element selection issues (has selector property)
548
+ UnsupportedOperationError // Operation not supported in local mode
510
549
  } from 'instavm';
511
550
 
512
551
  // Specific error handling
@@ -573,10 +612,12 @@ class InstaVM {
573
612
 
574
613
  ```typescript
575
614
  interface InstaVMOptions {
576
- baseURL?: string; // Default: 'https://api.instavm.io'
615
+ baseURL?: string; // Default: 'https://api.instavm.io' (ignored if local=true)
577
616
  timeout?: number; // Default: 300000 (5 minutes)
578
617
  maxRetries?: number; // Default: 3
579
618
  retryDelay?: number; // Default: 1000ms
619
+ local?: boolean; // Default: false - Use local container instead of cloud
620
+ localURL?: string; // Default: 'http://coderunner.local:8222' - Local container URL
580
621
  }
581
622
 
582
623
  interface ExecuteOptions {
@@ -845,6 +886,14 @@ All rights reserved. No redistribution or modification permitted.
845
886
 
846
887
  ## Changelog
847
888
 
889
+ ### Version 0.4.0
890
+
891
+ - ✅ **NEW**: Local execution mode support - Run code execution against local containers
892
+ - ✅ **NEW**: Local browser automation - Navigate and extract content without sessions
893
+ - ✅ **NEW**: `UnsupportedOperationError` - Better error messages for cloud-only features
894
+ - ✅ Parity with Python SDK v0.4.0 local mode features
895
+ - ✅ Improved flexibility for on-premise deployments
896
+
848
897
  ### Version 0.3.0
849
898
 
850
899
  - ✅ **NEW**: File download functionality - Download files from remote VM
package/dist/index.d.mts CHANGED
@@ -151,6 +151,7 @@ interface ExtractOptions {
151
151
  maxResults?: number;
152
152
  }
153
153
  interface ExtractContentOptions {
154
+ url?: string;
154
155
  includeInteractive?: boolean;
155
156
  includeAnchors?: boolean;
156
157
  maxAnchors?: number;
@@ -265,7 +266,8 @@ declare class BrowserSession extends EventEmitter {
265
266
  declare class BrowserManager {
266
267
  private httpClient;
267
268
  private activeSessions;
268
- constructor(httpClient: HTTPClient);
269
+ private local;
270
+ constructor(httpClient: HTTPClient, local?: boolean);
269
271
  /**
270
272
  * Create a new browser session
271
273
  */
@@ -290,6 +292,14 @@ declare class BrowserManager {
290
292
  * Close all active sessions
291
293
  */
292
294
  closeAllSessions(): Promise<void>;
295
+ /**
296
+ * Navigate to a URL (local mode support - no session required)
297
+ */
298
+ navigate(url: string, options?: NavigateOptions): Promise<NavigationResult>;
299
+ /**
300
+ * Extract LLM-friendly content (local mode support - no session required)
301
+ */
302
+ extractContent(options?: ExtractContentOptions): Promise<ExtractedContent>;
293
303
  /**
294
304
  * Clean up resources
295
305
  */
@@ -353,6 +363,8 @@ interface InstaVMOptions {
353
363
  timeout?: number;
354
364
  maxRetries?: number;
355
365
  retryDelay?: number;
366
+ local?: boolean;
367
+ localURL?: string;
356
368
  }
357
369
  /**
358
370
  * Main InstaVM client class
@@ -361,7 +373,12 @@ declare class InstaVM {
361
373
  private httpClient;
362
374
  private _sessionId;
363
375
  readonly browser: BrowserManager;
376
+ readonly local: boolean;
364
377
  constructor(apiKey: string, options?: InstaVMOptions);
378
+ /**
379
+ * Ensure operation is not called in local mode
380
+ */
381
+ private ensureNotLocal;
365
382
  /**
366
383
  * Execute code synchronously
367
384
  */
@@ -491,5 +508,11 @@ declare class ElementNotFoundError extends BrowserError {
491
508
  readonly selector?: string;
492
509
  constructor(message?: string, selector?: string, options?: any);
493
510
  }
511
+ /**
512
+ * Unsupported operation error (e.g., operations not available in local mode)
513
+ */
514
+ declare class UnsupportedOperationError extends InstaVMError {
515
+ constructor(message?: string, options?: any);
516
+ }
494
517
 
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 };
518
+ 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, UnsupportedOperationError, type UploadOptions, type UploadResult, type UsageStats, type WaitCondition };
package/dist/index.d.ts CHANGED
@@ -151,6 +151,7 @@ interface ExtractOptions {
151
151
  maxResults?: number;
152
152
  }
153
153
  interface ExtractContentOptions {
154
+ url?: string;
154
155
  includeInteractive?: boolean;
155
156
  includeAnchors?: boolean;
156
157
  maxAnchors?: number;
@@ -265,7 +266,8 @@ declare class BrowserSession extends EventEmitter {
265
266
  declare class BrowserManager {
266
267
  private httpClient;
267
268
  private activeSessions;
268
- constructor(httpClient: HTTPClient);
269
+ private local;
270
+ constructor(httpClient: HTTPClient, local?: boolean);
269
271
  /**
270
272
  * Create a new browser session
271
273
  */
@@ -290,6 +292,14 @@ declare class BrowserManager {
290
292
  * Close all active sessions
291
293
  */
292
294
  closeAllSessions(): Promise<void>;
295
+ /**
296
+ * Navigate to a URL (local mode support - no session required)
297
+ */
298
+ navigate(url: string, options?: NavigateOptions): Promise<NavigationResult>;
299
+ /**
300
+ * Extract LLM-friendly content (local mode support - no session required)
301
+ */
302
+ extractContent(options?: ExtractContentOptions): Promise<ExtractedContent>;
293
303
  /**
294
304
  * Clean up resources
295
305
  */
@@ -353,6 +363,8 @@ interface InstaVMOptions {
353
363
  timeout?: number;
354
364
  maxRetries?: number;
355
365
  retryDelay?: number;
366
+ local?: boolean;
367
+ localURL?: string;
356
368
  }
357
369
  /**
358
370
  * Main InstaVM client class
@@ -361,7 +373,12 @@ declare class InstaVM {
361
373
  private httpClient;
362
374
  private _sessionId;
363
375
  readonly browser: BrowserManager;
376
+ readonly local: boolean;
364
377
  constructor(apiKey: string, options?: InstaVMOptions);
378
+ /**
379
+ * Ensure operation is not called in local mode
380
+ */
381
+ private ensureNotLocal;
365
382
  /**
366
383
  * Execute code synchronously
367
384
  */
@@ -491,5 +508,11 @@ declare class ElementNotFoundError extends BrowserError {
491
508
  readonly selector?: string;
492
509
  constructor(message?: string, selector?: string, options?: any);
493
510
  }
511
+ /**
512
+ * Unsupported operation error (e.g., operations not available in local mode)
513
+ */
514
+ declare class UnsupportedOperationError extends InstaVMError {
515
+ constructor(message?: string, options?: any);
516
+ }
494
517
 
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 };
518
+ 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, UnsupportedOperationError, type UploadOptions, type UploadResult, type UsageStats, type WaitCondition };
package/dist/index.js CHANGED
@@ -46,7 +46,8 @@ __export(index_exports, {
46
46
  NetworkError: () => NetworkError,
47
47
  QuotaExceededError: () => QuotaExceededError,
48
48
  RateLimitError: () => RateLimitError,
49
- SessionError: () => SessionError
49
+ SessionError: () => SessionError,
50
+ UnsupportedOperationError: () => UnsupportedOperationError
50
51
  });
51
52
  module.exports = __toCommonJS(index_exports);
52
53
 
@@ -131,6 +132,11 @@ var ElementNotFoundError = class extends BrowserError {
131
132
  this.selector = selector;
132
133
  }
133
134
  };
135
+ var UnsupportedOperationError = class extends InstaVMError {
136
+ constructor(message = "Operation not supported", options) {
137
+ super(message, options);
138
+ }
139
+ };
134
140
 
135
141
  // src/utils/retry.ts
136
142
  function defaultRetryCondition(error) {
@@ -706,14 +712,20 @@ var BrowserSession = class extends import_eventemitter3.EventEmitter {
706
712
 
707
713
  // src/client/BrowserManager.ts
708
714
  var BrowserManager = class {
709
- constructor(httpClient) {
715
+ constructor(httpClient, local = false) {
710
716
  this.activeSessions = /* @__PURE__ */ new Map();
711
717
  this.httpClient = httpClient;
718
+ this.local = local;
712
719
  }
713
720
  /**
714
721
  * Create a new browser session
715
722
  */
716
723
  async createSession(options = {}) {
724
+ if (this.local) {
725
+ throw new UnsupportedOperationError(
726
+ "Browser session management is not supported in local mode. Use navigate() or extractContent() with URL directly."
727
+ );
728
+ }
717
729
  const requestData = {
718
730
  viewport_width: options.viewportWidth || 1920,
719
731
  viewport_height: options.viewportHeight || 1080,
@@ -812,6 +824,77 @@ var BrowserManager = class {
812
824
  );
813
825
  this.activeSessions.clear();
814
826
  }
827
+ /**
828
+ * Navigate to a URL (local mode support - no session required)
829
+ */
830
+ async navigate(url, options = {}) {
831
+ if (!this.local) {
832
+ throw new UnsupportedOperationError(
833
+ "navigate() without session is only supported in local mode. In cloud mode, create a session first."
834
+ );
835
+ }
836
+ const requestData = {
837
+ url,
838
+ wait_timeout: options.waitTimeout || 3e4
839
+ };
840
+ try {
841
+ const response = await this.httpClient.post(
842
+ "/v1/browser/interactions/navigate",
843
+ requestData
844
+ );
845
+ return {
846
+ success: response.success !== false,
847
+ url: response.url || url,
848
+ title: response.title,
849
+ status: response.status
850
+ };
851
+ } catch (error) {
852
+ const errorMessage = error instanceof Error ? error.message : String(error);
853
+ throw new BrowserNavigationError(
854
+ `Navigation failed: ${errorMessage}`,
855
+ { cause: error }
856
+ );
857
+ }
858
+ }
859
+ /**
860
+ * Extract LLM-friendly content (local mode support - no session required)
861
+ */
862
+ async extractContent(options = {}) {
863
+ if (!this.local) {
864
+ throw new UnsupportedOperationError(
865
+ "extractContent() without session is only supported in local mode. In cloud mode, create a session first."
866
+ );
867
+ }
868
+ if (!options.url) {
869
+ throw new BrowserInteractionError("url is required in local mode");
870
+ }
871
+ const requestData = {
872
+ url: options.url,
873
+ include_interactive: options.includeInteractive !== false,
874
+ include_anchors: options.includeAnchors !== false,
875
+ max_anchors: options.maxAnchors ?? 50
876
+ };
877
+ try {
878
+ const response = await this.httpClient.post(
879
+ "/v1/browser/interactions/content",
880
+ requestData
881
+ );
882
+ return {
883
+ readableContent: {
884
+ ...response.readable_content || {},
885
+ content: response.readable_content?.content || ""
886
+ },
887
+ interactiveElements: response.interactive_elements,
888
+ contentAnchors: response.content_anchors
889
+ };
890
+ } catch (error) {
891
+ const errorMessage = error instanceof Error ? error.message : String(error);
892
+ throw new BrowserInteractionError(
893
+ `Content extraction failed: ${errorMessage}`,
894
+ { cause: error }
895
+ );
896
+ }
897
+ }
815
898
  /**
816
899
  * Clean up resources
817
900
  */
@@ -825,34 +908,47 @@ var import_form_data = __toESM(require("form-data"));
825
908
  var InstaVM = class {
826
909
  constructor(apiKey, options = {}) {
827
910
  this._sessionId = null;
828
- if (!apiKey) {
829
- throw new Error("API key is required");
911
+ this.local = options.local || false;
912
+ if (!this.local && !apiKey) {
913
+ throw new AuthenticationError("API key is required for cloud mode");
830
914
  }
831
915
  const config = {
832
- baseURL: options.baseURL || "https://api.instavm.io",
916
+ baseURL: this.local ? options.localURL || "http://coderunner.local:8222" : options.baseURL || "https://api.instavm.io",
833
917
  timeout: options.timeout || 3e5,
834
918
  // 5 minutes
835
919
  maxRetries: options.maxRetries || 3,
836
920
  retryDelay: options.retryDelay || 1e3,
837
- apiKey
921
+ apiKey: apiKey || ""
838
922
  };
839
923
  this.httpClient = new HTTPClient(config);
840
- this.browser = new BrowserManager(this.httpClient);
924
+ this.browser = new BrowserManager(this.httpClient, this.local);
925
+ }
926
+ /**
927
+ * Ensure operation is not called in local mode
928
+ */
929
+ ensureNotLocal(operationName) {
930
+ if (this.local) {
931
+ throw new UnsupportedOperationError(
932
+ `${operationName} is not supported in local mode. This operation is only available when using the cloud API.`
933
+ );
934
+ }
841
935
  }
842
936
  /**
843
937
  * Execute code synchronously
844
938
  */
845
939
  async execute(command, options = {}) {
846
940
  let sessionId = options.sessionId || this._sessionId;
847
- if (!sessionId) {
941
+ if (!this.local && !sessionId) {
848
942
  sessionId = await this.createSession();
849
943
  }
850
944
  const requestData = {
851
945
  command,
852
946
  language: options.language || "python",
853
- timeout: options.timeout || 15,
854
- session_id: sessionId
947
+ timeout: options.timeout || 15
855
948
  };
949
+ if (!this.local && sessionId) {
950
+ requestData.session_id = sessionId;
951
+ }
856
952
  try {
857
953
  const response = await this.httpClient.postExecution(
858
954
  "/execute",
@@ -882,6 +978,7 @@ var InstaVM = class {
882
978
  * Execute code asynchronously
883
979
  */
884
980
  async executeAsync(command, options = {}) {
981
+ this.ensureNotLocal("Async execution");
885
982
  let sessionId = options.sessionId || this._sessionId;
886
983
  if (!sessionId) {
887
984
  sessionId = await this.createSession();
@@ -923,6 +1020,7 @@ var InstaVM = class {
923
1020
  * Upload files to the execution environment
924
1021
  */
925
1022
  async upload(files, options = {}) {
1023
+ this.ensureNotLocal("File upload");
926
1024
  const formData = new import_form_data.default();
927
1025
  for (const file of files) {
928
1026
  if (Buffer.isBuffer(file.content)) {
@@ -965,6 +1063,7 @@ var InstaVM = class {
965
1063
  * Create a new execution session
966
1064
  */
967
1065
  async createSession() {
1066
+ this.ensureNotLocal("Session management");
968
1067
  try {
969
1068
  const response = await this.httpClient.post("/v1/sessions/session", {
970
1069
  api_key: this.httpClient.apiKey
@@ -1004,6 +1103,7 @@ var InstaVM = class {
1004
1103
  * Get usage statistics for a session
1005
1104
  */
1006
1105
  async getUsage(sessionId) {
1106
+ this.ensureNotLocal("Usage tracking");
1007
1107
  const targetSessionId = sessionId || this._sessionId;
1008
1108
  if (!targetSessionId) {
1009
1109
  throw new SessionError("No active session to get usage for");
@@ -1030,6 +1130,7 @@ var InstaVM = class {
1030
1130
  * Download a file from the remote VM
1031
1131
  */
1032
1132
  async download(filename, options = {}) {
1133
+ this.ensureNotLocal("File download");
1033
1134
  const targetSessionId = options.sessionId || this._sessionId;
1034
1135
  if (!targetSessionId) {
1035
1136
  throw new SessionError("No active session to download from");
@@ -1087,6 +1188,7 @@ var InstaVM = class {
1087
1188
  NetworkError,
1088
1189
  QuotaExceededError,
1089
1190
  RateLimitError,
1090
- SessionError
1191
+ SessionError,
1192
+ UnsupportedOperationError
1091
1193
  });
1092
1194
  //# sourceMappingURL=index.js.map