instavm 0.15.0 → 0.17.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.
Files changed (50) hide show
  1. package/README.md +133 -9
  2. package/dist/InstaVM-DjkmUcaP.d.mts +1393 -0
  3. package/dist/InstaVM-DjkmUcaP.d.ts +1393 -0
  4. package/dist/_instavmToolsCore-34H4iqVZ.d.mts +26 -0
  5. package/dist/_instavmToolsCore-BuaJyxXB.d.ts +26 -0
  6. package/dist/cli.js +7973 -2227
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.mts +12 -919
  9. package/dist/index.d.ts +12 -919
  10. package/dist/index.js +1465 -141
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1450 -136
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/integrations/azure-openai.d.mts +18 -0
  15. package/dist/integrations/azure-openai.d.ts +18 -0
  16. package/dist/integrations/azure-openai.js +332 -0
  17. package/dist/integrations/azure-openai.js.map +1 -0
  18. package/dist/integrations/azure-openai.mjs +299 -0
  19. package/dist/integrations/azure-openai.mjs.map +1 -0
  20. package/dist/integrations/langchain.d.mts +7 -0
  21. package/dist/integrations/langchain.d.ts +7 -0
  22. package/dist/integrations/langchain.js +364 -0
  23. package/dist/integrations/langchain.js.map +1 -0
  24. package/dist/integrations/langchain.mjs +327 -0
  25. package/dist/integrations/langchain.mjs.map +1 -0
  26. package/dist/integrations/llamaindex.d.mts +11 -0
  27. package/dist/integrations/llamaindex.d.ts +11 -0
  28. package/dist/integrations/llamaindex.js +415 -0
  29. package/dist/integrations/llamaindex.js.map +1 -0
  30. package/dist/integrations/llamaindex.mjs +378 -0
  31. package/dist/integrations/llamaindex.mjs.map +1 -0
  32. package/dist/integrations/ollama.d.mts +35 -0
  33. package/dist/integrations/ollama.d.ts +35 -0
  34. package/dist/integrations/ollama.js +421 -0
  35. package/dist/integrations/ollama.js.map +1 -0
  36. package/dist/integrations/ollama.mjs +391 -0
  37. package/dist/integrations/ollama.mjs.map +1 -0
  38. package/dist/integrations/openai.d.mts +19 -0
  39. package/dist/integrations/openai.d.ts +19 -0
  40. package/dist/integrations/openai.js +302 -0
  41. package/dist/integrations/openai.js.map +1 -0
  42. package/dist/integrations/openai.mjs +272 -0
  43. package/dist/integrations/openai.mjs.map +1 -0
  44. package/package.json +46 -4
  45. package/dist/integrations/openai/index.d.mts +0 -16
  46. package/dist/integrations/openai/index.d.ts +0 -16
  47. package/dist/integrations/openai/index.js +0 -38
  48. package/dist/integrations/openai/index.js.map +0 -1
  49. package/dist/integrations/openai/index.mjs +0 -12
  50. package/dist/integrations/openai/index.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
33
  APIKeysManager: () => APIKeysManager,
34
34
  AuditManager: () => AuditManager,
35
35
  AuthenticationError: () => AuthenticationError,
@@ -41,23 +41,33 @@ __export(index_exports, {
41
41
  BrowserSessionError: () => BrowserSessionError,
42
42
  BrowserTimeoutError: () => BrowserTimeoutError,
43
43
  ComputerUseManager: () => ComputerUseManager,
44
+ CreditsManager: () => CreditsManager,
44
45
  CustomDomainsManager: () => CustomDomainsManager,
45
46
  ElementNotFoundError: () => ElementNotFoundError,
46
47
  ExecutionError: () => ExecutionError,
48
+ ForbiddenError: () => ForbiddenError,
49
+ INSTAVM_JS_SDK_VERSION: () => INSTAVM_JS_SDK_VERSION,
47
50
  InstaVM: () => InstaVM,
48
51
  InstaVMError: () => InstaVMError,
49
52
  NetworkError: () => NetworkError,
53
+ PTYManager: () => PTYManager,
50
54
  QuotaExceededError: () => QuotaExceededError,
51
55
  RateLimitError: () => RateLimitError,
56
+ RecordingsManager: () => RecordingsManager,
52
57
  SessionError: () => SessionError,
53
58
  SharesManager: () => SharesManager,
54
59
  SnapshotsManager: () => SnapshotsManager,
60
+ TapeContext: () => TapeContext,
61
+ TapesManager: () => TapesManager,
62
+ ToolCallRecorder: () => ToolCallRecorder,
63
+ ToolCallSpan: () => ToolCallSpan,
55
64
  UnsupportedOperationError: () => UnsupportedOperationError,
56
65
  VMsManager: () => VMsManager,
66
+ VaultsManager: () => VaultsManager,
57
67
  VolumesManager: () => VolumesManager,
58
68
  WebhooksManager: () => WebhooksManager
59
69
  });
60
- module.exports = __toCommonJS(index_exports);
70
+ module.exports = __toCommonJS(src_exports);
61
71
 
62
72
  // src/client/HTTPClient.ts
63
73
  var import_axios = __toESM(require("axios"));
@@ -92,6 +102,11 @@ var QuotaExceededError = class extends InstaVMError {
92
102
  super(message, { ...options, statusCode: 402 });
93
103
  }
94
104
  };
105
+ var ForbiddenError = class extends InstaVMError {
106
+ constructor(message = "Forbidden", options) {
107
+ super(message, { ...options, statusCode: 403 });
108
+ }
109
+ };
95
110
  var NetworkError = class extends InstaVMError {
96
111
  constructor(message = "Network error", options) {
97
112
  super(message, options);
@@ -186,11 +201,17 @@ async function withRetry(fn, options) {
186
201
  throw lastError;
187
202
  }
188
203
 
204
+ // src/version.ts
205
+ var INSTAVM_JS_SDK_VERSION = "0.17.0";
206
+
189
207
  // src/client/HTTPClient.ts
190
208
  var HTTPClient = class {
191
209
  get apiKey() {
192
210
  return this.config.apiKey;
193
211
  }
212
+ get baseURL() {
213
+ return this.config.baseURL;
214
+ }
194
215
  constructor(config) {
195
216
  this.config = config;
196
217
  this.client = import_axios.default.create({
@@ -198,7 +219,7 @@ var HTTPClient = class {
198
219
  timeout: config.timeout,
199
220
  headers: {
200
221
  "Content-Type": "application/json",
201
- "User-Agent": "instavm-js-sdk/0.15.0"
222
+ "User-Agent": `instavm-js-sdk/${INSTAVM_JS_SDK_VERSION}`
202
223
  }
203
224
  });
204
225
  this.setupInterceptors();
@@ -231,6 +252,11 @@ var HTTPClient = class {
231
252
  statusCode: status,
232
253
  response: data
233
254
  });
255
+ case 403:
256
+ throw new ForbiddenError(message, {
257
+ statusCode: status,
258
+ response: data
259
+ });
234
260
  case 429: {
235
261
  const retryAfter = parseInt(
236
262
  axiosError.response?.headers["retry-after"] || "60"
@@ -584,12 +610,24 @@ var BrowserSession = class extends import_eventemitter3.EventEmitter {
584
610
  */
585
611
  async scroll(options = {}) {
586
612
  this.ensureActive();
613
+ if (!options.selector && options.x == null && options.y == null) {
614
+ throw new BrowserInteractionError(
615
+ "At least one of 'selector', 'x', or 'y' must be provided for scrolling."
616
+ );
617
+ }
587
618
  const requestData = {
588
619
  session_id: this.sessionId,
589
- x: options.x || 0,
590
- y: options.y || 500,
591
620
  behavior: options.behavior || "auto"
592
621
  };
622
+ if (options.selector) {
623
+ requestData.selector = options.selector;
624
+ }
625
+ if (options.x != null) {
626
+ requestData.x = options.x;
627
+ }
628
+ if (options.y != null) {
629
+ requestData.y = options.y;
630
+ }
593
631
  try {
594
632
  await this.httpClient.post(
595
633
  "/v1/browser/interactions/scroll",
@@ -636,11 +674,13 @@ var BrowserSession = class extends import_eventemitter3.EventEmitter {
636
674
  async extractElements(selector, attributes = ["text"], options = {}) {
637
675
  this.ensureActive();
638
676
  const requestData = {
639
- selector,
640
677
  attributes,
641
678
  session_id: this.sessionId,
642
679
  max_results: options.maxResults || 100
643
680
  };
681
+ if (selector) {
682
+ requestData.selector = selector;
683
+ }
644
684
  try {
645
685
  const response = await this.httpClient.post(
646
686
  "/v1/browser/interactions/extract",
@@ -707,6 +747,29 @@ var BrowserSession = class extends import_eventemitter3.EventEmitter {
707
747
  );
708
748
  }
709
749
  }
750
+ /**
751
+ * Wait for a string condition (parity with Python ``wait_for``).
752
+ */
753
+ async waitFor(condition, selector, timeout = 3e4) {
754
+ this.ensureActive();
755
+ const requestData = {
756
+ session_id: this.sessionId,
757
+ condition,
758
+ timeout
759
+ };
760
+ if (selector) {
761
+ requestData.selector = selector;
762
+ }
763
+ try {
764
+ return await this.httpClient.post("/v1/browser/interactions/wait", requestData);
765
+ } catch (error) {
766
+ const errorMessage = getErrorMessage(error);
767
+ if (errorMessage.includes("timeout")) {
768
+ throw new BrowserTimeoutError(`Wait condition timeout: ${condition}`, { cause: error });
769
+ }
770
+ throw new BrowserInteractionError(`Wait failed: ${errorMessage}`, { cause: error });
771
+ }
772
+ }
710
773
  /**
711
774
  * Wait for a condition
712
775
  */
@@ -776,12 +839,45 @@ var BrowserSession = class extends import_eventemitter3.EventEmitter {
776
839
  }
777
840
  };
778
841
 
842
+ // src/utils/path.ts
843
+ function encodePathSegment(value) {
844
+ const segment = String(value);
845
+ if (segment === "." || segment === "..") {
846
+ throw new Error("Path traversal not allowed in path segment");
847
+ }
848
+ return encodeURIComponent(segment);
849
+ }
850
+ function encodePathSegments(path2) {
851
+ const segments = path2.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
852
+ if (segments.some((segment) => segment === "..")) {
853
+ throw new Error("Path traversal not allowed in proxy path");
854
+ }
855
+ return segments.map((segment) => encodeURIComponent(segment)).join("/");
856
+ }
857
+
779
858
  // src/client/BrowserManager.ts
780
859
  var BrowserManager = class {
781
- constructor(httpClient, local = false) {
860
+ constructor(httpClient, local = false, getClient) {
782
861
  this.activeSessions = /* @__PURE__ */ new Map();
862
+ this._defaultBrowserSessionId = null;
863
+ this._defaultSessionPromise = null;
783
864
  this.httpClient = httpClient;
784
865
  this.local = local;
866
+ this.getClient = getClient;
867
+ }
868
+ async ensureDefaultBrowserSessionId() {
869
+ if (this._defaultBrowserSessionId) {
870
+ return this._defaultBrowserSessionId;
871
+ }
872
+ if (!this._defaultSessionPromise) {
873
+ this._defaultSessionPromise = this.createSession().catch((err) => {
874
+ this._defaultSessionPromise = null;
875
+ throw err;
876
+ });
877
+ }
878
+ const session = await this._defaultSessionPromise;
879
+ this._defaultBrowserSessionId = session.sessionId;
880
+ return this._defaultBrowserSessionId;
785
881
  }
786
882
  /**
787
883
  * Create a new browser session
@@ -798,10 +894,7 @@ var BrowserManager = class {
798
894
  user_agent: options.userAgent
799
895
  };
800
896
  try {
801
- const response = await this.httpClient.post(
802
- "/v1/browser/sessions/",
803
- requestData
804
- );
897
+ const response = await this.httpClient.post("/v1/browser/sessions/", requestData);
805
898
  if (!response.session_id) {
806
899
  throw new BrowserSessionError("No session ID returned from server");
807
900
  }
@@ -809,14 +902,17 @@ var BrowserManager = class {
809
902
  this.activeSessions.set(response.session_id, session);
810
903
  session.on("close", () => {
811
904
  this.activeSessions.delete(response.session_id);
905
+ if (this._defaultBrowserSessionId === response.session_id) {
906
+ this._defaultBrowserSessionId = null;
907
+ this._defaultSessionPromise = null;
908
+ }
812
909
  });
813
910
  return session;
814
911
  } catch (error) {
815
912
  const errorMessage = error instanceof Error ? error.message : String(error);
816
- throw new BrowserSessionError(
817
- `Failed to create browser session: ${errorMessage}`,
818
- { cause: error }
819
- );
913
+ throw new BrowserSessionError(`Failed to create browser session: ${errorMessage}`, {
914
+ cause: error
915
+ });
820
916
  }
821
917
  }
822
918
  /**
@@ -824,9 +920,8 @@ var BrowserManager = class {
824
920
  */
825
921
  async getSession(sessionId) {
826
922
  try {
827
- const response = await this.httpClient.get(
828
- `/v1/browser/sessions/${sessionId}`
829
- );
923
+ const safe = encodePathSegment(sessionId);
924
+ const response = await this.httpClient.get(`/v1/browser/sessions/${safe}`);
830
925
  return {
831
926
  sessionId: response.session_id,
832
927
  status: response.status || "active",
@@ -837,10 +932,7 @@ var BrowserManager = class {
837
932
  };
838
933
  } catch (error) {
839
934
  const errorMessage = error instanceof Error ? error.message : String(error);
840
- throw new BrowserSessionError(
841
- `Failed to get session info: ${errorMessage}`,
842
- { cause: error }
843
- );
935
+ throw new BrowserSessionError(`Failed to get session info: ${errorMessage}`, { cause: error });
844
936
  }
845
937
  }
846
938
  /**
@@ -849,10 +941,11 @@ var BrowserManager = class {
849
941
  async listSessions() {
850
942
  try {
851
943
  const response = await this.httpClient.get("/v1/browser/sessions/");
852
- if (!Array.isArray(response.sessions)) {
944
+ const sessions = Array.isArray(response) ? response : response?.sessions;
945
+ if (!Array.isArray(sessions)) {
853
946
  return [];
854
947
  }
855
- return response.sessions.map((session) => ({
948
+ return sessions.map((session) => ({
856
949
  sessionId: session.session_id,
857
950
  status: session.status || "active",
858
951
  createdAt: session.created_at,
@@ -862,52 +955,62 @@ var BrowserManager = class {
862
955
  }));
863
956
  } catch (error) {
864
957
  const errorMessage = error instanceof Error ? error.message : String(error);
865
- throw new BrowserSessionError(
866
- `Failed to list sessions: ${errorMessage}`,
867
- { cause: error }
868
- );
958
+ throw new BrowserSessionError(`Failed to list sessions: ${errorMessage}`, { cause: error });
869
959
  }
870
960
  }
871
- /**
872
- * Get a locally tracked session
873
- */
874
961
  getLocalSession(sessionId) {
875
962
  return this.activeSessions.get(sessionId);
876
963
  }
877
- /**
878
- * Get all locally tracked sessions
879
- */
964
+ attachSession(sessionId) {
965
+ const existing = this.activeSessions.get(sessionId);
966
+ if (existing) {
967
+ return existing;
968
+ }
969
+ const session = new BrowserSession(sessionId, this.httpClient);
970
+ this.activeSessions.set(sessionId, session);
971
+ session.on("close", () => {
972
+ this.activeSessions.delete(sessionId);
973
+ if (this._defaultBrowserSessionId === sessionId) {
974
+ this._defaultBrowserSessionId = null;
975
+ this._defaultSessionPromise = null;
976
+ }
977
+ });
978
+ return session;
979
+ }
880
980
  getLocalSessions() {
881
981
  return Array.from(this.activeSessions.values());
882
982
  }
883
- /**
884
- * Close all active sessions
885
- */
886
983
  async closeAllSessions() {
887
984
  const sessions = Array.from(this.activeSessions.values());
888
- await Promise.allSettled(
889
- sessions.map((session) => session.close())
890
- );
985
+ await Promise.allSettled(sessions.map((session) => session.close()));
891
986
  this.activeSessions.clear();
987
+ this._defaultBrowserSessionId = null;
988
+ this._defaultSessionPromise = null;
892
989
  }
893
990
  /**
894
- * Navigate to a URL (local mode support - no session required)
991
+ * Navigate to a URL (local: no session; cloud: auto default browser session)
895
992
  */
896
993
  async navigate(url, options = {}) {
897
994
  if (!this.local) {
898
- throw new UnsupportedOperationError(
899
- "navigate() without session is only supported in local mode. In cloud mode, create a session first."
900
- );
995
+ const client = this.getClient?.();
996
+ if (!client) {
997
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
998
+ }
999
+ const sid = await this.ensureDefaultBrowserSessionId();
1000
+ const raw = await client.browserNavigate(url, sid, options.waitTimeout ?? 3e4);
1001
+ return {
1002
+ success: raw.success !== false,
1003
+ url: raw.url || url,
1004
+ title: raw.title,
1005
+ status: raw.status
1006
+ };
901
1007
  }
902
1008
  const requestData = {
903
1009
  url,
904
1010
  wait_timeout: options.waitTimeout || 3e4
905
1011
  };
906
1012
  try {
907
- const response = await this.httpClient.post(
908
- "/v1/browser/interactions/navigate",
909
- requestData
910
- );
1013
+ const response = await this.httpClient.post("/v1/browser/interactions/navigate", requestData);
911
1014
  return {
912
1015
  success: response.success !== false,
913
1016
  url: response.url || url,
@@ -916,20 +1019,112 @@ var BrowserManager = class {
916
1019
  };
917
1020
  } catch (error) {
918
1021
  const errorMessage = error instanceof Error ? error.message : String(error);
919
- throw new BrowserNavigationError(
920
- `Navigation failed: ${errorMessage}`,
921
- { cause: error }
922
- );
1022
+ throw new BrowserNavigationError(`Navigation failed: ${errorMessage}`, { cause: error });
1023
+ }
1024
+ }
1025
+ async click(selector, sessionId, force = false, timeout = 3e4) {
1026
+ if (!this.local) {
1027
+ const client = this.getClient?.();
1028
+ if (!client) {
1029
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1030
+ }
1031
+ const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
1032
+ await client.browserClick(selector, sid, force, timeout);
1033
+ return;
1034
+ }
1035
+ throw new UnsupportedOperationError("click() without session is only supported in cloud mode via InstaVM.");
1036
+ }
1037
+ async type(selector, text, sessionId, delay = 100, timeout = 3e4) {
1038
+ if (!this.local) {
1039
+ const client = this.getClient?.();
1040
+ if (!client) {
1041
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1042
+ }
1043
+ const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
1044
+ await client.browserType(selector, text, sid, delay, timeout);
1045
+ return;
923
1046
  }
1047
+ throw new UnsupportedOperationError("type() requires cloud mode with InstaVM client.");
1048
+ }
1049
+ async fill(selector, value, sessionId, timeout = 3e4) {
1050
+ if (!this.local) {
1051
+ const client = this.getClient?.();
1052
+ if (!client) {
1053
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1054
+ }
1055
+ const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
1056
+ await client.browserFill(selector, value, sid, timeout);
1057
+ return;
1058
+ }
1059
+ throw new UnsupportedOperationError("fill() requires cloud mode with InstaVM client.");
1060
+ }
1061
+ async scroll(options = {}) {
1062
+ if (!this.local) {
1063
+ const client = this.getClient?.();
1064
+ if (!client) {
1065
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1066
+ }
1067
+ const sid = options.sessionId ?? await this.ensureDefaultBrowserSessionId();
1068
+ await client.browserScroll(sid, options.selector ?? null, options.x ?? null, options.y ?? null);
1069
+ return;
1070
+ }
1071
+ throw new UnsupportedOperationError("scroll() cloud convenience requires InstaVM client.");
1072
+ }
1073
+ async waitFor(condition, selector, sessionId, timeout = 3e4) {
1074
+ if (!this.local) {
1075
+ const client = this.getClient?.();
1076
+ if (!client) {
1077
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1078
+ }
1079
+ const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
1080
+ return client.browserWait(condition, sid, selector ?? null, timeout);
1081
+ }
1082
+ throw new UnsupportedOperationError("waitFor() requires cloud mode with InstaVM client.");
1083
+ }
1084
+ async extractElements(selector, sessionId, attributes, options = {}) {
1085
+ if (!this.local) {
1086
+ const client = this.getClient?.();
1087
+ if (!client) {
1088
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1089
+ }
1090
+ const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
1091
+ const attrs = attributes ?? ["text"];
1092
+ const result = await client.browserExtractElements(sid, selector ?? null, attrs);
1093
+ if (options.maxResults !== void 0 && Array.isArray(result)) {
1094
+ return result.slice(0, options.maxResults);
1095
+ }
1096
+ return result;
1097
+ }
1098
+ throw new UnsupportedOperationError("extractElements() requires cloud mode with InstaVM client.");
924
1099
  }
925
1100
  /**
926
- * Extract LLM-friendly content (local mode support - no session required)
1101
+ * Extract LLM-friendly content (local: URL required; cloud: optional session)
927
1102
  */
928
1103
  async extractContent(options = {}) {
929
1104
  if (!this.local) {
930
- throw new UnsupportedOperationError(
931
- "extractContent() without session is only supported in local mode. In cloud mode, create a session first."
1105
+ const client = this.getClient?.();
1106
+ if (!client) {
1107
+ throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
1108
+ }
1109
+ let sid = options.sessionId;
1110
+ if (!sid) {
1111
+ sid = await this.ensureDefaultBrowserSessionId();
1112
+ }
1113
+ const raw = await client.browserExtractContent(
1114
+ sid,
1115
+ options.url ?? null,
1116
+ options.includeInteractive !== false,
1117
+ options.includeAnchors !== false,
1118
+ options.maxAnchors ?? 50
932
1119
  );
1120
+ return {
1121
+ readableContent: {
1122
+ ...raw.readable_content || {},
1123
+ content: raw.readable_content?.content || ""
1124
+ },
1125
+ interactiveElements: raw.interactive_elements,
1126
+ contentAnchors: raw.content_anchors
1127
+ };
933
1128
  }
934
1129
  if (!options.url) {
935
1130
  throw new BrowserInteractionError("url is required in local mode");
@@ -941,10 +1136,7 @@ var BrowserManager = class {
941
1136
  max_anchors: options.maxAnchors ?? 50
942
1137
  };
943
1138
  try {
944
- const response = await this.httpClient.post(
945
- "/v1/browser/interactions/content",
946
- requestData
947
- );
1139
+ const response = await this.httpClient.post("/v1/browser/interactions/content", requestData);
948
1140
  return {
949
1141
  readableContent: {
950
1142
  ...response.readable_content || {},
@@ -955,36 +1147,33 @@ var BrowserManager = class {
955
1147
  };
956
1148
  } catch (error) {
957
1149
  const errorMessage = error instanceof Error ? error.message : String(error);
958
- throw new BrowserInteractionError(
959
- `Content extraction failed: ${errorMessage}`,
960
- { cause: error }
961
- );
1150
+ throw new BrowserInteractionError(`Content extraction failed: ${errorMessage}`, { cause: error });
962
1151
  }
963
1152
  }
964
1153
  /**
965
- * Clean up resources
1154
+ * Closes the default browser session if one was created for cloud convenience.
966
1155
  */
1156
+ async close() {
1157
+ if (!this._defaultBrowserSessionId) {
1158
+ return;
1159
+ }
1160
+ const sid = this._defaultBrowserSessionId;
1161
+ const session = this.activeSessions.get(sid);
1162
+ if (session) {
1163
+ try {
1164
+ await session.close();
1165
+ } catch {
1166
+ }
1167
+ }
1168
+ this._defaultBrowserSessionId = null;
1169
+ this._defaultSessionPromise = null;
1170
+ }
967
1171
  async dispose() {
1172
+ await this.close();
968
1173
  await this.closeAllSessions();
969
1174
  }
970
1175
  };
971
1176
 
972
- // src/utils/path.ts
973
- function encodePathSegment(value) {
974
- const segment = String(value);
975
- if (segment === "." || segment === "..") {
976
- throw new Error("Path traversal not allowed in path segment");
977
- }
978
- return encodeURIComponent(segment);
979
- }
980
- function encodePathSegments(path2) {
981
- const segments = path2.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
982
- if (segments.some((segment) => segment === "..")) {
983
- throw new Error("Path traversal not allowed in proxy path");
984
- }
985
- return segments.map((segment) => encodeURIComponent(segment)).join("/");
986
- }
987
-
988
1177
  // src/client/VMsManager.ts
989
1178
  var VMsManager = class {
990
1179
  constructor(httpClient, local = false) {
@@ -1529,7 +1718,7 @@ var WebhooksManager = class {
1529
1718
 
1530
1719
  // src/client/VolumesManager.ts
1531
1720
  var import_fs = __toESM(require("fs"));
1532
- var import_path9 = __toESM(require("path"));
1721
+ var import_path10 = __toESM(require("path"));
1533
1722
  var import_form_data = __toESM(require("form-data"));
1534
1723
  var VolumesManager = class {
1535
1724
  constructor(httpClient, local = false) {
@@ -1626,7 +1815,7 @@ var VolumesManager = class {
1626
1815
  const formData = new import_form_data.default();
1627
1816
  if (hasFilePath) {
1628
1817
  const sourcePath = payload.filePath;
1629
- const filename = payload.filename || import_path9.default.basename(sourcePath);
1818
+ const filename = payload.filename || import_path10.default.basename(sourcePath);
1630
1819
  formData.append("file", import_fs.default.createReadStream(sourcePath), { filename });
1631
1820
  } else {
1632
1821
  const filename = payload.filename || "upload.bin";
@@ -1683,60 +1872,760 @@ var VolumesManager = class {
1683
1872
  }
1684
1873
  };
1685
1874
 
1686
- // src/client/InstaVM.ts
1687
- var import_form_data2 = __toESM(require("form-data"));
1688
- var InstaVM = class {
1689
- constructor(apiKey, options = {}) {
1690
- this._sessionId = null;
1691
- this._vmUsed = false;
1692
- this.local = options.local || false;
1693
- this.timeout = options.timeout || 3e5;
1694
- this.memory_mb = options.memory_mb;
1695
- this.cpu_count = options.cpu_count;
1696
- this.metadata = options.metadata;
1697
- this.env = options.env;
1698
- if (!this.local && !apiKey) {
1699
- throw new AuthenticationError("API key is required for cloud mode");
1875
+ // src/client/TapesManager.ts
1876
+ var TapesManager = class {
1877
+ constructor(httpClient, local = false) {
1878
+ this.httpClient = httpClient;
1879
+ this.local = local;
1880
+ }
1881
+ ensureCloud(operation) {
1882
+ if (this.local) {
1883
+ throw new UnsupportedOperationError(
1884
+ `${operation} is not supported in local mode.`
1885
+ );
1700
1886
  }
1701
- const config = {
1702
- baseURL: this.local ? options.localURL || "http://coderunner.local:8222" : options.baseURL || "https://api.instavm.io",
1703
- timeout: this.timeout,
1704
- maxRetries: options.maxRetries || 3,
1705
- retryDelay: options.retryDelay || 1e3,
1706
- apiKey: apiKey || ""
1887
+ }
1888
+ authHeaders() {
1889
+ return { "X-API-Key": this.httpClient.apiKey };
1890
+ }
1891
+ async start(sessionId, options = {}) {
1892
+ this.ensureCloud("Tape start");
1893
+ const safe = encodePathSegment(sessionId);
1894
+ return this.httpClient.post(
1895
+ `/v1/sessions/${safe}/tape/start`,
1896
+ {
1897
+ record_egress_content: options.recordEgressContent ?? false,
1898
+ record_fs: options.recordFs ?? true,
1899
+ retention_days: options.retentionDays ?? 7
1900
+ },
1901
+ this.authHeaders()
1902
+ );
1903
+ }
1904
+ async stop(tapeId) {
1905
+ this.ensureCloud("Tape stop");
1906
+ const safe = encodePathSegment(tapeId);
1907
+ return this.httpClient.post(
1908
+ `/v1/tapes/${safe}/stop`,
1909
+ void 0,
1910
+ this.authHeaders()
1911
+ );
1912
+ }
1913
+ async list(query = {}) {
1914
+ this.ensureCloud("Tape listing");
1915
+ const params = {
1916
+ limit: query.limit ?? 50,
1917
+ offset: query.offset ?? 0
1707
1918
  };
1708
- this.httpClient = new HTTPClient(config);
1709
- this.browser = new BrowserManager(this.httpClient, this.local);
1710
- this.vms = new VMsManager(this.httpClient, this.local);
1711
- this.snapshots = new SnapshotsManager(this.httpClient, this.local);
1712
- this.shares = new SharesManager(this.httpClient, this.local, () => this._sessionId);
1713
- this.customDomains = new CustomDomainsManager(this.httpClient, this.local);
1714
- this.computerUse = new ComputerUseManager(this.httpClient, this.local);
1715
- this.apiKeys = new APIKeysManager(this.httpClient, this.local);
1716
- this.audit = new AuditManager(this.httpClient, this.local);
1717
- this.webhooks = new WebhooksManager(this.httpClient, this.local);
1718
- this.volumes = new VolumesManager(this.httpClient, this.local);
1919
+ if (query.sessionId !== void 0) params.session_id = query.sessionId;
1920
+ if (query.vmId !== void 0) params.vm_id = query.vmId;
1921
+ if (query.status !== void 0) params.status = query.status;
1922
+ const result = await this.httpClient.get(
1923
+ "/v1/tapes",
1924
+ params,
1925
+ this.authHeaders()
1926
+ );
1927
+ return Array.isArray(result) ? result : [];
1719
1928
  }
1720
- /**
1721
- * Ensure operation is not called in local mode
1722
- */
1723
- ensureNotLocal(operationName) {
1929
+ async get(tapeId) {
1930
+ this.ensureCloud("Tape lookup");
1931
+ const safe = encodePathSegment(tapeId);
1932
+ return this.httpClient.get(
1933
+ `/v1/tapes/${safe}`,
1934
+ void 0,
1935
+ this.authHeaders()
1936
+ );
1937
+ }
1938
+ async events(tapeId, query = {}) {
1939
+ this.ensureCloud("Tape events");
1940
+ const safe = encodePathSegment(tapeId);
1941
+ const params = { limit: query.limit ?? 200 };
1942
+ if (query.afterStep !== void 0) params.after_step = query.afterStep;
1943
+ if (query.kind !== void 0) params.kind = query.kind;
1944
+ const result = await this.httpClient.get(
1945
+ `/v1/tapes/${safe}/events`,
1946
+ params,
1947
+ this.authHeaders()
1948
+ );
1949
+ return Array.isArray(result) ? result : [];
1950
+ }
1951
+ async appendEvent(tapeId, kind, options = {}) {
1952
+ this.ensureCloud("Tape append event");
1953
+ const safe = encodePathSegment(tapeId);
1954
+ const body = { kind, payload: options.payload || {} };
1955
+ if (options.stepId !== void 0) body.step_id = options.stepId;
1956
+ if (options.parentStepId !== void 0) body.parent_step_id = options.parentStepId;
1957
+ if (options.checkpointId !== void 0) body.checkpoint_id = options.checkpointId;
1958
+ return this.httpClient.post(
1959
+ `/v1/tapes/${safe}/events`,
1960
+ body,
1961
+ this.authHeaders()
1962
+ );
1963
+ }
1964
+ async lanes(tapeId) {
1965
+ this.ensureCloud("Tape lanes");
1966
+ const safe = encodePathSegment(tapeId);
1967
+ return this.httpClient.get(
1968
+ `/v1/tapes/${safe}/lanes`,
1969
+ void 0,
1970
+ this.authHeaders()
1971
+ );
1972
+ }
1973
+ async diff(tapeId, fromStep, toStep) {
1974
+ this.ensureCloud("Tape diff");
1975
+ const safe = encodePathSegment(tapeId);
1976
+ return this.httpClient.get(
1977
+ `/v1/tapes/${safe}/diff`,
1978
+ { from: fromStep, to: toStep },
1979
+ this.authHeaders()
1980
+ );
1981
+ }
1982
+ async branch(tapeId, atStep, options = {}) {
1983
+ this.ensureCloud("Tape branch");
1984
+ const safe = encodePathSegment(tapeId);
1985
+ return this.httpClient.post(
1986
+ `/v1/tapes/${safe}/branch`,
1987
+ { at_step: atStep, mode: options.mode || "live" },
1988
+ this.authHeaders()
1989
+ );
1990
+ }
1991
+ async delete(tapeId) {
1992
+ this.ensureCloud("Tape deletion");
1993
+ const safe = encodePathSegment(tapeId);
1994
+ return this.httpClient.delete(
1995
+ `/v1/tapes/${safe}`,
1996
+ this.authHeaders()
1997
+ );
1998
+ }
1999
+ async export(tapeId) {
2000
+ this.ensureCloud("Tape export");
2001
+ const safe = encodePathSegment(tapeId);
2002
+ return this.httpClient.get(
2003
+ `/v1/tapes/${safe}/export`,
2004
+ void 0,
2005
+ this.authHeaders()
2006
+ );
2007
+ }
2008
+ };
2009
+
2010
+ // src/client/VaultsManager.ts
2011
+ var VaultsManager = class {
2012
+ constructor(httpClient, local = false) {
2013
+ this.httpClient = httpClient;
2014
+ this.local = local;
2015
+ }
2016
+ ensureCloud() {
1724
2017
  if (this.local) {
1725
2018
  throw new UnsupportedOperationError(
1726
- `${operationName} is not supported in local mode. This operation is only available when using the cloud API.`
2019
+ "Vault management is not supported in local mode."
1727
2020
  );
1728
2021
  }
1729
2022
  }
1730
- /**
1731
- * Execute code synchronously
1732
- *
1733
- * @param command - Command to execute
1734
- * @param options - Execution options
1735
- * @param options.timeout - Request timeout in milliseconds (used for HTTP request timeout and sent to API in seconds)
1736
- * @returns Execution result
1737
- */
1738
- async execute(command, options = {}) {
1739
- let sessionId = options.sessionId || this._sessionId;
2023
+ auth() {
2024
+ return { "X-API-Key": this.httpClient.apiKey };
2025
+ }
2026
+ async list() {
2027
+ this.ensureCloud();
2028
+ const result = await this.httpClient.get("/v1/vaults", void 0, this.auth());
2029
+ if (Array.isArray(result)) return result;
2030
+ return result?.vaults || [];
2031
+ }
2032
+ async create(name, description) {
2033
+ this.ensureCloud();
2034
+ const body = { name };
2035
+ if (description !== void 0) body.description = description;
2036
+ return this.httpClient.post("/v1/vaults", body, this.auth());
2037
+ }
2038
+ async get(vaultId) {
2039
+ this.ensureCloud();
2040
+ return this.httpClient.get(
2041
+ `/v1/vaults/${encodePathSegment(vaultId)}`,
2042
+ void 0,
2043
+ this.auth()
2044
+ );
2045
+ }
2046
+ async update(vaultId, updates) {
2047
+ this.ensureCloud();
2048
+ const body = {};
2049
+ if (updates.name !== void 0) body.name = updates.name;
2050
+ if (updates.description !== void 0) body.description = updates.description;
2051
+ return this.httpClient.patch(
2052
+ `/v1/vaults/${encodePathSegment(vaultId)}`,
2053
+ body,
2054
+ this.auth()
2055
+ );
2056
+ }
2057
+ async delete(vaultId) {
2058
+ this.ensureCloud();
2059
+ return this.httpClient.delete(
2060
+ `/v1/vaults/${encodePathSegment(vaultId)}`,
2061
+ this.auth()
2062
+ );
2063
+ }
2064
+ async discover(vaultId) {
2065
+ this.ensureCloud();
2066
+ return this.httpClient.get(
2067
+ `/v1/vaults/${encodePathSegment(vaultId)}/discover`,
2068
+ void 0,
2069
+ this.auth()
2070
+ );
2071
+ }
2072
+ async listCredentials(vaultId) {
2073
+ this.ensureCloud();
2074
+ const result = await this.httpClient.get(
2075
+ `/v1/vaults/${encodePathSegment(vaultId)}/credentials`,
2076
+ void 0,
2077
+ this.auth()
2078
+ );
2079
+ if (Array.isArray(result)) return result;
2080
+ return result?.credentials || [];
2081
+ }
2082
+ async addCredential(vaultId, name, value, options = {}) {
2083
+ this.ensureCloud();
2084
+ const body = {
2085
+ name,
2086
+ value,
2087
+ credential_type: options.credentialType ?? "api_key"
2088
+ };
2089
+ if (options.description !== void 0) body.description = options.description;
2090
+ return this.httpClient.post(
2091
+ `/v1/vaults/${encodePathSegment(vaultId)}/credentials`,
2092
+ body,
2093
+ this.auth()
2094
+ );
2095
+ }
2096
+ async rotateCredential(vaultId, credentialId, value) {
2097
+ this.ensureCloud();
2098
+ return this.httpClient.post(
2099
+ `/v1/vaults/${encodePathSegment(vaultId)}/credentials/${encodePathSegment(credentialId)}/rotate`,
2100
+ { value },
2101
+ this.auth()
2102
+ );
2103
+ }
2104
+ async deleteCredential(vaultId, credentialId) {
2105
+ this.ensureCloud();
2106
+ return this.httpClient.delete(
2107
+ `/v1/vaults/${encodePathSegment(vaultId)}/credentials/${encodePathSegment(credentialId)}`,
2108
+ this.auth()
2109
+ );
2110
+ }
2111
+ async listServices(vaultId) {
2112
+ this.ensureCloud();
2113
+ const result = await this.httpClient.get(
2114
+ `/v1/vaults/${encodePathSegment(vaultId)}/services`,
2115
+ void 0,
2116
+ this.auth()
2117
+ );
2118
+ if (Array.isArray(result)) return result;
2119
+ return result?.services || [];
2120
+ }
2121
+ async addService(vaultId, host, authConfig, options = {}) {
2122
+ this.ensureCloud();
2123
+ const body = {
2124
+ host,
2125
+ auth_config: authConfig,
2126
+ enabled: options.enabled ?? true
2127
+ };
2128
+ if (options.description !== void 0) body.description = options.description;
2129
+ return this.httpClient.post(
2130
+ `/v1/vaults/${encodePathSegment(vaultId)}/services`,
2131
+ body,
2132
+ this.auth()
2133
+ );
2134
+ }
2135
+ async addServicesFromTemplates(vaultId, templateIds) {
2136
+ this.ensureCloud();
2137
+ return this.httpClient.post(
2138
+ `/v1/vaults/${encodePathSegment(vaultId)}/services:from_template`,
2139
+ { template_ids: templateIds },
2140
+ this.auth()
2141
+ );
2142
+ }
2143
+ async deleteService(vaultId, serviceId) {
2144
+ this.ensureCloud();
2145
+ return this.httpClient.delete(
2146
+ `/v1/vaults/${encodePathSegment(vaultId)}/services/${encodePathSegment(serviceId)}`,
2147
+ this.auth()
2148
+ );
2149
+ }
2150
+ async getCatalog() {
2151
+ this.ensureCloud();
2152
+ const result = await this.httpClient.get(
2153
+ "/v1/vaults/catalog",
2154
+ void 0,
2155
+ this.auth()
2156
+ );
2157
+ if (Array.isArray(result)) return result;
2158
+ return result?.templates || [];
2159
+ }
2160
+ async getRequestLogs(vaultId, limit = 100) {
2161
+ this.ensureCloud();
2162
+ const result = await this.httpClient.get(
2163
+ `/v1/vaults/${encodePathSegment(vaultId)}/request-logs`,
2164
+ { limit },
2165
+ this.auth()
2166
+ );
2167
+ if (Array.isArray(result)) return result;
2168
+ return result?.logs || result?.request_logs || [];
2169
+ }
2170
+ };
2171
+
2172
+ // src/client/PTYManager.ts
2173
+ var PTYManager = class {
2174
+ constructor(httpClient, local, getBaseURL) {
2175
+ this.httpClient = httpClient;
2176
+ this.local = local;
2177
+ this.getBaseURL = getBaseURL;
2178
+ }
2179
+ ensureCloud() {
2180
+ if (this.local) {
2181
+ throw new UnsupportedOperationError(
2182
+ "PTY management is not supported in local mode."
2183
+ );
2184
+ }
2185
+ }
2186
+ auth() {
2187
+ return { "X-API-Key": this.httpClient.apiKey };
2188
+ }
2189
+ buildPayload(options) {
2190
+ const payload = {
2191
+ cols: options.cols ?? 80,
2192
+ rows: options.rows ?? 24
2193
+ };
2194
+ if (options.command !== void 0) payload.shell = options.command;
2195
+ if (options.ptyId !== void 0) payload.id = options.ptyId;
2196
+ return payload;
2197
+ }
2198
+ buildWsUrl(path2) {
2199
+ const trimmedBase = this.getBaseURL().replace(/\/+$/, "");
2200
+ const base = trimmedBase.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://");
2201
+ const apiKey = encodeURIComponent(this.httpClient.apiKey || "");
2202
+ return `${base}${path2}?api_key=${apiKey}`;
2203
+ }
2204
+ // -- Session-scoped --------------------------------------------------------
2205
+ async create(sessionId, options = {}) {
2206
+ this.ensureCloud();
2207
+ return this.httpClient.post(
2208
+ `/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions`,
2209
+ this.buildPayload(options),
2210
+ this.auth()
2211
+ );
2212
+ }
2213
+ async list(sessionId) {
2214
+ this.ensureCloud();
2215
+ const result = await this.httpClient.get(
2216
+ `/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions`,
2217
+ void 0,
2218
+ this.auth()
2219
+ );
2220
+ if (Array.isArray(result)) return result;
2221
+ return result?.sessions || [];
2222
+ }
2223
+ async get(sessionId, ptyId) {
2224
+ this.ensureCloud();
2225
+ return this.httpClient.get(
2226
+ `/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions/${encodePathSegment(ptyId)}`,
2227
+ void 0,
2228
+ this.auth()
2229
+ );
2230
+ }
2231
+ async resize(sessionId, ptyId, cols, rows) {
2232
+ this.ensureCloud();
2233
+ return this.httpClient.post(
2234
+ `/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions/${encodePathSegment(ptyId)}/resize`,
2235
+ { cols, rows },
2236
+ this.auth()
2237
+ );
2238
+ }
2239
+ async kill(sessionId, ptyId) {
2240
+ this.ensureCloud();
2241
+ return this.httpClient.delete(
2242
+ `/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions/${encodePathSegment(ptyId)}`,
2243
+ this.auth()
2244
+ );
2245
+ }
2246
+ wsUrl(sessionId, ptyId) {
2247
+ return this.buildWsUrl(
2248
+ `/v1/sessions/${encodePathSegment(sessionId)}/pty/${encodePathSegment(ptyId)}/connect`
2249
+ );
2250
+ }
2251
+ // -- VM-scoped (docker exec -it pattern) -----------------------------------
2252
+ async createForVm(vmId, options = {}) {
2253
+ this.ensureCloud();
2254
+ return this.httpClient.post(
2255
+ `/v1/vms/${encodePathSegment(vmId)}/pty/sessions`,
2256
+ this.buildPayload(options),
2257
+ this.auth()
2258
+ );
2259
+ }
2260
+ async listForVm(vmId) {
2261
+ this.ensureCloud();
2262
+ const result = await this.httpClient.get(
2263
+ `/v1/vms/${encodePathSegment(vmId)}/pty/sessions`,
2264
+ void 0,
2265
+ this.auth()
2266
+ );
2267
+ if (Array.isArray(result)) return result;
2268
+ return result?.sessions || [];
2269
+ }
2270
+ async getForVm(vmId, ptyId) {
2271
+ this.ensureCloud();
2272
+ return this.httpClient.get(
2273
+ `/v1/vms/${encodePathSegment(vmId)}/pty/sessions/${encodePathSegment(ptyId)}`,
2274
+ void 0,
2275
+ this.auth()
2276
+ );
2277
+ }
2278
+ async resizeForVm(vmId, ptyId, cols, rows) {
2279
+ this.ensureCloud();
2280
+ return this.httpClient.post(
2281
+ `/v1/vms/${encodePathSegment(vmId)}/pty/sessions/${encodePathSegment(ptyId)}/resize`,
2282
+ { cols, rows },
2283
+ this.auth()
2284
+ );
2285
+ }
2286
+ async killForVm(vmId, ptyId) {
2287
+ this.ensureCloud();
2288
+ return this.httpClient.delete(
2289
+ `/v1/vms/${encodePathSegment(vmId)}/pty/sessions/${encodePathSegment(ptyId)}`,
2290
+ this.auth()
2291
+ );
2292
+ }
2293
+ wsUrlForVm(vmId, ptyId) {
2294
+ return this.buildWsUrl(
2295
+ `/v1/vms/${encodePathSegment(vmId)}/pty/${encodePathSegment(ptyId)}/connect`
2296
+ );
2297
+ }
2298
+ };
2299
+
2300
+ // src/client/RecordingsManager.ts
2301
+ var import_fs2 = __toESM(require("fs"));
2302
+ var import_promises = require("fs/promises");
2303
+ var import_promises2 = require("stream/promises");
2304
+ var import_axios2 = __toESM(require("axios"));
2305
+ var RecordingsManager = class {
2306
+ constructor(httpClient, local, getBaseURL) {
2307
+ this.httpClient = httpClient;
2308
+ this.local = local;
2309
+ this.getBaseURL = getBaseURL;
2310
+ }
2311
+ ensureCloud() {
2312
+ if (this.local) {
2313
+ throw new UnsupportedOperationError(
2314
+ "Recording management is not supported in local mode."
2315
+ );
2316
+ }
2317
+ }
2318
+ auth() {
2319
+ return { "X-API-Key": this.httpClient.apiKey };
2320
+ }
2321
+ sdkHeaders() {
2322
+ return { "User-Agent": `instavm-js-sdk/${INSTAVM_JS_SDK_VERSION}` };
2323
+ }
2324
+ async list(query = {}) {
2325
+ this.ensureCloud();
2326
+ const params = {
2327
+ limit: query.limit ?? 50,
2328
+ offset: query.offset ?? 0
2329
+ };
2330
+ if (query.sessionId !== void 0) params.session_id = query.sessionId;
2331
+ if (query.status !== void 0) params.status = query.status;
2332
+ const result = await this.httpClient.get("/v1/recordings", params, this.auth());
2333
+ return Array.isArray(result) ? result : [];
2334
+ }
2335
+ async get(recordingId) {
2336
+ this.ensureCloud();
2337
+ return this.httpClient.get(
2338
+ `/v1/recordings/${encodePathSegment(recordingId)}`,
2339
+ void 0,
2340
+ this.auth()
2341
+ );
2342
+ }
2343
+ /**
2344
+ * Resolve the presigned download URL for a recording without following the
2345
+ * 302 redirect. The returned URL is short-lived and pre-authorised.
2346
+ */
2347
+ async getDownloadUrl(recordingId) {
2348
+ this.ensureCloud();
2349
+ const url = `${this.getBaseURL().replace(/\/+$/, "")}/v1/recordings/${encodePathSegment(recordingId)}/download`;
2350
+ const response = await import_axios2.default.request({
2351
+ method: "GET",
2352
+ url,
2353
+ headers: {
2354
+ ...this.auth(),
2355
+ ...this.sdkHeaders()
2356
+ },
2357
+ maxRedirects: 0,
2358
+ validateStatus: (status) => status >= 200 && status < 400
2359
+ });
2360
+ if ([301, 302, 303, 307, 308].includes(response.status)) {
2361
+ const location = response.headers?.location || response.headers?.Location;
2362
+ if (!location) {
2363
+ throw new InstaVMError("Recording download response missing Location header");
2364
+ }
2365
+ return location;
2366
+ }
2367
+ throw new InstaVMError(
2368
+ `Server did not redirect to a presigned URL (status=${response.status})`
2369
+ );
2370
+ }
2371
+ /**
2372
+ * Stream a recording to a local file using the presigned URL. The S3 URL
2373
+ * must NOT carry our X-API-Key header — fetch it with a clean axios call.
2374
+ */
2375
+ async download(recordingId, outputPath) {
2376
+ this.ensureCloud();
2377
+ const presignedUrl = await this.getDownloadUrl(recordingId);
2378
+ const response = await import_axios2.default.request({
2379
+ method: "GET",
2380
+ url: presignedUrl,
2381
+ responseType: "stream",
2382
+ headers: this.sdkHeaders()
2383
+ });
2384
+ const writer = import_fs2.default.createWriteStream(outputPath);
2385
+ try {
2386
+ await (0, import_promises2.pipeline)(response.data, writer);
2387
+ } catch (error) {
2388
+ await (0, import_promises.unlink)(outputPath).catch(() => void 0);
2389
+ throw error;
2390
+ }
2391
+ const fileStat = await (0, import_promises.stat)(outputPath);
2392
+ return { recordingId, path: outputPath, bytes: fileStat.size };
2393
+ }
2394
+ async delete(recordingId) {
2395
+ this.ensureCloud();
2396
+ return this.httpClient.delete(
2397
+ `/v1/recordings/${encodePathSegment(recordingId)}`,
2398
+ this.auth()
2399
+ );
2400
+ }
2401
+ };
2402
+
2403
+ // src/client/CreditsManager.ts
2404
+ var CreditsManager = class {
2405
+ constructor(httpClient, local = false) {
2406
+ this.httpClient = httpClient;
2407
+ this.local = local;
2408
+ }
2409
+ ensureCloud() {
2410
+ if (this.local) {
2411
+ throw new UnsupportedOperationError(
2412
+ "Credits API is not supported in local mode."
2413
+ );
2414
+ }
2415
+ }
2416
+ auth() {
2417
+ return { "X-API-Key": this.httpClient.apiKey };
2418
+ }
2419
+ async allocation() {
2420
+ this.ensureCloud();
2421
+ return this.httpClient.get(
2422
+ "/v1/credits/allocation",
2423
+ void 0,
2424
+ this.auth()
2425
+ );
2426
+ }
2427
+ async usage(period = "current_month", options = {}) {
2428
+ this.ensureCloud();
2429
+ const params = { period, limit: options.limit ?? 100 };
2430
+ if (options.usageType !== void 0) params.usage_type = options.usageType;
2431
+ const result = await this.httpClient.get("/v1/credits/usage", params, this.auth());
2432
+ return Array.isArray(result) ? result : [];
2433
+ }
2434
+ async summary(period = "current_month") {
2435
+ this.ensureCloud();
2436
+ return this.httpClient.get(
2437
+ "/v1/credits/summary",
2438
+ { period },
2439
+ this.auth()
2440
+ );
2441
+ }
2442
+ async check(usageType, requiredCredits) {
2443
+ this.ensureCloud();
2444
+ return this.httpClient.get(
2445
+ "/v1/credits/check",
2446
+ { usage_type: usageType, required_credits: requiredCredits },
2447
+ this.auth()
2448
+ );
2449
+ }
2450
+ async rates() {
2451
+ this.ensureCloud();
2452
+ return this.httpClient.get(
2453
+ "/v1/credits/rates",
2454
+ void 0,
2455
+ this.auth()
2456
+ );
2457
+ }
2458
+ async usageTrends(period = "30d", granularity = "daily") {
2459
+ this.ensureCloud();
2460
+ return this.httpClient.get(
2461
+ "/v1/credits/usage/trends",
2462
+ { period, granularity },
2463
+ this.auth()
2464
+ );
2465
+ }
2466
+ async usageForecast() {
2467
+ this.ensureCloud();
2468
+ return this.httpClient.get(
2469
+ "/v1/credits/usage/forecasting",
2470
+ void 0,
2471
+ this.auth()
2472
+ );
2473
+ }
2474
+ };
2475
+
2476
+ // src/client/TapeContext.ts
2477
+ var TapeContext = class {
2478
+ constructor(tapes, sessionId, startOpts) {
2479
+ this.tapes = tapes;
2480
+ this.sessionId = sessionId;
2481
+ this.startOpts = startOpts;
2482
+ this.info = null;
2483
+ }
2484
+ get id() {
2485
+ return this.info?.id;
2486
+ }
2487
+ async start() {
2488
+ if (!this.sessionId) {
2489
+ throw new SessionError("Session ID not set. Please create a session first.");
2490
+ }
2491
+ this.info = await this.tapes.start(this.sessionId, this.startOpts);
2492
+ }
2493
+ async stop() {
2494
+ if (!this.info?.id) {
2495
+ return;
2496
+ }
2497
+ this.info = await this.tapes.stop(this.info.id);
2498
+ }
2499
+ async [Symbol.asyncDispose]() {
2500
+ await this.stop();
2501
+ }
2502
+ };
2503
+
2504
+ // src/client/ToolCallRecorder.ts
2505
+ var ToolCallRecorder = class {
2506
+ constructor(tapes, tapeId) {
2507
+ this.tapes = tapes;
2508
+ this.tapeId = tapeId;
2509
+ }
2510
+ async event(kind, payload, stepId) {
2511
+ return this.tapes.appendEvent(this.tapeId, kind, {
2512
+ payload: payload || {},
2513
+ stepId
2514
+ });
2515
+ }
2516
+ span(name, payload) {
2517
+ return new ToolCallSpan(this, name, payload || {});
2518
+ }
2519
+ };
2520
+ var ToolCallSpan = class {
2521
+ constructor(recorder, name, startPayload) {
2522
+ this.recorder = recorder;
2523
+ this.name = name;
2524
+ this.startPayload = startPayload;
2525
+ this.endPayload = {};
2526
+ }
2527
+ attach(payload) {
2528
+ Object.assign(this.endPayload, payload);
2529
+ }
2530
+ async enter() {
2531
+ const result = await this.recorder.event("tool_call_start", {
2532
+ name: this.name,
2533
+ ...this.startPayload
2534
+ });
2535
+ this.stepId = result.step_id;
2536
+ }
2537
+ async exit(err) {
2538
+ const payload = {
2539
+ name: this.name,
2540
+ ok: err === void 0 || err === null,
2541
+ ...this.endPayload
2542
+ };
2543
+ if (err) {
2544
+ payload.error = err instanceof Error ? err.message : String(err);
2545
+ }
2546
+ await this.recorder.event("tool_call_end", payload, this.stepId);
2547
+ }
2548
+ };
2549
+
2550
+ // src/client/InstaVM.ts
2551
+ var import_form_data2 = __toESM(require("form-data"));
2552
+ var InstaVM = class {
2553
+ constructor(apiKey, options = {}) {
2554
+ this._sessionId = null;
2555
+ this._vmUsed = false;
2556
+ this.local = options.local || false;
2557
+ this.timeout = options.timeout || 3e5;
2558
+ this.memory_mb = options.memory_mb;
2559
+ this.cpu_count = options.cpu_count;
2560
+ this.metadata = options.metadata;
2561
+ this.env = options.env;
2562
+ this._autoStartSession = options.auto_start_session !== false;
2563
+ if (!this.local && !apiKey) {
2564
+ throw new AuthenticationError("API key is required for cloud mode");
2565
+ }
2566
+ const config = {
2567
+ baseURL: this.local ? options.localURL || "http://coderunner.local:8222" : options.baseURL || "https://api.instavm.io",
2568
+ timeout: this.timeout,
2569
+ maxRetries: options.maxRetries || 3,
2570
+ retryDelay: options.retryDelay || 1e3,
2571
+ apiKey: apiKey || ""
2572
+ };
2573
+ this.httpClient = new HTTPClient(config);
2574
+ this.browser = new BrowserManager(this.httpClient, this.local, () => this);
2575
+ this.vms = new VMsManager(this.httpClient, this.local);
2576
+ this.snapshots = new SnapshotsManager(this.httpClient, this.local);
2577
+ this.shares = new SharesManager(this.httpClient, this.local, () => this._sessionId);
2578
+ this.customDomains = new CustomDomainsManager(this.httpClient, this.local);
2579
+ this.computerUse = new ComputerUseManager(this.httpClient, this.local);
2580
+ this.apiKeys = new APIKeysManager(this.httpClient, this.local);
2581
+ this.audit = new AuditManager(this.httpClient, this.local);
2582
+ this.webhooks = new WebhooksManager(this.httpClient, this.local);
2583
+ this.volumes = new VolumesManager(this.httpClient, this.local);
2584
+ this.tapes = new TapesManager(this.httpClient, this.local);
2585
+ this.vaults = new VaultsManager(this.httpClient, this.local);
2586
+ this.pty = new PTYManager(this.httpClient, this.local, () => this.httpClient.baseURL);
2587
+ this.recordings = new RecordingsManager(this.httpClient, this.local, () => this.httpClient.baseURL);
2588
+ this.credits = new CreditsManager(this.httpClient, this.local);
2589
+ if (this._autoStartSession && !this.local && apiKey) {
2590
+ this.sessionBootstrap = this.createSession();
2591
+ void this.sessionBootstrap.catch(() => void 0);
2592
+ }
2593
+ }
2594
+ async awaitSessionBootstrap() {
2595
+ if (this.sessionBootstrap) {
2596
+ try {
2597
+ await this.sessionBootstrap;
2598
+ } finally {
2599
+ this.sessionBootstrap = void 0;
2600
+ }
2601
+ }
2602
+ }
2603
+ /**
2604
+ * Ensure operation is not called in local mode
2605
+ */
2606
+ ensureNotLocal(operationName) {
2607
+ if (this.local) {
2608
+ throw new UnsupportedOperationError(
2609
+ `${operationName} is not supported in local mode. This operation is only available when using the cloud API.`
2610
+ );
2611
+ }
2612
+ }
2613
+ authHeaders() {
2614
+ return { "X-API-Key": this.httpClient.apiKey };
2615
+ }
2616
+ /**
2617
+ * Execute code synchronously
2618
+ *
2619
+ * @param command - Command to execute
2620
+ * @param options - Execution options
2621
+ * @param options.timeout - Request timeout in milliseconds (used for HTTP request timeout and sent to API in seconds)
2622
+ * @returns Execution result
2623
+ */
2624
+ async execute(command, options = {}) {
2625
+ if (!this.local) {
2626
+ await this.awaitSessionBootstrap();
2627
+ }
2628
+ let sessionId = options.sessionId || this._sessionId;
1740
2629
  if (!this.local && !sessionId) {
1741
2630
  sessionId = await this.createSession();
1742
2631
  }
@@ -1789,6 +2678,7 @@ var InstaVM = class {
1789
2678
  */
1790
2679
  async executeAsync(command, options = {}) {
1791
2680
  this.ensureNotLocal("Async execution");
2681
+ await this.awaitSessionBootstrap();
1792
2682
  let sessionId = options.sessionId || this._sessionId;
1793
2683
  if (!sessionId) {
1794
2684
  sessionId = await this.createSession();
@@ -1875,6 +2765,7 @@ var InstaVM = class {
1875
2765
  */
1876
2766
  async upload(files, options = {}) {
1877
2767
  this.ensureNotLocal("File upload");
2768
+ await this.awaitSessionBootstrap();
1878
2769
  const sessionId = options.sessionId || this._sessionId;
1879
2770
  if (!sessionId) {
1880
2771
  throw new SessionError("Session ID not set. Please create a session first using createSession().");
@@ -1941,7 +2832,7 @@ var InstaVM = class {
1941
2832
  "/v1/sessions/session",
1942
2833
  requestBody
1943
2834
  );
1944
- if (response.session_id) {
2835
+ if (response && response.session_id) {
1945
2836
  this._sessionId = response.session_id;
1946
2837
  return response.session_id;
1947
2838
  }
@@ -2007,10 +2898,11 @@ var InstaVM = class {
2007
2898
  if (port !== void 0) {
2008
2899
  params.port = port;
2009
2900
  }
2901
+ const safeSid = encodePathSegment(targetSessionId);
2010
2902
  return this.httpClient.get(
2011
- `/v1/sessions/app-url/${targetSessionId}`,
2903
+ `/v1/sessions/app-url/${safeSid}`,
2012
2904
  params,
2013
- { "X-API-Key": this.httpClient.apiKey }
2905
+ this.authHeaders()
2014
2906
  );
2015
2907
  }
2016
2908
  /**
@@ -2044,10 +2936,11 @@ var InstaVM = class {
2044
2936
  throw new SessionError("No active session to get usage for");
2045
2937
  }
2046
2938
  try {
2939
+ const safeSid = encodePathSegment(targetSessionId);
2047
2940
  const response = await this.httpClient.get(
2048
- `/v1/sessions/usage/${targetSessionId}`,
2941
+ `/v1/sessions/usage/${safeSid}`,
2049
2942
  void 0,
2050
- { "X-API-Key": this.httpClient.apiKey }
2943
+ this.authHeaders()
2051
2944
  );
2052
2945
  return {
2053
2946
  sessionsUsed: response.sessions_used || 0,
@@ -2069,7 +2962,7 @@ var InstaVM = class {
2069
2962
  async getCurrentUser() {
2070
2963
  this.ensureNotLocal("User profile lookup");
2071
2964
  return this.httpClient.get(
2072
- "/v1/me",
2965
+ "/v1/users/me",
2073
2966
  void 0,
2074
2967
  { "X-API-Key": this.httpClient.apiKey }
2075
2968
  );
@@ -2083,12 +2976,429 @@ var InstaVM = class {
2083
2976
  if (!targetSessionId) {
2084
2977
  throw new SessionError("Session ID not set. Please create a session first.");
2085
2978
  }
2979
+ const safeSid = encodePathSegment(targetSessionId);
2086
2980
  return this.httpClient.get(
2087
- `/v1/sessions/status/${targetSessionId}`,
2981
+ `/v1/sessions/status/${safeSid}`,
2088
2982
  void 0,
2089
- { "X-API-Key": this.httpClient.apiKey }
2983
+ this.authHeaders()
2090
2984
  );
2091
2985
  }
2986
+ /**
2987
+ * Get session record details from ``GET /v1/sessions/session/{session_id}`` (Python: ``get_session_info``).
2988
+ */
2989
+ async getSessionInfo(sessionId) {
2990
+ this.ensureNotLocal("Session management");
2991
+ const sid = sessionId || this._sessionId;
2992
+ if (!sid) {
2993
+ throw new SessionError("Session ID not set. Please create a session first.");
2994
+ }
2995
+ try {
2996
+ const safeSid = encodePathSegment(sid);
2997
+ const result = await this.httpClient.get(
2998
+ `/v1/sessions/session/${safeSid}`,
2999
+ void 0,
3000
+ this.authHeaders()
3001
+ );
3002
+ if (typeof result !== "object" || result === null) {
3003
+ throw new SessionError("Failed to get session info: non-JSON response");
3004
+ }
3005
+ return result;
3006
+ } catch (error) {
3007
+ if (error instanceof InstaVMError) {
3008
+ throw error;
3009
+ }
3010
+ const errorMessage = error instanceof Error ? error.message : String(error);
3011
+ throw new SessionError(`Failed to get session info: ${errorMessage}`, { cause: error });
3012
+ }
3013
+ }
3014
+ /**
3015
+ * Day-wise usage breakdown from ``GET /v1/users/me/usage-breakdown`` (Python: ``get_usage_breakdown``).
3016
+ */
3017
+ async getUsageBreakdown(options = {}) {
3018
+ this.ensureNotLocal("Usage breakdown lookup");
3019
+ const params = { days: options.days ?? 30 };
3020
+ if (options.startDate !== void 0) {
3021
+ params.start_date = options.startDate;
3022
+ }
3023
+ if (options.endDate !== void 0) {
3024
+ params.end_date = options.endDate;
3025
+ }
3026
+ try {
3027
+ const result = await this.httpClient.get(
3028
+ "/v1/users/me/usage-breakdown",
3029
+ params,
3030
+ this.authHeaders()
3031
+ );
3032
+ if (typeof result !== "object" || result === null) {
3033
+ throw new InstaVMError("Failed to fetch usage breakdown: non-JSON response");
3034
+ }
3035
+ return result;
3036
+ } catch (error) {
3037
+ if (error instanceof InstaVMError) {
3038
+ throw error;
3039
+ }
3040
+ const errorMessage = error instanceof Error ? error.message : String(error);
3041
+ throw new InstaVMError(`Failed to fetch usage breakdown: ${errorMessage}`, { cause: error });
3042
+ }
3043
+ }
3044
+ /**
3045
+ * Time-travel tape context manager (Python: ``tape()`` / ``TapeContext``).
3046
+ */
3047
+ tape(sessionId, options = {}) {
3048
+ const sid = sessionId ?? this._sessionId ?? void 0;
3049
+ return new TapeContext(this.tapes, sid, options);
3050
+ }
3051
+ /** Build a tool-call recorder for an active tape id (Python: ``ToolCallRecorder``). */
3052
+ toolCallRecorder(tapeId) {
3053
+ return new ToolCallRecorder(this.tapes, tapeId);
3054
+ }
3055
+ /**
3056
+ * @deprecated Streaming is not supported by the API; yields a single chunk from ``execute``.
3057
+ */
3058
+ async *executeStreaming(command, onOutput) {
3059
+ console.warn(
3060
+ "executeStreaming is deprecated. The API does not support streaming execution. Use execute() or executeAsync() instead."
3061
+ );
3062
+ const result = await this.execute(command);
3063
+ const output = result.stdout || "";
3064
+ if (onOutput) {
3065
+ onOutput(output);
3066
+ }
3067
+ yield output;
3068
+ }
3069
+ // --- Vault (flat API parity with Python InstaVM) ---
3070
+ async listVaults() {
3071
+ return this.vaults.list();
3072
+ }
3073
+ async createVault(name, description) {
3074
+ return this.vaults.create(name, description);
3075
+ }
3076
+ async getVault(vaultId) {
3077
+ return this.vaults.get(vaultId);
3078
+ }
3079
+ async updateVault(vaultId, updates) {
3080
+ return this.vaults.update(vaultId, updates);
3081
+ }
3082
+ async deleteVault(vaultId) {
3083
+ return this.vaults.delete(vaultId);
3084
+ }
3085
+ async discoverVault(vaultId) {
3086
+ return this.vaults.discover(vaultId);
3087
+ }
3088
+ async listVaultCredentials(vaultId) {
3089
+ return this.vaults.listCredentials(vaultId);
3090
+ }
3091
+ async addVaultCredential(vaultId, name, value, options = {}) {
3092
+ return this.vaults.addCredential(vaultId, name, value, options);
3093
+ }
3094
+ async rotateVaultCredential(vaultId, credentialId, value) {
3095
+ return this.vaults.rotateCredential(vaultId, credentialId, value);
3096
+ }
3097
+ async deleteVaultCredential(vaultId, credentialId) {
3098
+ return this.vaults.deleteCredential(vaultId, credentialId);
3099
+ }
3100
+ async listVaultServices(vaultId) {
3101
+ return this.vaults.listServices(vaultId);
3102
+ }
3103
+ async addVaultService(vaultId, host, authConfig, options = {}) {
3104
+ return this.vaults.addService(vaultId, host, authConfig, options);
3105
+ }
3106
+ async deleteVaultService(vaultId, serviceId) {
3107
+ return this.vaults.deleteService(vaultId, serviceId);
3108
+ }
3109
+ async addVaultServicesFromTemplates(vaultId, templateIds) {
3110
+ return this.vaults.addServicesFromTemplates(vaultId, templateIds);
3111
+ }
3112
+ async getVaultCatalog() {
3113
+ return this.vaults.getCatalog();
3114
+ }
3115
+ async getVaultRequestLogs(vaultId, limit = 100) {
3116
+ return this.vaults.getRequestLogs(vaultId, limit);
3117
+ }
3118
+ // --- Browser (client-level parity with Python InstaVM) ---
3119
+ async createBrowserSession(viewportWidth = 1920, viewportHeight = 1080, userAgent) {
3120
+ this.ensureNotLocal("Browser session management");
3121
+ const body = {
3122
+ viewport_width: viewportWidth,
3123
+ viewport_height: viewportHeight
3124
+ };
3125
+ if (userAgent) {
3126
+ body.user_agent = userAgent;
3127
+ }
3128
+ try {
3129
+ const result = await this.httpClient.post(
3130
+ "/v1/browser/sessions/",
3131
+ body,
3132
+ this.authHeaders()
3133
+ );
3134
+ const sid = result.session_id;
3135
+ if (!sid) {
3136
+ throw new BrowserSessionError("No session ID returned from server");
3137
+ }
3138
+ return sid;
3139
+ } catch (error) {
3140
+ const msg = error instanceof Error ? error.message : String(error);
3141
+ throw new BrowserSessionError(`Failed to create browser session: ${msg}`, { cause: error });
3142
+ }
3143
+ }
3144
+ async getBrowserSession(sessionId) {
3145
+ this.ensureNotLocal("Browser session management");
3146
+ const safe = encodePathSegment(sessionId);
3147
+ try {
3148
+ return await this.httpClient.get(
3149
+ `/v1/browser/sessions/${safe}`,
3150
+ void 0,
3151
+ this.authHeaders()
3152
+ );
3153
+ } catch (error) {
3154
+ const status = error.statusCode;
3155
+ if (status === 404) {
3156
+ throw new BrowserSessionError("Browser session not found or expired", { cause: error });
3157
+ }
3158
+ const msg = error instanceof Error ? error.message : String(error);
3159
+ throw new BrowserSessionError(`Failed to get browser session: ${msg}`, { cause: error });
3160
+ }
3161
+ }
3162
+ async closeBrowserSession(sessionId) {
3163
+ this.ensureNotLocal("Browser session management");
3164
+ const safe = encodePathSegment(sessionId);
3165
+ try {
3166
+ await this.httpClient.delete(`/v1/browser/sessions/${safe}`, this.authHeaders());
3167
+ return true;
3168
+ } catch (error) {
3169
+ const status = error.statusCode;
3170
+ if (status === 404) {
3171
+ return true;
3172
+ }
3173
+ const msg = error instanceof Error ? error.message : String(error);
3174
+ throw new BrowserSessionError(`Failed to close browser session: ${msg}`, { cause: error });
3175
+ }
3176
+ }
3177
+ async listBrowserSessions() {
3178
+ this.ensureNotLocal("Browser session management");
3179
+ const result = await this.httpClient.get("/v1/browser/sessions/", void 0, this.authHeaders());
3180
+ if (Array.isArray(result)) {
3181
+ return result;
3182
+ }
3183
+ return result?.sessions || [];
3184
+ }
3185
+ async browserNavigate(url, sessionId, waitTimeout = 3e4) {
3186
+ if (!this.local) {
3187
+ if (!sessionId) {
3188
+ throw new BrowserSessionError("session_id is required in cloud mode");
3189
+ }
3190
+ }
3191
+ const data = { url, wait_timeout: waitTimeout };
3192
+ if (!this.local && sessionId) {
3193
+ data.session_id = sessionId;
3194
+ }
3195
+ try {
3196
+ return await this.httpClient.post(
3197
+ "/v1/browser/interactions/navigate",
3198
+ data,
3199
+ this.authHeaders()
3200
+ );
3201
+ } catch (error) {
3202
+ const status = error.statusCode;
3203
+ if (status === 404) {
3204
+ throw new BrowserSessionError("Browser session not found or expired", { cause: error });
3205
+ }
3206
+ if (status === 408) {
3207
+ throw new BrowserTimeoutError("Navigation timeout", { cause: error });
3208
+ }
3209
+ const msg = error instanceof Error ? error.message : String(error);
3210
+ throw new BrowserNavigationError(`Failed to navigate: ${msg}`, { cause: error });
3211
+ }
3212
+ }
3213
+ async browserClick(selector, sessionId, force = false, timeout = 3e4) {
3214
+ this.ensureNotLocal("Browser click interaction");
3215
+ try {
3216
+ return await this.httpClient.post(
3217
+ "/v1/browser/interactions/click",
3218
+ { selector, session_id: sessionId, force, timeout },
3219
+ this.authHeaders()
3220
+ );
3221
+ } catch (error) {
3222
+ const status = error.statusCode;
3223
+ if (status === 404) {
3224
+ throw new ElementNotFoundError(`Element not found: ${selector}`, selector, { cause: error });
3225
+ }
3226
+ if (status === 408) {
3227
+ throw new BrowserTimeoutError("Click timeout", { cause: error });
3228
+ }
3229
+ const msg = error instanceof Error ? error.message : String(error);
3230
+ throw new BrowserInteractionError(`Failed to click element: ${msg}`, { cause: error });
3231
+ }
3232
+ }
3233
+ async browserType(selector, text, sessionId, delay = 100, timeout = 3e4) {
3234
+ this.ensureNotLocal("Browser type interaction");
3235
+ try {
3236
+ return await this.httpClient.post(
3237
+ "/v1/browser/interactions/type",
3238
+ { selector, text, session_id: sessionId, delay, timeout },
3239
+ this.authHeaders()
3240
+ );
3241
+ } catch (error) {
3242
+ const status = error.statusCode;
3243
+ if (status === 404) {
3244
+ throw new ElementNotFoundError(`Element not found: ${selector}`, selector, { cause: error });
3245
+ }
3246
+ if (status === 408) {
3247
+ throw new BrowserTimeoutError("Type timeout", { cause: error });
3248
+ }
3249
+ const msg = error instanceof Error ? error.message : String(error);
3250
+ throw new BrowserInteractionError(`Failed to type text: ${msg}`, { cause: error });
3251
+ }
3252
+ }
3253
+ async browserFill(selector, value, sessionId, timeout = 3e4) {
3254
+ this.ensureNotLocal("Browser fill interaction");
3255
+ try {
3256
+ return await this.httpClient.post(
3257
+ "/v1/browser/interactions/fill",
3258
+ { selector, value, session_id: sessionId, timeout },
3259
+ this.authHeaders()
3260
+ );
3261
+ } catch (error) {
3262
+ const status = error.statusCode;
3263
+ if (status === 404) {
3264
+ throw new ElementNotFoundError(`Element not found: ${selector}`, selector, { cause: error });
3265
+ }
3266
+ if (status === 408) {
3267
+ throw new BrowserTimeoutError("Fill timeout", { cause: error });
3268
+ }
3269
+ const msg = error instanceof Error ? error.message : String(error);
3270
+ throw new BrowserInteractionError(`Failed to fill form: ${msg}`, { cause: error });
3271
+ }
3272
+ }
3273
+ async browserScroll(sessionId, selector, x, y) {
3274
+ this.ensureNotLocal("Browser scroll interaction");
3275
+ if (selector == null && x == null && y == null) {
3276
+ throw new BrowserInteractionError(
3277
+ "At least one of 'selector', 'x', or 'y' must be provided for scrolling."
3278
+ );
3279
+ }
3280
+ const data = { session_id: sessionId };
3281
+ if (selector) {
3282
+ data.selector = selector;
3283
+ }
3284
+ if (x != null) {
3285
+ data.x = x;
3286
+ }
3287
+ if (y != null) {
3288
+ data.y = y;
3289
+ }
3290
+ try {
3291
+ return await this.httpClient.post(
3292
+ "/v1/browser/interactions/scroll",
3293
+ data,
3294
+ this.authHeaders()
3295
+ );
3296
+ } catch (error) {
3297
+ const msg = error instanceof Error ? error.message : String(error);
3298
+ throw new BrowserInteractionError(`Failed to scroll: ${msg}`, { cause: error });
3299
+ }
3300
+ }
3301
+ async browserWait(condition, sessionId, selector, timeout = 3e4) {
3302
+ this.ensureNotLocal("Browser wait interaction");
3303
+ const data = { condition, session_id: sessionId, timeout };
3304
+ if (selector) {
3305
+ data.selector = selector;
3306
+ }
3307
+ try {
3308
+ return await this.httpClient.post(
3309
+ "/v1/browser/interactions/wait",
3310
+ data,
3311
+ this.authHeaders()
3312
+ );
3313
+ } catch (error) {
3314
+ const status = error.statusCode;
3315
+ if (status === 408) {
3316
+ throw new BrowserTimeoutError(`Wait timeout for condition: ${condition}`, { cause: error });
3317
+ }
3318
+ const msg = error instanceof Error ? error.message : String(error);
3319
+ throw new BrowserInteractionError(`Failed to wait for condition: ${msg}`, { cause: error });
3320
+ }
3321
+ }
3322
+ async browserScreenshot(sessionId, fullPage = true, clip, format = "png", quality) {
3323
+ this.ensureNotLocal("Browser screenshot");
3324
+ const data = {
3325
+ session_id: sessionId,
3326
+ full_page: fullPage,
3327
+ format
3328
+ };
3329
+ if (clip) {
3330
+ data.clip = clip;
3331
+ }
3332
+ if (quality != null) {
3333
+ data.quality = quality;
3334
+ }
3335
+ try {
3336
+ const result = await this.httpClient.post(
3337
+ "/v1/browser/interactions/screenshot",
3338
+ data,
3339
+ this.authHeaders()
3340
+ );
3341
+ return result.screenshot || "";
3342
+ } catch (error) {
3343
+ const msg = error instanceof Error ? error.message : String(error);
3344
+ throw new BrowserInteractionError(`Failed to take screenshot: ${msg}`, { cause: error });
3345
+ }
3346
+ }
3347
+ async browserExtractElements(sessionId, selector, attributes) {
3348
+ this.ensureNotLocal("Browser element extraction");
3349
+ const data = { session_id: sessionId };
3350
+ if (selector) {
3351
+ data.selector = selector;
3352
+ }
3353
+ if (attributes?.length) {
3354
+ data.attributes = attributes;
3355
+ }
3356
+ try {
3357
+ const result = await this.httpClient.post(
3358
+ "/v1/browser/interactions/extract",
3359
+ data,
3360
+ this.authHeaders()
3361
+ );
3362
+ return result.elements || [];
3363
+ } catch (error) {
3364
+ const msg = error instanceof Error ? error.message : String(error);
3365
+ throw new BrowserInteractionError(`Failed to extract elements: ${msg}`, { cause: error });
3366
+ }
3367
+ }
3368
+ async browserExtractContent(sessionId, url, includeInteractive = true, includeAnchors = true, maxAnchors = 50) {
3369
+ if (!this.local) {
3370
+ if (!sessionId) {
3371
+ throw new BrowserSessionError("session_id is required in cloud mode");
3372
+ }
3373
+ } else if (!url) {
3374
+ throw new BrowserInteractionError("url is required in local mode");
3375
+ }
3376
+ const data = {
3377
+ include_interactive: includeInteractive,
3378
+ include_anchors: includeAnchors,
3379
+ max_anchors: maxAnchors
3380
+ };
3381
+ if (!this.local && sessionId) {
3382
+ data.session_id = sessionId;
3383
+ }
3384
+ if (this.local && url) {
3385
+ data.url = url;
3386
+ }
3387
+ try {
3388
+ return await this.httpClient.post(
3389
+ "/v1/browser/interactions/content",
3390
+ data,
3391
+ this.authHeaders()
3392
+ );
3393
+ } catch (error) {
3394
+ const status = error.statusCode;
3395
+ if (status === 404) {
3396
+ throw new BrowserSessionError("Browser session not found or expired", { cause: error });
3397
+ }
3398
+ const msg = error instanceof Error ? error.message : String(error);
3399
+ throw new BrowserInteractionError(`Failed to extract content: ${msg}`, { cause: error });
3400
+ }
3401
+ }
2092
3402
  /**
2093
3403
  * Download a file from the remote VM
2094
3404
  */
@@ -2174,8 +3484,9 @@ var InstaVM = class {
2174
3484
  if (!targetSessionId) {
2175
3485
  throw new SessionError("Session ID not set. Please create a session first.");
2176
3486
  }
3487
+ const safeSid = encodePathSegment(targetSessionId);
2177
3488
  const response = await this.httpClient.post(
2178
- `/v1/egress/session/${targetSessionId}`,
3489
+ `/v1/egress/session/${safeSid}`,
2179
3490
  {
2180
3491
  allow_public_repos: options.allowPackageManagers ?? true,
2181
3492
  allow_http: options.allowHttp ?? true,
@@ -2196,8 +3507,9 @@ var InstaVM = class {
2196
3507
  if (!targetSessionId) {
2197
3508
  throw new SessionError("Session ID not set. Please create a session first.");
2198
3509
  }
3510
+ const safeSid = encodePathSegment(targetSessionId);
2199
3511
  const response = await this.httpClient.get(
2200
- `/v1/egress/session/${targetSessionId}`,
3512
+ `/v1/egress/session/${safeSid}`,
2201
3513
  void 0,
2202
3514
  { "X-API-Key": this.httpClient.apiKey }
2203
3515
  );
@@ -2214,8 +3526,9 @@ var InstaVM = class {
2214
3526
  */
2215
3527
  async setVmEgress(vmId, options = {}) {
2216
3528
  this.ensureNotLocal("Egress policy management");
3529
+ const safeVmId = encodePathSegment(vmId);
2217
3530
  const response = await this.httpClient.post(
2218
- `/v1/egress/vm/${vmId}`,
3531
+ `/v1/egress/vm/${safeVmId}`,
2219
3532
  {
2220
3533
  allow_public_repos: options.allowPackageManagers ?? true,
2221
3534
  allow_http: options.allowHttp ?? true,
@@ -2232,8 +3545,9 @@ var InstaVM = class {
2232
3545
  */
2233
3546
  async getVmEgress(vmId) {
2234
3547
  this.ensureNotLocal("Egress policy management");
3548
+ const safeVmId = encodePathSegment(vmId);
2235
3549
  const response = await this.httpClient.get(
2236
- `/v1/egress/vm/${vmId}`,
3550
+ `/v1/egress/vm/${safeVmId}`,
2237
3551
  void 0,
2238
3552
  { "X-API-Key": this.httpClient.apiKey }
2239
3553
  );
@@ -2322,19 +3636,29 @@ var InstaVM = class {
2322
3636
  BrowserSessionError,
2323
3637
  BrowserTimeoutError,
2324
3638
  ComputerUseManager,
3639
+ CreditsManager,
2325
3640
  CustomDomainsManager,
2326
3641
  ElementNotFoundError,
2327
3642
  ExecutionError,
3643
+ ForbiddenError,
3644
+ INSTAVM_JS_SDK_VERSION,
2328
3645
  InstaVM,
2329
3646
  InstaVMError,
2330
3647
  NetworkError,
3648
+ PTYManager,
2331
3649
  QuotaExceededError,
2332
3650
  RateLimitError,
3651
+ RecordingsManager,
2333
3652
  SessionError,
2334
3653
  SharesManager,
2335
3654
  SnapshotsManager,
3655
+ TapeContext,
3656
+ TapesManager,
3657
+ ToolCallRecorder,
3658
+ ToolCallSpan,
2336
3659
  UnsupportedOperationError,
2337
3660
  VMsManager,
3661
+ VaultsManager,
2338
3662
  VolumesManager,
2339
3663
  WebhooksManager
2340
3664
  });