instavm 0.8.1 → 0.8.3

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
@@ -64,7 +64,7 @@ const client = new InstaVM('', {
64
64
 
65
65
  // Execute code locally without session management
66
66
  const result = await client.execute("print('Hello from local container!')");
67
- console.log(result.output);
67
+ console.log(result.stdout);
68
68
 
69
69
  // Browser automation in local mode (no session required)
70
70
  const content = await client.browser.extractContent({
@@ -94,6 +94,9 @@ import { InstaVM } from 'instavm';
94
94
 
95
95
  const client = new InstaVM('your_api_key');
96
96
 
97
+ // Create a session first (required for upload)
98
+ await client.createSession();
99
+
97
100
  // Upload files to the execution environment
98
101
  const files = [
99
102
  {
@@ -110,7 +113,7 @@ console.log(result);
110
113
  const execution = await client.execute('python /remote/path/script.py', {
111
114
  language: 'bash'
112
115
  });
113
- console.log(execution.output);
116
+ console.log(execution.stdout);
114
117
  ```
115
118
 
116
119
  ### File Download
@@ -170,13 +173,18 @@ import { InstaVM } from 'instavm';
170
173
 
171
174
  const client = new InstaVM('your_api_key');
172
175
 
173
- // Execute command asynchronously (returns task info)
176
+ // Execute command asynchronously (returns task ID)
174
177
  const result = await client.executeAsync("sleep 5 && echo 'Long task complete!'", {
175
178
  language: 'bash'
176
179
  });
177
-
178
- console.log(`Task ${result.taskId} status: ${result.status}`);
179
- console.log(`Output so far: ${result.output}`);
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}`);
180
188
  ```
181
189
 
182
190
  ## Browser Automation
@@ -319,7 +327,7 @@ data = {"terms": search_terms, "timestamp": "2024-01-01"}
319
327
  print(json.dumps(data))
320
328
  `);
321
329
 
322
- const searchData = JSON.parse(dataPrep.output.trim());
330
+ const searchData = JSON.parse(dataPrep.stdout.trim());
323
331
 
324
332
  // Use browser to search and collect results
325
333
  const session = await client.browser.createSession();
@@ -359,7 +367,7 @@ top_words = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)[:10]
359
367
  print("Top words:", top_words)
360
368
  `);
361
369
 
362
- console.log(analysis.output);
370
+ console.log(analysis.stdout);
363
371
  await client.dispose();
364
372
  ```
365
373
 
@@ -385,7 +393,7 @@ print(f"Std: {data['numbers'].std():.2f}")
385
393
  print(f"Categories: {data['categories'].value_counts().to_dict()}")
386
394
  `);
387
395
 
388
- console.log(result.output);
396
+ console.log(result.stdout);
389
397
  ```
390
398
 
391
399
  ### Bash Commands
@@ -404,7 +412,7 @@ echo "Memory Info:"
404
412
  free -h
405
413
  `, { language: 'bash' });
406
414
 
407
- console.log(sysInfo.output);
415
+ console.log(sysInfo.stdout);
408
416
  ```
409
417
 
410
418
  ### Session Persistence
@@ -415,7 +423,19 @@ await client.execute('data = [1, 2, 3, 4, 5]');
415
423
  await client.execute('total = sum(data)');
416
424
 
417
425
  const result = await client.execute('print(f"Total: {total}, Average: {total/len(data)}")');
418
- console.log(result.output); // Output: Total: 15, Average: 3.0
426
+ console.log(result.stdout); // Output: Total: 15, Average: 3.0
427
+ ```
428
+
429
+ ### Session Status Check
430
+
431
+ ```typescript
432
+ // Check if current session is still active
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}`);
419
439
  ```
420
440
 
421
441
  ## Advanced Features
@@ -583,11 +603,12 @@ const client = new InstaVM('your_api_key', {
583
603
  ```typescript
584
604
  class InstaVM {
585
605
  constructor(apiKey: string, options?: InstaVMOptions)
586
-
606
+
587
607
  // Code execution
588
608
  execute(command: string, options?: ExecuteOptions): Promise<ExecutionResult>
589
609
  executeAsync(command: string, options?: ExecuteOptions): Promise<AsyncExecutionResult>
590
-
610
+ getTaskResult(taskId: string, pollInterval?: number, timeout?: number): Promise<TaskResult>
611
+
591
612
  // File operations
592
613
  upload(files: FileUpload[], options?: UploadOptions): Promise<UploadResult>
593
614
  download(filename: string, options?: DownloadOptions): Promise<DownloadResult>
@@ -595,14 +616,15 @@ class InstaVM {
595
616
  // Session management
596
617
  createSession(): Promise<string>
597
618
  closeSession(sessionId?: string): Promise<void>
619
+ isSessionActive(sessionId?: string): Promise<boolean>
598
620
  getUsage(sessionId?: string): Promise<UsageStats>
599
-
621
+
600
622
  // Browser automation
601
623
  browser: BrowserManager
602
-
624
+
603
625
  // Properties
604
626
  readonly sessionId: string | null
605
-
627
+
606
628
  // Cleanup
607
629
  dispose(): Promise<void>
608
630
  }
@@ -681,13 +703,24 @@ class BrowserSession extends EventEmitter {
681
703
 
682
704
  ```typescript
683
705
  interface ExecutionResult {
684
- output: string;
706
+ stdout: string;
707
+ stderr: string;
685
708
  success: boolean;
686
- executionTime: number;
709
+ executionTime?: number;
710
+ cpuTime?: number;
711
+ createdAt?: string;
687
712
  sessionId?: string;
688
713
  error?: string;
689
714
  }
690
715
 
716
+ interface TaskResult {
717
+ stdout: string;
718
+ stderr: string;
719
+ executionTime?: number;
720
+ cpuTime?: number;
721
+ createdAt?: string;
722
+ }
723
+
691
724
  interface NavigationResult {
692
725
  success: boolean;
693
726
  url: string;
@@ -734,7 +767,7 @@ async function withInstaVM(apiKey: string, callback: (client: InstaVM) => Promis
734
767
 
735
768
  await withInstaVM('your_api_key', async (client) => {
736
769
  const result = await client.execute('print("Hello, World!")');
737
- console.log(result.output);
770
+ console.log(result.stdout);
738
771
  });
739
772
  ```
740
773
 
@@ -828,7 +861,7 @@ async function main() {
828
861
 
829
862
  try {
830
863
  const result = await client.execute('print("Hello from CommonJS!")');
831
- console.log(result.output);
864
+ console.log(result.stdout);
832
865
  } catch (error) {
833
866
  if (error instanceof AuthenticationError) {
834
867
  console.error('Authentication failed');
@@ -886,6 +919,17 @@ All rights reserved. No redistribution or modification permitted.
886
919
 
887
920
  ## Changelog
888
921
 
922
+ ### Version 0.8.3
923
+
924
+ - ✅ **NEW**: `getTaskResult()` method - Poll for async task completion with configurable intervals
925
+ - ✅ **NEW**: `isSessionActive()` method - Check if session is still active by querying VM status
926
+ - ✅ **IMPROVED**: Execution result format - Now returns `stdout` and `stderr` separately (matching Python SDK)
927
+ - ✅ **IMPROVED**: Enhanced execution results - Added `cpuTime`, `executionTime`, and `createdAt` fields
928
+ - ✅ **IMPROVED**: Better error messages - Rate limit errors now show `detail` field from API response
929
+ - ✅ **FIXED**: Session status check - Now uses `/v1/sessions/status/` endpoint for accurate VM status
930
+ - ✅ **UPDATED**: closeSession behavior - Sessions auto-expire on server, no longer makes DELETE API call
931
+ - ✅ Full parity with Python SDK v0.8.3
932
+
889
933
  ### Version 0.4.0
890
934
 
891
935
  - ✅ **NEW**: Local execution mode support - Run code execution against local containers
package/dist/index.d.mts CHANGED
@@ -316,9 +316,12 @@ interface ExecuteOptions {
316
316
  sessionId?: string;
317
317
  }
318
318
  interface ExecutionResult {
319
- output: string;
319
+ stdout: string;
320
+ stderr: string;
320
321
  success: boolean;
321
- executionTime: number;
322
+ executionTime?: number;
323
+ cpuTime?: number;
324
+ createdAt?: string;
322
325
  sessionId?: string;
323
326
  error?: string;
324
327
  }
@@ -331,6 +334,13 @@ interface AsyncExecutionResult {
331
334
  sessionId?: string;
332
335
  error?: string;
333
336
  }
337
+ interface TaskResult {
338
+ stdout: string;
339
+ stderr: string;
340
+ executionTime?: number;
341
+ cpuTime?: number;
342
+ createdAt?: string;
343
+ }
334
344
  interface FileUpload {
335
345
  name: string;
336
346
  content: Buffer | string;
@@ -401,6 +411,16 @@ declare class InstaVM {
401
411
  * Execute code asynchronously
402
412
  */
403
413
  executeAsync(command: string, options?: ExecuteOptions): Promise<AsyncExecutionResult>;
414
+ /**
415
+ * Poll for async task result
416
+ *
417
+ * @param taskId - The task ID from executeAsync
418
+ * @param pollInterval - Seconds between polls (default: 1)
419
+ * @param timeout - Maximum seconds to wait (default: 60)
420
+ * @returns Task result with stdout, stderr, execution time, etc.
421
+ * @throws Error if task doesn't complete within timeout
422
+ */
423
+ getTaskResult(taskId: string, pollInterval?: number, timeout?: number): Promise<TaskResult>;
404
424
  /**
405
425
  * Upload files to the execution environment
406
426
  */
@@ -414,6 +434,12 @@ declare class InstaVM {
414
434
  * Note: Sessions auto-expire on the server side. This just clears local state.
415
435
  */
416
436
  closeSession(sessionId?: string): Promise<void>;
437
+ /**
438
+ * Check if current session is still active by checking VM status
439
+ *
440
+ * @returns true if session is active, false otherwise
441
+ */
442
+ isSessionActive(sessionId?: string): Promise<boolean>;
417
443
  /**
418
444
  * Get usage statistics for a session
419
445
  */
package/dist/index.d.ts CHANGED
@@ -316,9 +316,12 @@ interface ExecuteOptions {
316
316
  sessionId?: string;
317
317
  }
318
318
  interface ExecutionResult {
319
- output: string;
319
+ stdout: string;
320
+ stderr: string;
320
321
  success: boolean;
321
- executionTime: number;
322
+ executionTime?: number;
323
+ cpuTime?: number;
324
+ createdAt?: string;
322
325
  sessionId?: string;
323
326
  error?: string;
324
327
  }
@@ -331,6 +334,13 @@ interface AsyncExecutionResult {
331
334
  sessionId?: string;
332
335
  error?: string;
333
336
  }
337
+ interface TaskResult {
338
+ stdout: string;
339
+ stderr: string;
340
+ executionTime?: number;
341
+ cpuTime?: number;
342
+ createdAt?: string;
343
+ }
334
344
  interface FileUpload {
335
345
  name: string;
336
346
  content: Buffer | string;
@@ -401,6 +411,16 @@ declare class InstaVM {
401
411
  * Execute code asynchronously
402
412
  */
403
413
  executeAsync(command: string, options?: ExecuteOptions): Promise<AsyncExecutionResult>;
414
+ /**
415
+ * Poll for async task result
416
+ *
417
+ * @param taskId - The task ID from executeAsync
418
+ * @param pollInterval - Seconds between polls (default: 1)
419
+ * @param timeout - Maximum seconds to wait (default: 60)
420
+ * @returns Task result with stdout, stderr, execution time, etc.
421
+ * @throws Error if task doesn't complete within timeout
422
+ */
423
+ getTaskResult(taskId: string, pollInterval?: number, timeout?: number): Promise<TaskResult>;
404
424
  /**
405
425
  * Upload files to the execution environment
406
426
  */
@@ -414,6 +434,12 @@ declare class InstaVM {
414
434
  * Note: Sessions auto-expire on the server side. This just clears local state.
415
435
  */
416
436
  closeSession(sessionId?: string): Promise<void>;
437
+ /**
438
+ * Check if current session is still active by checking VM status
439
+ *
440
+ * @returns true if session is active, false otherwise
441
+ */
442
+ isSessionActive(sessionId?: string): Promise<boolean>;
417
443
  /**
418
444
  * Get usage statistics for a session
419
445
  */
package/dist/index.js CHANGED
@@ -211,7 +211,7 @@ var HTTPClient = class {
211
211
  const axiosError = error;
212
212
  const status = axiosError.response?.status;
213
213
  const data = axiosError.response?.data;
214
- const message = data?.message || data?.error || axiosError.message;
214
+ const message = data?.message || data?.error || data?.detail || axiosError.message;
215
215
  switch (status) {
216
216
  case 401:
217
217
  throw new AuthenticationError(message, {
@@ -223,14 +223,16 @@ var HTTPClient = class {
223
223
  statusCode: status,
224
224
  response: data
225
225
  });
226
- case 429:
226
+ case 429: {
227
227
  const retryAfter = parseInt(
228
228
  axiosError.response?.headers["retry-after"] || "60"
229
229
  );
230
- throw new RateLimitError(message, retryAfter, {
230
+ const rateLimitMessage = data?.detail || message;
231
+ throw new RateLimitError(rateLimitMessage, retryAfter, {
231
232
  statusCode: status,
232
233
  response: data
233
234
  });
235
+ }
234
236
  case 500:
235
237
  case 502:
236
238
  case 503:
@@ -308,6 +310,7 @@ var HTTPClient = class {
308
310
  */
309
311
  async postFormData(url, formData, headers) {
310
312
  const requestHeaders = {
313
+ "X-API-Key": this.config.apiKey,
311
314
  ...formData.getHeaders(),
312
315
  ...headers
313
316
  };
@@ -990,9 +993,12 @@ var InstaVM = class {
990
993
  this._sessionId = response.session_id;
991
994
  }
992
995
  return {
993
- output: response.stdout || response.output || "",
996
+ stdout: response.stdout || "",
997
+ stderr: response.stderr || "",
994
998
  success: response.success !== false,
995
- executionTime: response.execution_time || 0,
999
+ executionTime: response.execution_time,
1000
+ cpuTime: response.cpu_time,
1001
+ createdAt: response.created_at,
996
1002
  sessionId: response.session_id,
997
1003
  error: response.error
998
1004
  };
@@ -1048,11 +1054,58 @@ var InstaVM = class {
1048
1054
  );
1049
1055
  }
1050
1056
  }
1057
+ /**
1058
+ * Poll for async task result
1059
+ *
1060
+ * @param taskId - The task ID from executeAsync
1061
+ * @param pollInterval - Seconds between polls (default: 1)
1062
+ * @param timeout - Maximum seconds to wait (default: 60)
1063
+ * @returns Task result with stdout, stderr, execution time, etc.
1064
+ * @throws Error if task doesn't complete within timeout
1065
+ */
1066
+ async getTaskResult(taskId, pollInterval = 1, timeout = 60) {
1067
+ this.ensureNotLocal("Async task result retrieval");
1068
+ const startTime = Date.now();
1069
+ while (Date.now() - startTime < timeout * 1e3) {
1070
+ try {
1071
+ const response = await this.httpClient.get(
1072
+ `/v1/executions/${taskId}`,
1073
+ void 0,
1074
+ { "X-API-Key": this.httpClient.apiKey }
1075
+ );
1076
+ if (response.is_complete) {
1077
+ return {
1078
+ stdout: response.serialized_stdout || "",
1079
+ stderr: response.serialized_stderr || "",
1080
+ cpuTime: response.cpu_time,
1081
+ executionTime: response.execution_time,
1082
+ createdAt: response.created_at
1083
+ };
1084
+ }
1085
+ await new Promise((resolve) => setTimeout(resolve, pollInterval * 1e3));
1086
+ } catch (error) {
1087
+ if (error instanceof AuthenticationError || error instanceof RateLimitError || error instanceof NetworkError) {
1088
+ throw error;
1089
+ }
1090
+ if (Date.now() - startTime >= timeout * 1e3) {
1091
+ throw new ExecutionError(
1092
+ `Failed to get task result: ${error instanceof Error ? error.message : String(error)}`
1093
+ );
1094
+ }
1095
+ await new Promise((resolve) => setTimeout(resolve, pollInterval * 1e3));
1096
+ }
1097
+ }
1098
+ throw new ExecutionError(`Task ${taskId} timed out after ${timeout}s`);
1099
+ }
1051
1100
  /**
1052
1101
  * Upload files to the execution environment
1053
1102
  */
1054
1103
  async upload(files, options = {}) {
1055
1104
  this.ensureNotLocal("File upload");
1105
+ const sessionId = options.sessionId || this._sessionId;
1106
+ if (!sessionId) {
1107
+ throw new SessionError("Session ID not set. Please create a session first using createSession().");
1108
+ }
1056
1109
  const formData = new import_form_data.default();
1057
1110
  for (const file of files) {
1058
1111
  if (Buffer.isBuffer(file.content)) {
@@ -1064,9 +1117,7 @@ var InstaVM = class {
1064
1117
  formData.append("paths", file.path);
1065
1118
  }
1066
1119
  }
1067
- if (options.sessionId || this._sessionId) {
1068
- formData.append("session_id", options.sessionId || this._sessionId);
1069
- }
1120
+ formData.append("session_id", sessionId);
1070
1121
  if (options.remotePath) {
1071
1122
  formData.append("remote_path", options.remotePath);
1072
1123
  }
@@ -1127,6 +1178,30 @@ var InstaVM = class {
1127
1178
  this._sessionId = null;
1128
1179
  }
1129
1180
  }
1181
+ /**
1182
+ * Check if current session is still active by checking VM status
1183
+ *
1184
+ * @returns true if session is active, false otherwise
1185
+ */
1186
+ async isSessionActive(sessionId) {
1187
+ const targetSessionId = sessionId || this._sessionId;
1188
+ if (!targetSessionId) {
1189
+ return false;
1190
+ }
1191
+ try {
1192
+ const response = await this.httpClient.get(
1193
+ `/v1/sessions/status/${targetSessionId}`,
1194
+ void 0,
1195
+ { "X-API-Key": this.httpClient.apiKey }
1196
+ );
1197
+ return response.vm_status === "active";
1198
+ } catch (error) {
1199
+ if (error instanceof SessionError || error instanceof AuthenticationError || error instanceof NetworkError) {
1200
+ return false;
1201
+ }
1202
+ return false;
1203
+ }
1204
+ }
1130
1205
  /**
1131
1206
  * Get usage statistics for a session
1132
1207
  */