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.
- package/README.md +133 -9
- package/dist/InstaVM-DjkmUcaP.d.mts +1393 -0
- package/dist/InstaVM-DjkmUcaP.d.ts +1393 -0
- package/dist/_instavmToolsCore-34H4iqVZ.d.mts +26 -0
- package/dist/_instavmToolsCore-BuaJyxXB.d.ts +26 -0
- package/dist/cli.js +7973 -2227
- package/dist/cli.js.map +1 -1
- package/dist/index.d.mts +12 -919
- package/dist/index.d.ts +12 -919
- package/dist/index.js +1465 -141
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1450 -136
- package/dist/index.mjs.map +1 -1
- package/dist/integrations/azure-openai.d.mts +18 -0
- package/dist/integrations/azure-openai.d.ts +18 -0
- package/dist/integrations/azure-openai.js +332 -0
- package/dist/integrations/azure-openai.js.map +1 -0
- package/dist/integrations/azure-openai.mjs +299 -0
- package/dist/integrations/azure-openai.mjs.map +1 -0
- package/dist/integrations/langchain.d.mts +7 -0
- package/dist/integrations/langchain.d.ts +7 -0
- package/dist/integrations/langchain.js +364 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/langchain.mjs +327 -0
- package/dist/integrations/langchain.mjs.map +1 -0
- package/dist/integrations/llamaindex.d.mts +11 -0
- package/dist/integrations/llamaindex.d.ts +11 -0
- package/dist/integrations/llamaindex.js +415 -0
- package/dist/integrations/llamaindex.js.map +1 -0
- package/dist/integrations/llamaindex.mjs +378 -0
- package/dist/integrations/llamaindex.mjs.map +1 -0
- package/dist/integrations/ollama.d.mts +35 -0
- package/dist/integrations/ollama.d.ts +35 -0
- package/dist/integrations/ollama.js +421 -0
- package/dist/integrations/ollama.js.map +1 -0
- package/dist/integrations/ollama.mjs +391 -0
- package/dist/integrations/ollama.mjs.map +1 -0
- package/dist/integrations/openai.d.mts +19 -0
- package/dist/integrations/openai.d.ts +19 -0
- package/dist/integrations/openai.js +302 -0
- package/dist/integrations/openai.js.map +1 -0
- package/dist/integrations/openai.mjs +272 -0
- package/dist/integrations/openai.mjs.map +1 -0
- package/package.json +46 -4
- package/dist/integrations/openai/index.d.mts +0 -16
- package/dist/integrations/openai/index.d.ts +0 -16
- package/dist/integrations/openai/index.js +0 -38
- package/dist/integrations/openai/index.js.map +0 -1
- package/dist/integrations/openai/index.mjs +0 -12
- 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
|
|
32
|
-
__export(
|
|
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(
|
|
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":
|
|
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
|
-
|
|
818
|
-
|
|
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
|
|
828
|
-
|
|
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
|
-
|
|
944
|
+
const sessions = Array.isArray(response) ? response : response?.sessions;
|
|
945
|
+
if (!Array.isArray(sessions)) {
|
|
853
946
|
return [];
|
|
854
947
|
}
|
|
855
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
899
|
-
|
|
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
|
-
|
|
921
|
-
|
|
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
|
|
1101
|
+
* Extract LLM-friendly content (local: URL required; cloud: optional session)
|
|
927
1102
|
*/
|
|
928
1103
|
async extractContent(options = {}) {
|
|
929
1104
|
if (!this.local) {
|
|
930
|
-
|
|
931
|
-
|
|
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
|
-
*
|
|
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
|
|
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 ||
|
|
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/
|
|
1687
|
-
var
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
this.
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
this.
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
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
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
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
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
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
|
-
|
|
2019
|
+
"Vault management is not supported in local mode."
|
|
1727
2020
|
);
|
|
1728
2021
|
}
|
|
1729
2022
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
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/${
|
|
2903
|
+
`/v1/sessions/app-url/${safeSid}`,
|
|
2012
2904
|
params,
|
|
2013
|
-
|
|
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/${
|
|
2941
|
+
`/v1/sessions/usage/${safeSid}`,
|
|
2049
2942
|
void 0,
|
|
2050
|
-
|
|
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/${
|
|
2981
|
+
`/v1/sessions/status/${safeSid}`,
|
|
2088
2982
|
void 0,
|
|
2089
|
-
|
|
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/${
|
|
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/${
|
|
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/${
|
|
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/${
|
|
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
|
});
|