instavm 0.16.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 +86 -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 +5821 -2296
- 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 +1464 -140
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1449 -135
- 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 +44 -3
- 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.mjs
CHANGED
|
@@ -31,6 +31,11 @@ var QuotaExceededError = class extends InstaVMError {
|
|
|
31
31
|
super(message, { ...options, statusCode: 402 });
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
+
var ForbiddenError = class extends InstaVMError {
|
|
35
|
+
constructor(message = "Forbidden", options) {
|
|
36
|
+
super(message, { ...options, statusCode: 403 });
|
|
37
|
+
}
|
|
38
|
+
};
|
|
34
39
|
var NetworkError = class extends InstaVMError {
|
|
35
40
|
constructor(message = "Network error", options) {
|
|
36
41
|
super(message, options);
|
|
@@ -125,11 +130,17 @@ async function withRetry(fn, options) {
|
|
|
125
130
|
throw lastError;
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
// src/version.ts
|
|
134
|
+
var INSTAVM_JS_SDK_VERSION = "0.17.0";
|
|
135
|
+
|
|
128
136
|
// src/client/HTTPClient.ts
|
|
129
137
|
var HTTPClient = class {
|
|
130
138
|
get apiKey() {
|
|
131
139
|
return this.config.apiKey;
|
|
132
140
|
}
|
|
141
|
+
get baseURL() {
|
|
142
|
+
return this.config.baseURL;
|
|
143
|
+
}
|
|
133
144
|
constructor(config) {
|
|
134
145
|
this.config = config;
|
|
135
146
|
this.client = axios.create({
|
|
@@ -137,7 +148,7 @@ var HTTPClient = class {
|
|
|
137
148
|
timeout: config.timeout,
|
|
138
149
|
headers: {
|
|
139
150
|
"Content-Type": "application/json",
|
|
140
|
-
"User-Agent":
|
|
151
|
+
"User-Agent": `instavm-js-sdk/${INSTAVM_JS_SDK_VERSION}`
|
|
141
152
|
}
|
|
142
153
|
});
|
|
143
154
|
this.setupInterceptors();
|
|
@@ -170,6 +181,11 @@ var HTTPClient = class {
|
|
|
170
181
|
statusCode: status,
|
|
171
182
|
response: data
|
|
172
183
|
});
|
|
184
|
+
case 403:
|
|
185
|
+
throw new ForbiddenError(message, {
|
|
186
|
+
statusCode: status,
|
|
187
|
+
response: data
|
|
188
|
+
});
|
|
173
189
|
case 429: {
|
|
174
190
|
const retryAfter = parseInt(
|
|
175
191
|
axiosError.response?.headers["retry-after"] || "60"
|
|
@@ -523,12 +539,24 @@ var BrowserSession = class extends EventEmitter {
|
|
|
523
539
|
*/
|
|
524
540
|
async scroll(options = {}) {
|
|
525
541
|
this.ensureActive();
|
|
542
|
+
if (!options.selector && options.x == null && options.y == null) {
|
|
543
|
+
throw new BrowserInteractionError(
|
|
544
|
+
"At least one of 'selector', 'x', or 'y' must be provided for scrolling."
|
|
545
|
+
);
|
|
546
|
+
}
|
|
526
547
|
const requestData = {
|
|
527
548
|
session_id: this.sessionId,
|
|
528
|
-
x: options.x || 0,
|
|
529
|
-
y: options.y || 500,
|
|
530
549
|
behavior: options.behavior || "auto"
|
|
531
550
|
};
|
|
551
|
+
if (options.selector) {
|
|
552
|
+
requestData.selector = options.selector;
|
|
553
|
+
}
|
|
554
|
+
if (options.x != null) {
|
|
555
|
+
requestData.x = options.x;
|
|
556
|
+
}
|
|
557
|
+
if (options.y != null) {
|
|
558
|
+
requestData.y = options.y;
|
|
559
|
+
}
|
|
532
560
|
try {
|
|
533
561
|
await this.httpClient.post(
|
|
534
562
|
"/v1/browser/interactions/scroll",
|
|
@@ -575,11 +603,13 @@ var BrowserSession = class extends EventEmitter {
|
|
|
575
603
|
async extractElements(selector, attributes = ["text"], options = {}) {
|
|
576
604
|
this.ensureActive();
|
|
577
605
|
const requestData = {
|
|
578
|
-
selector,
|
|
579
606
|
attributes,
|
|
580
607
|
session_id: this.sessionId,
|
|
581
608
|
max_results: options.maxResults || 100
|
|
582
609
|
};
|
|
610
|
+
if (selector) {
|
|
611
|
+
requestData.selector = selector;
|
|
612
|
+
}
|
|
583
613
|
try {
|
|
584
614
|
const response = await this.httpClient.post(
|
|
585
615
|
"/v1/browser/interactions/extract",
|
|
@@ -646,6 +676,29 @@ var BrowserSession = class extends EventEmitter {
|
|
|
646
676
|
);
|
|
647
677
|
}
|
|
648
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* Wait for a string condition (parity with Python ``wait_for``).
|
|
681
|
+
*/
|
|
682
|
+
async waitFor(condition, selector, timeout = 3e4) {
|
|
683
|
+
this.ensureActive();
|
|
684
|
+
const requestData = {
|
|
685
|
+
session_id: this.sessionId,
|
|
686
|
+
condition,
|
|
687
|
+
timeout
|
|
688
|
+
};
|
|
689
|
+
if (selector) {
|
|
690
|
+
requestData.selector = selector;
|
|
691
|
+
}
|
|
692
|
+
try {
|
|
693
|
+
return await this.httpClient.post("/v1/browser/interactions/wait", requestData);
|
|
694
|
+
} catch (error) {
|
|
695
|
+
const errorMessage = getErrorMessage(error);
|
|
696
|
+
if (errorMessage.includes("timeout")) {
|
|
697
|
+
throw new BrowserTimeoutError(`Wait condition timeout: ${condition}`, { cause: error });
|
|
698
|
+
}
|
|
699
|
+
throw new BrowserInteractionError(`Wait failed: ${errorMessage}`, { cause: error });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
649
702
|
/**
|
|
650
703
|
* Wait for a condition
|
|
651
704
|
*/
|
|
@@ -715,12 +768,45 @@ var BrowserSession = class extends EventEmitter {
|
|
|
715
768
|
}
|
|
716
769
|
};
|
|
717
770
|
|
|
771
|
+
// src/utils/path.ts
|
|
772
|
+
function encodePathSegment(value) {
|
|
773
|
+
const segment = String(value);
|
|
774
|
+
if (segment === "." || segment === "..") {
|
|
775
|
+
throw new Error("Path traversal not allowed in path segment");
|
|
776
|
+
}
|
|
777
|
+
return encodeURIComponent(segment);
|
|
778
|
+
}
|
|
779
|
+
function encodePathSegments(path2) {
|
|
780
|
+
const segments = path2.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
|
|
781
|
+
if (segments.some((segment) => segment === "..")) {
|
|
782
|
+
throw new Error("Path traversal not allowed in proxy path");
|
|
783
|
+
}
|
|
784
|
+
return segments.map((segment) => encodeURIComponent(segment)).join("/");
|
|
785
|
+
}
|
|
786
|
+
|
|
718
787
|
// src/client/BrowserManager.ts
|
|
719
788
|
var BrowserManager = class {
|
|
720
|
-
constructor(httpClient, local = false) {
|
|
789
|
+
constructor(httpClient, local = false, getClient) {
|
|
721
790
|
this.activeSessions = /* @__PURE__ */ new Map();
|
|
791
|
+
this._defaultBrowserSessionId = null;
|
|
792
|
+
this._defaultSessionPromise = null;
|
|
722
793
|
this.httpClient = httpClient;
|
|
723
794
|
this.local = local;
|
|
795
|
+
this.getClient = getClient;
|
|
796
|
+
}
|
|
797
|
+
async ensureDefaultBrowserSessionId() {
|
|
798
|
+
if (this._defaultBrowserSessionId) {
|
|
799
|
+
return this._defaultBrowserSessionId;
|
|
800
|
+
}
|
|
801
|
+
if (!this._defaultSessionPromise) {
|
|
802
|
+
this._defaultSessionPromise = this.createSession().catch((err) => {
|
|
803
|
+
this._defaultSessionPromise = null;
|
|
804
|
+
throw err;
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
const session = await this._defaultSessionPromise;
|
|
808
|
+
this._defaultBrowserSessionId = session.sessionId;
|
|
809
|
+
return this._defaultBrowserSessionId;
|
|
724
810
|
}
|
|
725
811
|
/**
|
|
726
812
|
* Create a new browser session
|
|
@@ -737,10 +823,7 @@ var BrowserManager = class {
|
|
|
737
823
|
user_agent: options.userAgent
|
|
738
824
|
};
|
|
739
825
|
try {
|
|
740
|
-
const response = await this.httpClient.post(
|
|
741
|
-
"/v1/browser/sessions/",
|
|
742
|
-
requestData
|
|
743
|
-
);
|
|
826
|
+
const response = await this.httpClient.post("/v1/browser/sessions/", requestData);
|
|
744
827
|
if (!response.session_id) {
|
|
745
828
|
throw new BrowserSessionError("No session ID returned from server");
|
|
746
829
|
}
|
|
@@ -748,14 +831,17 @@ var BrowserManager = class {
|
|
|
748
831
|
this.activeSessions.set(response.session_id, session);
|
|
749
832
|
session.on("close", () => {
|
|
750
833
|
this.activeSessions.delete(response.session_id);
|
|
834
|
+
if (this._defaultBrowserSessionId === response.session_id) {
|
|
835
|
+
this._defaultBrowserSessionId = null;
|
|
836
|
+
this._defaultSessionPromise = null;
|
|
837
|
+
}
|
|
751
838
|
});
|
|
752
839
|
return session;
|
|
753
840
|
} catch (error) {
|
|
754
841
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
755
|
-
throw new BrowserSessionError(
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
);
|
|
842
|
+
throw new BrowserSessionError(`Failed to create browser session: ${errorMessage}`, {
|
|
843
|
+
cause: error
|
|
844
|
+
});
|
|
759
845
|
}
|
|
760
846
|
}
|
|
761
847
|
/**
|
|
@@ -763,9 +849,8 @@ var BrowserManager = class {
|
|
|
763
849
|
*/
|
|
764
850
|
async getSession(sessionId) {
|
|
765
851
|
try {
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
);
|
|
852
|
+
const safe = encodePathSegment(sessionId);
|
|
853
|
+
const response = await this.httpClient.get(`/v1/browser/sessions/${safe}`);
|
|
769
854
|
return {
|
|
770
855
|
sessionId: response.session_id,
|
|
771
856
|
status: response.status || "active",
|
|
@@ -776,10 +861,7 @@ var BrowserManager = class {
|
|
|
776
861
|
};
|
|
777
862
|
} catch (error) {
|
|
778
863
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
779
|
-
throw new BrowserSessionError(
|
|
780
|
-
`Failed to get session info: ${errorMessage}`,
|
|
781
|
-
{ cause: error }
|
|
782
|
-
);
|
|
864
|
+
throw new BrowserSessionError(`Failed to get session info: ${errorMessage}`, { cause: error });
|
|
783
865
|
}
|
|
784
866
|
}
|
|
785
867
|
/**
|
|
@@ -788,10 +870,11 @@ var BrowserManager = class {
|
|
|
788
870
|
async listSessions() {
|
|
789
871
|
try {
|
|
790
872
|
const response = await this.httpClient.get("/v1/browser/sessions/");
|
|
791
|
-
|
|
873
|
+
const sessions = Array.isArray(response) ? response : response?.sessions;
|
|
874
|
+
if (!Array.isArray(sessions)) {
|
|
792
875
|
return [];
|
|
793
876
|
}
|
|
794
|
-
return
|
|
877
|
+
return sessions.map((session) => ({
|
|
795
878
|
sessionId: session.session_id,
|
|
796
879
|
status: session.status || "active",
|
|
797
880
|
createdAt: session.created_at,
|
|
@@ -801,52 +884,62 @@ var BrowserManager = class {
|
|
|
801
884
|
}));
|
|
802
885
|
} catch (error) {
|
|
803
886
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
804
|
-
throw new BrowserSessionError(
|
|
805
|
-
`Failed to list sessions: ${errorMessage}`,
|
|
806
|
-
{ cause: error }
|
|
807
|
-
);
|
|
887
|
+
throw new BrowserSessionError(`Failed to list sessions: ${errorMessage}`, { cause: error });
|
|
808
888
|
}
|
|
809
889
|
}
|
|
810
|
-
/**
|
|
811
|
-
* Get a locally tracked session
|
|
812
|
-
*/
|
|
813
890
|
getLocalSession(sessionId) {
|
|
814
891
|
return this.activeSessions.get(sessionId);
|
|
815
892
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
893
|
+
attachSession(sessionId) {
|
|
894
|
+
const existing = this.activeSessions.get(sessionId);
|
|
895
|
+
if (existing) {
|
|
896
|
+
return existing;
|
|
897
|
+
}
|
|
898
|
+
const session = new BrowserSession(sessionId, this.httpClient);
|
|
899
|
+
this.activeSessions.set(sessionId, session);
|
|
900
|
+
session.on("close", () => {
|
|
901
|
+
this.activeSessions.delete(sessionId);
|
|
902
|
+
if (this._defaultBrowserSessionId === sessionId) {
|
|
903
|
+
this._defaultBrowserSessionId = null;
|
|
904
|
+
this._defaultSessionPromise = null;
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
return session;
|
|
908
|
+
}
|
|
819
909
|
getLocalSessions() {
|
|
820
910
|
return Array.from(this.activeSessions.values());
|
|
821
911
|
}
|
|
822
|
-
/**
|
|
823
|
-
* Close all active sessions
|
|
824
|
-
*/
|
|
825
912
|
async closeAllSessions() {
|
|
826
913
|
const sessions = Array.from(this.activeSessions.values());
|
|
827
|
-
await Promise.allSettled(
|
|
828
|
-
sessions.map((session) => session.close())
|
|
829
|
-
);
|
|
914
|
+
await Promise.allSettled(sessions.map((session) => session.close()));
|
|
830
915
|
this.activeSessions.clear();
|
|
916
|
+
this._defaultBrowserSessionId = null;
|
|
917
|
+
this._defaultSessionPromise = null;
|
|
831
918
|
}
|
|
832
919
|
/**
|
|
833
|
-
* Navigate to a URL (local
|
|
920
|
+
* Navigate to a URL (local: no session; cloud: auto default browser session)
|
|
834
921
|
*/
|
|
835
922
|
async navigate(url, options = {}) {
|
|
836
923
|
if (!this.local) {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
924
|
+
const client = this.getClient?.();
|
|
925
|
+
if (!client) {
|
|
926
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
927
|
+
}
|
|
928
|
+
const sid = await this.ensureDefaultBrowserSessionId();
|
|
929
|
+
const raw = await client.browserNavigate(url, sid, options.waitTimeout ?? 3e4);
|
|
930
|
+
return {
|
|
931
|
+
success: raw.success !== false,
|
|
932
|
+
url: raw.url || url,
|
|
933
|
+
title: raw.title,
|
|
934
|
+
status: raw.status
|
|
935
|
+
};
|
|
840
936
|
}
|
|
841
937
|
const requestData = {
|
|
842
938
|
url,
|
|
843
939
|
wait_timeout: options.waitTimeout || 3e4
|
|
844
940
|
};
|
|
845
941
|
try {
|
|
846
|
-
const response = await this.httpClient.post(
|
|
847
|
-
"/v1/browser/interactions/navigate",
|
|
848
|
-
requestData
|
|
849
|
-
);
|
|
942
|
+
const response = await this.httpClient.post("/v1/browser/interactions/navigate", requestData);
|
|
850
943
|
return {
|
|
851
944
|
success: response.success !== false,
|
|
852
945
|
url: response.url || url,
|
|
@@ -855,20 +948,112 @@ var BrowserManager = class {
|
|
|
855
948
|
};
|
|
856
949
|
} catch (error) {
|
|
857
950
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
858
|
-
throw new BrowserNavigationError(
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
951
|
+
throw new BrowserNavigationError(`Navigation failed: ${errorMessage}`, { cause: error });
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
async click(selector, sessionId, force = false, timeout = 3e4) {
|
|
955
|
+
if (!this.local) {
|
|
956
|
+
const client = this.getClient?.();
|
|
957
|
+
if (!client) {
|
|
958
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
959
|
+
}
|
|
960
|
+
const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
|
|
961
|
+
await client.browserClick(selector, sid, force, timeout);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
throw new UnsupportedOperationError("click() without session is only supported in cloud mode via InstaVM.");
|
|
965
|
+
}
|
|
966
|
+
async type(selector, text, sessionId, delay = 100, timeout = 3e4) {
|
|
967
|
+
if (!this.local) {
|
|
968
|
+
const client = this.getClient?.();
|
|
969
|
+
if (!client) {
|
|
970
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
971
|
+
}
|
|
972
|
+
const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
|
|
973
|
+
await client.browserType(selector, text, sid, delay, timeout);
|
|
974
|
+
return;
|
|
862
975
|
}
|
|
976
|
+
throw new UnsupportedOperationError("type() requires cloud mode with InstaVM client.");
|
|
977
|
+
}
|
|
978
|
+
async fill(selector, value, sessionId, timeout = 3e4) {
|
|
979
|
+
if (!this.local) {
|
|
980
|
+
const client = this.getClient?.();
|
|
981
|
+
if (!client) {
|
|
982
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
983
|
+
}
|
|
984
|
+
const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
|
|
985
|
+
await client.browserFill(selector, value, sid, timeout);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
throw new UnsupportedOperationError("fill() requires cloud mode with InstaVM client.");
|
|
989
|
+
}
|
|
990
|
+
async scroll(options = {}) {
|
|
991
|
+
if (!this.local) {
|
|
992
|
+
const client = this.getClient?.();
|
|
993
|
+
if (!client) {
|
|
994
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
995
|
+
}
|
|
996
|
+
const sid = options.sessionId ?? await this.ensureDefaultBrowserSessionId();
|
|
997
|
+
await client.browserScroll(sid, options.selector ?? null, options.x ?? null, options.y ?? null);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
throw new UnsupportedOperationError("scroll() cloud convenience requires InstaVM client.");
|
|
1001
|
+
}
|
|
1002
|
+
async waitFor(condition, selector, sessionId, timeout = 3e4) {
|
|
1003
|
+
if (!this.local) {
|
|
1004
|
+
const client = this.getClient?.();
|
|
1005
|
+
if (!client) {
|
|
1006
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
1007
|
+
}
|
|
1008
|
+
const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
|
|
1009
|
+
return client.browserWait(condition, sid, selector ?? null, timeout);
|
|
1010
|
+
}
|
|
1011
|
+
throw new UnsupportedOperationError("waitFor() requires cloud mode with InstaVM client.");
|
|
1012
|
+
}
|
|
1013
|
+
async extractElements(selector, sessionId, attributes, options = {}) {
|
|
1014
|
+
if (!this.local) {
|
|
1015
|
+
const client = this.getClient?.();
|
|
1016
|
+
if (!client) {
|
|
1017
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
1018
|
+
}
|
|
1019
|
+
const sid = sessionId ?? await this.ensureDefaultBrowserSessionId();
|
|
1020
|
+
const attrs = attributes ?? ["text"];
|
|
1021
|
+
const result = await client.browserExtractElements(sid, selector ?? null, attrs);
|
|
1022
|
+
if (options.maxResults !== void 0 && Array.isArray(result)) {
|
|
1023
|
+
return result.slice(0, options.maxResults);
|
|
1024
|
+
}
|
|
1025
|
+
return result;
|
|
1026
|
+
}
|
|
1027
|
+
throw new UnsupportedOperationError("extractElements() requires cloud mode with InstaVM client.");
|
|
863
1028
|
}
|
|
864
1029
|
/**
|
|
865
|
-
* Extract LLM-friendly content (local
|
|
1030
|
+
* Extract LLM-friendly content (local: URL required; cloud: optional session)
|
|
866
1031
|
*/
|
|
867
1032
|
async extractContent(options = {}) {
|
|
868
1033
|
if (!this.local) {
|
|
869
|
-
|
|
870
|
-
|
|
1034
|
+
const client = this.getClient?.();
|
|
1035
|
+
if (!client) {
|
|
1036
|
+
throw new UnsupportedOperationError("BrowserManager is missing InstaVM client reference.");
|
|
1037
|
+
}
|
|
1038
|
+
let sid = options.sessionId;
|
|
1039
|
+
if (!sid) {
|
|
1040
|
+
sid = await this.ensureDefaultBrowserSessionId();
|
|
1041
|
+
}
|
|
1042
|
+
const raw = await client.browserExtractContent(
|
|
1043
|
+
sid,
|
|
1044
|
+
options.url ?? null,
|
|
1045
|
+
options.includeInteractive !== false,
|
|
1046
|
+
options.includeAnchors !== false,
|
|
1047
|
+
options.maxAnchors ?? 50
|
|
871
1048
|
);
|
|
1049
|
+
return {
|
|
1050
|
+
readableContent: {
|
|
1051
|
+
...raw.readable_content || {},
|
|
1052
|
+
content: raw.readable_content?.content || ""
|
|
1053
|
+
},
|
|
1054
|
+
interactiveElements: raw.interactive_elements,
|
|
1055
|
+
contentAnchors: raw.content_anchors
|
|
1056
|
+
};
|
|
872
1057
|
}
|
|
873
1058
|
if (!options.url) {
|
|
874
1059
|
throw new BrowserInteractionError("url is required in local mode");
|
|
@@ -880,10 +1065,7 @@ var BrowserManager = class {
|
|
|
880
1065
|
max_anchors: options.maxAnchors ?? 50
|
|
881
1066
|
};
|
|
882
1067
|
try {
|
|
883
|
-
const response = await this.httpClient.post(
|
|
884
|
-
"/v1/browser/interactions/content",
|
|
885
|
-
requestData
|
|
886
|
-
);
|
|
1068
|
+
const response = await this.httpClient.post("/v1/browser/interactions/content", requestData);
|
|
887
1069
|
return {
|
|
888
1070
|
readableContent: {
|
|
889
1071
|
...response.readable_content || {},
|
|
@@ -894,36 +1076,33 @@ var BrowserManager = class {
|
|
|
894
1076
|
};
|
|
895
1077
|
} catch (error) {
|
|
896
1078
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
897
|
-
throw new BrowserInteractionError(
|
|
898
|
-
`Content extraction failed: ${errorMessage}`,
|
|
899
|
-
{ cause: error }
|
|
900
|
-
);
|
|
1079
|
+
throw new BrowserInteractionError(`Content extraction failed: ${errorMessage}`, { cause: error });
|
|
901
1080
|
}
|
|
902
1081
|
}
|
|
903
1082
|
/**
|
|
904
|
-
*
|
|
1083
|
+
* Closes the default browser session if one was created for cloud convenience.
|
|
905
1084
|
*/
|
|
1085
|
+
async close() {
|
|
1086
|
+
if (!this._defaultBrowserSessionId) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const sid = this._defaultBrowserSessionId;
|
|
1090
|
+
const session = this.activeSessions.get(sid);
|
|
1091
|
+
if (session) {
|
|
1092
|
+
try {
|
|
1093
|
+
await session.close();
|
|
1094
|
+
} catch {
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
this._defaultBrowserSessionId = null;
|
|
1098
|
+
this._defaultSessionPromise = null;
|
|
1099
|
+
}
|
|
906
1100
|
async dispose() {
|
|
1101
|
+
await this.close();
|
|
907
1102
|
await this.closeAllSessions();
|
|
908
1103
|
}
|
|
909
1104
|
};
|
|
910
1105
|
|
|
911
|
-
// src/utils/path.ts
|
|
912
|
-
function encodePathSegment(value) {
|
|
913
|
-
const segment = String(value);
|
|
914
|
-
if (segment === "." || segment === "..") {
|
|
915
|
-
throw new Error("Path traversal not allowed in path segment");
|
|
916
|
-
}
|
|
917
|
-
return encodeURIComponent(segment);
|
|
918
|
-
}
|
|
919
|
-
function encodePathSegments(path2) {
|
|
920
|
-
const segments = path2.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
|
|
921
|
-
if (segments.some((segment) => segment === "..")) {
|
|
922
|
-
throw new Error("Path traversal not allowed in proxy path");
|
|
923
|
-
}
|
|
924
|
-
return segments.map((segment) => encodeURIComponent(segment)).join("/");
|
|
925
|
-
}
|
|
926
|
-
|
|
927
1106
|
// src/client/VMsManager.ts
|
|
928
1107
|
var VMsManager = class {
|
|
929
1108
|
constructor(httpClient, local = false) {
|
|
@@ -1622,60 +1801,760 @@ var VolumesManager = class {
|
|
|
1622
1801
|
}
|
|
1623
1802
|
};
|
|
1624
1803
|
|
|
1625
|
-
// src/client/
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
this.
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
this.
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
this.env = options.env;
|
|
1637
|
-
if (!this.local && !apiKey) {
|
|
1638
|
-
throw new AuthenticationError("API key is required for cloud mode");
|
|
1804
|
+
// src/client/TapesManager.ts
|
|
1805
|
+
var TapesManager = class {
|
|
1806
|
+
constructor(httpClient, local = false) {
|
|
1807
|
+
this.httpClient = httpClient;
|
|
1808
|
+
this.local = local;
|
|
1809
|
+
}
|
|
1810
|
+
ensureCloud(operation) {
|
|
1811
|
+
if (this.local) {
|
|
1812
|
+
throw new UnsupportedOperationError(
|
|
1813
|
+
`${operation} is not supported in local mode.`
|
|
1814
|
+
);
|
|
1639
1815
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1816
|
+
}
|
|
1817
|
+
authHeaders() {
|
|
1818
|
+
return { "X-API-Key": this.httpClient.apiKey };
|
|
1819
|
+
}
|
|
1820
|
+
async start(sessionId, options = {}) {
|
|
1821
|
+
this.ensureCloud("Tape start");
|
|
1822
|
+
const safe = encodePathSegment(sessionId);
|
|
1823
|
+
return this.httpClient.post(
|
|
1824
|
+
`/v1/sessions/${safe}/tape/start`,
|
|
1825
|
+
{
|
|
1826
|
+
record_egress_content: options.recordEgressContent ?? false,
|
|
1827
|
+
record_fs: options.recordFs ?? true,
|
|
1828
|
+
retention_days: options.retentionDays ?? 7
|
|
1829
|
+
},
|
|
1830
|
+
this.authHeaders()
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
async stop(tapeId) {
|
|
1834
|
+
this.ensureCloud("Tape stop");
|
|
1835
|
+
const safe = encodePathSegment(tapeId);
|
|
1836
|
+
return this.httpClient.post(
|
|
1837
|
+
`/v1/tapes/${safe}/stop`,
|
|
1838
|
+
void 0,
|
|
1839
|
+
this.authHeaders()
|
|
1840
|
+
);
|
|
1841
|
+
}
|
|
1842
|
+
async list(query = {}) {
|
|
1843
|
+
this.ensureCloud("Tape listing");
|
|
1844
|
+
const params = {
|
|
1845
|
+
limit: query.limit ?? 50,
|
|
1846
|
+
offset: query.offset ?? 0
|
|
1646
1847
|
};
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
this.webhooks = new WebhooksManager(this.httpClient, this.local);
|
|
1657
|
-
this.volumes = new VolumesManager(this.httpClient, this.local);
|
|
1848
|
+
if (query.sessionId !== void 0) params.session_id = query.sessionId;
|
|
1849
|
+
if (query.vmId !== void 0) params.vm_id = query.vmId;
|
|
1850
|
+
if (query.status !== void 0) params.status = query.status;
|
|
1851
|
+
const result = await this.httpClient.get(
|
|
1852
|
+
"/v1/tapes",
|
|
1853
|
+
params,
|
|
1854
|
+
this.authHeaders()
|
|
1855
|
+
);
|
|
1856
|
+
return Array.isArray(result) ? result : [];
|
|
1658
1857
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1858
|
+
async get(tapeId) {
|
|
1859
|
+
this.ensureCloud("Tape lookup");
|
|
1860
|
+
const safe = encodePathSegment(tapeId);
|
|
1861
|
+
return this.httpClient.get(
|
|
1862
|
+
`/v1/tapes/${safe}`,
|
|
1863
|
+
void 0,
|
|
1864
|
+
this.authHeaders()
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
async events(tapeId, query = {}) {
|
|
1868
|
+
this.ensureCloud("Tape events");
|
|
1869
|
+
const safe = encodePathSegment(tapeId);
|
|
1870
|
+
const params = { limit: query.limit ?? 200 };
|
|
1871
|
+
if (query.afterStep !== void 0) params.after_step = query.afterStep;
|
|
1872
|
+
if (query.kind !== void 0) params.kind = query.kind;
|
|
1873
|
+
const result = await this.httpClient.get(
|
|
1874
|
+
`/v1/tapes/${safe}/events`,
|
|
1875
|
+
params,
|
|
1876
|
+
this.authHeaders()
|
|
1877
|
+
);
|
|
1878
|
+
return Array.isArray(result) ? result : [];
|
|
1879
|
+
}
|
|
1880
|
+
async appendEvent(tapeId, kind, options = {}) {
|
|
1881
|
+
this.ensureCloud("Tape append event");
|
|
1882
|
+
const safe = encodePathSegment(tapeId);
|
|
1883
|
+
const body = { kind, payload: options.payload || {} };
|
|
1884
|
+
if (options.stepId !== void 0) body.step_id = options.stepId;
|
|
1885
|
+
if (options.parentStepId !== void 0) body.parent_step_id = options.parentStepId;
|
|
1886
|
+
if (options.checkpointId !== void 0) body.checkpoint_id = options.checkpointId;
|
|
1887
|
+
return this.httpClient.post(
|
|
1888
|
+
`/v1/tapes/${safe}/events`,
|
|
1889
|
+
body,
|
|
1890
|
+
this.authHeaders()
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1893
|
+
async lanes(tapeId) {
|
|
1894
|
+
this.ensureCloud("Tape lanes");
|
|
1895
|
+
const safe = encodePathSegment(tapeId);
|
|
1896
|
+
return this.httpClient.get(
|
|
1897
|
+
`/v1/tapes/${safe}/lanes`,
|
|
1898
|
+
void 0,
|
|
1899
|
+
this.authHeaders()
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
async diff(tapeId, fromStep, toStep) {
|
|
1903
|
+
this.ensureCloud("Tape diff");
|
|
1904
|
+
const safe = encodePathSegment(tapeId);
|
|
1905
|
+
return this.httpClient.get(
|
|
1906
|
+
`/v1/tapes/${safe}/diff`,
|
|
1907
|
+
{ from: fromStep, to: toStep },
|
|
1908
|
+
this.authHeaders()
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
async branch(tapeId, atStep, options = {}) {
|
|
1912
|
+
this.ensureCloud("Tape branch");
|
|
1913
|
+
const safe = encodePathSegment(tapeId);
|
|
1914
|
+
return this.httpClient.post(
|
|
1915
|
+
`/v1/tapes/${safe}/branch`,
|
|
1916
|
+
{ at_step: atStep, mode: options.mode || "live" },
|
|
1917
|
+
this.authHeaders()
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
async delete(tapeId) {
|
|
1921
|
+
this.ensureCloud("Tape deletion");
|
|
1922
|
+
const safe = encodePathSegment(tapeId);
|
|
1923
|
+
return this.httpClient.delete(
|
|
1924
|
+
`/v1/tapes/${safe}`,
|
|
1925
|
+
this.authHeaders()
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
async export(tapeId) {
|
|
1929
|
+
this.ensureCloud("Tape export");
|
|
1930
|
+
const safe = encodePathSegment(tapeId);
|
|
1931
|
+
return this.httpClient.get(
|
|
1932
|
+
`/v1/tapes/${safe}/export`,
|
|
1933
|
+
void 0,
|
|
1934
|
+
this.authHeaders()
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
|
|
1939
|
+
// src/client/VaultsManager.ts
|
|
1940
|
+
var VaultsManager = class {
|
|
1941
|
+
constructor(httpClient, local = false) {
|
|
1942
|
+
this.httpClient = httpClient;
|
|
1943
|
+
this.local = local;
|
|
1944
|
+
}
|
|
1945
|
+
ensureCloud() {
|
|
1663
1946
|
if (this.local) {
|
|
1664
1947
|
throw new UnsupportedOperationError(
|
|
1665
|
-
|
|
1948
|
+
"Vault management is not supported in local mode."
|
|
1666
1949
|
);
|
|
1667
1950
|
}
|
|
1668
1951
|
}
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1952
|
+
auth() {
|
|
1953
|
+
return { "X-API-Key": this.httpClient.apiKey };
|
|
1954
|
+
}
|
|
1955
|
+
async list() {
|
|
1956
|
+
this.ensureCloud();
|
|
1957
|
+
const result = await this.httpClient.get("/v1/vaults", void 0, this.auth());
|
|
1958
|
+
if (Array.isArray(result)) return result;
|
|
1959
|
+
return result?.vaults || [];
|
|
1960
|
+
}
|
|
1961
|
+
async create(name, description) {
|
|
1962
|
+
this.ensureCloud();
|
|
1963
|
+
const body = { name };
|
|
1964
|
+
if (description !== void 0) body.description = description;
|
|
1965
|
+
return this.httpClient.post("/v1/vaults", body, this.auth());
|
|
1966
|
+
}
|
|
1967
|
+
async get(vaultId) {
|
|
1968
|
+
this.ensureCloud();
|
|
1969
|
+
return this.httpClient.get(
|
|
1970
|
+
`/v1/vaults/${encodePathSegment(vaultId)}`,
|
|
1971
|
+
void 0,
|
|
1972
|
+
this.auth()
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
async update(vaultId, updates) {
|
|
1976
|
+
this.ensureCloud();
|
|
1977
|
+
const body = {};
|
|
1978
|
+
if (updates.name !== void 0) body.name = updates.name;
|
|
1979
|
+
if (updates.description !== void 0) body.description = updates.description;
|
|
1980
|
+
return this.httpClient.patch(
|
|
1981
|
+
`/v1/vaults/${encodePathSegment(vaultId)}`,
|
|
1982
|
+
body,
|
|
1983
|
+
this.auth()
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
async delete(vaultId) {
|
|
1987
|
+
this.ensureCloud();
|
|
1988
|
+
return this.httpClient.delete(
|
|
1989
|
+
`/v1/vaults/${encodePathSegment(vaultId)}`,
|
|
1990
|
+
this.auth()
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
async discover(vaultId) {
|
|
1994
|
+
this.ensureCloud();
|
|
1995
|
+
return this.httpClient.get(
|
|
1996
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/discover`,
|
|
1997
|
+
void 0,
|
|
1998
|
+
this.auth()
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
async listCredentials(vaultId) {
|
|
2002
|
+
this.ensureCloud();
|
|
2003
|
+
const result = await this.httpClient.get(
|
|
2004
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/credentials`,
|
|
2005
|
+
void 0,
|
|
2006
|
+
this.auth()
|
|
2007
|
+
);
|
|
2008
|
+
if (Array.isArray(result)) return result;
|
|
2009
|
+
return result?.credentials || [];
|
|
2010
|
+
}
|
|
2011
|
+
async addCredential(vaultId, name, value, options = {}) {
|
|
2012
|
+
this.ensureCloud();
|
|
2013
|
+
const body = {
|
|
2014
|
+
name,
|
|
2015
|
+
value,
|
|
2016
|
+
credential_type: options.credentialType ?? "api_key"
|
|
2017
|
+
};
|
|
2018
|
+
if (options.description !== void 0) body.description = options.description;
|
|
2019
|
+
return this.httpClient.post(
|
|
2020
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/credentials`,
|
|
2021
|
+
body,
|
|
2022
|
+
this.auth()
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2025
|
+
async rotateCredential(vaultId, credentialId, value) {
|
|
2026
|
+
this.ensureCloud();
|
|
2027
|
+
return this.httpClient.post(
|
|
2028
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/credentials/${encodePathSegment(credentialId)}/rotate`,
|
|
2029
|
+
{ value },
|
|
2030
|
+
this.auth()
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
async deleteCredential(vaultId, credentialId) {
|
|
2034
|
+
this.ensureCloud();
|
|
2035
|
+
return this.httpClient.delete(
|
|
2036
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/credentials/${encodePathSegment(credentialId)}`,
|
|
2037
|
+
this.auth()
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
async listServices(vaultId) {
|
|
2041
|
+
this.ensureCloud();
|
|
2042
|
+
const result = await this.httpClient.get(
|
|
2043
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/services`,
|
|
2044
|
+
void 0,
|
|
2045
|
+
this.auth()
|
|
2046
|
+
);
|
|
2047
|
+
if (Array.isArray(result)) return result;
|
|
2048
|
+
return result?.services || [];
|
|
2049
|
+
}
|
|
2050
|
+
async addService(vaultId, host, authConfig, options = {}) {
|
|
2051
|
+
this.ensureCloud();
|
|
2052
|
+
const body = {
|
|
2053
|
+
host,
|
|
2054
|
+
auth_config: authConfig,
|
|
2055
|
+
enabled: options.enabled ?? true
|
|
2056
|
+
};
|
|
2057
|
+
if (options.description !== void 0) body.description = options.description;
|
|
2058
|
+
return this.httpClient.post(
|
|
2059
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/services`,
|
|
2060
|
+
body,
|
|
2061
|
+
this.auth()
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
async addServicesFromTemplates(vaultId, templateIds) {
|
|
2065
|
+
this.ensureCloud();
|
|
2066
|
+
return this.httpClient.post(
|
|
2067
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/services:from_template`,
|
|
2068
|
+
{ template_ids: templateIds },
|
|
2069
|
+
this.auth()
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
async deleteService(vaultId, serviceId) {
|
|
2073
|
+
this.ensureCloud();
|
|
2074
|
+
return this.httpClient.delete(
|
|
2075
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/services/${encodePathSegment(serviceId)}`,
|
|
2076
|
+
this.auth()
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
async getCatalog() {
|
|
2080
|
+
this.ensureCloud();
|
|
2081
|
+
const result = await this.httpClient.get(
|
|
2082
|
+
"/v1/vaults/catalog",
|
|
2083
|
+
void 0,
|
|
2084
|
+
this.auth()
|
|
2085
|
+
);
|
|
2086
|
+
if (Array.isArray(result)) return result;
|
|
2087
|
+
return result?.templates || [];
|
|
2088
|
+
}
|
|
2089
|
+
async getRequestLogs(vaultId, limit = 100) {
|
|
2090
|
+
this.ensureCloud();
|
|
2091
|
+
const result = await this.httpClient.get(
|
|
2092
|
+
`/v1/vaults/${encodePathSegment(vaultId)}/request-logs`,
|
|
2093
|
+
{ limit },
|
|
2094
|
+
this.auth()
|
|
2095
|
+
);
|
|
2096
|
+
if (Array.isArray(result)) return result;
|
|
2097
|
+
return result?.logs || result?.request_logs || [];
|
|
2098
|
+
}
|
|
2099
|
+
};
|
|
2100
|
+
|
|
2101
|
+
// src/client/PTYManager.ts
|
|
2102
|
+
var PTYManager = class {
|
|
2103
|
+
constructor(httpClient, local, getBaseURL) {
|
|
2104
|
+
this.httpClient = httpClient;
|
|
2105
|
+
this.local = local;
|
|
2106
|
+
this.getBaseURL = getBaseURL;
|
|
2107
|
+
}
|
|
2108
|
+
ensureCloud() {
|
|
2109
|
+
if (this.local) {
|
|
2110
|
+
throw new UnsupportedOperationError(
|
|
2111
|
+
"PTY management is not supported in local mode."
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
auth() {
|
|
2116
|
+
return { "X-API-Key": this.httpClient.apiKey };
|
|
2117
|
+
}
|
|
2118
|
+
buildPayload(options) {
|
|
2119
|
+
const payload = {
|
|
2120
|
+
cols: options.cols ?? 80,
|
|
2121
|
+
rows: options.rows ?? 24
|
|
2122
|
+
};
|
|
2123
|
+
if (options.command !== void 0) payload.shell = options.command;
|
|
2124
|
+
if (options.ptyId !== void 0) payload.id = options.ptyId;
|
|
2125
|
+
return payload;
|
|
2126
|
+
}
|
|
2127
|
+
buildWsUrl(path2) {
|
|
2128
|
+
const trimmedBase = this.getBaseURL().replace(/\/+$/, "");
|
|
2129
|
+
const base = trimmedBase.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://");
|
|
2130
|
+
const apiKey = encodeURIComponent(this.httpClient.apiKey || "");
|
|
2131
|
+
return `${base}${path2}?api_key=${apiKey}`;
|
|
2132
|
+
}
|
|
2133
|
+
// -- Session-scoped --------------------------------------------------------
|
|
2134
|
+
async create(sessionId, options = {}) {
|
|
2135
|
+
this.ensureCloud();
|
|
2136
|
+
return this.httpClient.post(
|
|
2137
|
+
`/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions`,
|
|
2138
|
+
this.buildPayload(options),
|
|
2139
|
+
this.auth()
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
2142
|
+
async list(sessionId) {
|
|
2143
|
+
this.ensureCloud();
|
|
2144
|
+
const result = await this.httpClient.get(
|
|
2145
|
+
`/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions`,
|
|
2146
|
+
void 0,
|
|
2147
|
+
this.auth()
|
|
2148
|
+
);
|
|
2149
|
+
if (Array.isArray(result)) return result;
|
|
2150
|
+
return result?.sessions || [];
|
|
2151
|
+
}
|
|
2152
|
+
async get(sessionId, ptyId) {
|
|
2153
|
+
this.ensureCloud();
|
|
2154
|
+
return this.httpClient.get(
|
|
2155
|
+
`/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions/${encodePathSegment(ptyId)}`,
|
|
2156
|
+
void 0,
|
|
2157
|
+
this.auth()
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2160
|
+
async resize(sessionId, ptyId, cols, rows) {
|
|
2161
|
+
this.ensureCloud();
|
|
2162
|
+
return this.httpClient.post(
|
|
2163
|
+
`/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions/${encodePathSegment(ptyId)}/resize`,
|
|
2164
|
+
{ cols, rows },
|
|
2165
|
+
this.auth()
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
async kill(sessionId, ptyId) {
|
|
2169
|
+
this.ensureCloud();
|
|
2170
|
+
return this.httpClient.delete(
|
|
2171
|
+
`/v1/sessions/${encodePathSegment(sessionId)}/pty/sessions/${encodePathSegment(ptyId)}`,
|
|
2172
|
+
this.auth()
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
wsUrl(sessionId, ptyId) {
|
|
2176
|
+
return this.buildWsUrl(
|
|
2177
|
+
`/v1/sessions/${encodePathSegment(sessionId)}/pty/${encodePathSegment(ptyId)}/connect`
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
// -- VM-scoped (docker exec -it pattern) -----------------------------------
|
|
2181
|
+
async createForVm(vmId, options = {}) {
|
|
2182
|
+
this.ensureCloud();
|
|
2183
|
+
return this.httpClient.post(
|
|
2184
|
+
`/v1/vms/${encodePathSegment(vmId)}/pty/sessions`,
|
|
2185
|
+
this.buildPayload(options),
|
|
2186
|
+
this.auth()
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
async listForVm(vmId) {
|
|
2190
|
+
this.ensureCloud();
|
|
2191
|
+
const result = await this.httpClient.get(
|
|
2192
|
+
`/v1/vms/${encodePathSegment(vmId)}/pty/sessions`,
|
|
2193
|
+
void 0,
|
|
2194
|
+
this.auth()
|
|
2195
|
+
);
|
|
2196
|
+
if (Array.isArray(result)) return result;
|
|
2197
|
+
return result?.sessions || [];
|
|
2198
|
+
}
|
|
2199
|
+
async getForVm(vmId, ptyId) {
|
|
2200
|
+
this.ensureCloud();
|
|
2201
|
+
return this.httpClient.get(
|
|
2202
|
+
`/v1/vms/${encodePathSegment(vmId)}/pty/sessions/${encodePathSegment(ptyId)}`,
|
|
2203
|
+
void 0,
|
|
2204
|
+
this.auth()
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
async resizeForVm(vmId, ptyId, cols, rows) {
|
|
2208
|
+
this.ensureCloud();
|
|
2209
|
+
return this.httpClient.post(
|
|
2210
|
+
`/v1/vms/${encodePathSegment(vmId)}/pty/sessions/${encodePathSegment(ptyId)}/resize`,
|
|
2211
|
+
{ cols, rows },
|
|
2212
|
+
this.auth()
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
async killForVm(vmId, ptyId) {
|
|
2216
|
+
this.ensureCloud();
|
|
2217
|
+
return this.httpClient.delete(
|
|
2218
|
+
`/v1/vms/${encodePathSegment(vmId)}/pty/sessions/${encodePathSegment(ptyId)}`,
|
|
2219
|
+
this.auth()
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2222
|
+
wsUrlForVm(vmId, ptyId) {
|
|
2223
|
+
return this.buildWsUrl(
|
|
2224
|
+
`/v1/vms/${encodePathSegment(vmId)}/pty/${encodePathSegment(ptyId)}/connect`
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
|
|
2229
|
+
// src/client/RecordingsManager.ts
|
|
2230
|
+
import fs2 from "fs";
|
|
2231
|
+
import { stat, unlink } from "fs/promises";
|
|
2232
|
+
import { pipeline } from "stream/promises";
|
|
2233
|
+
import axios2 from "axios";
|
|
2234
|
+
var RecordingsManager = class {
|
|
2235
|
+
constructor(httpClient, local, getBaseURL) {
|
|
2236
|
+
this.httpClient = httpClient;
|
|
2237
|
+
this.local = local;
|
|
2238
|
+
this.getBaseURL = getBaseURL;
|
|
2239
|
+
}
|
|
2240
|
+
ensureCloud() {
|
|
2241
|
+
if (this.local) {
|
|
2242
|
+
throw new UnsupportedOperationError(
|
|
2243
|
+
"Recording management is not supported in local mode."
|
|
2244
|
+
);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
auth() {
|
|
2248
|
+
return { "X-API-Key": this.httpClient.apiKey };
|
|
2249
|
+
}
|
|
2250
|
+
sdkHeaders() {
|
|
2251
|
+
return { "User-Agent": `instavm-js-sdk/${INSTAVM_JS_SDK_VERSION}` };
|
|
2252
|
+
}
|
|
2253
|
+
async list(query = {}) {
|
|
2254
|
+
this.ensureCloud();
|
|
2255
|
+
const params = {
|
|
2256
|
+
limit: query.limit ?? 50,
|
|
2257
|
+
offset: query.offset ?? 0
|
|
2258
|
+
};
|
|
2259
|
+
if (query.sessionId !== void 0) params.session_id = query.sessionId;
|
|
2260
|
+
if (query.status !== void 0) params.status = query.status;
|
|
2261
|
+
const result = await this.httpClient.get("/v1/recordings", params, this.auth());
|
|
2262
|
+
return Array.isArray(result) ? result : [];
|
|
2263
|
+
}
|
|
2264
|
+
async get(recordingId) {
|
|
2265
|
+
this.ensureCloud();
|
|
2266
|
+
return this.httpClient.get(
|
|
2267
|
+
`/v1/recordings/${encodePathSegment(recordingId)}`,
|
|
2268
|
+
void 0,
|
|
2269
|
+
this.auth()
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Resolve the presigned download URL for a recording without following the
|
|
2274
|
+
* 302 redirect. The returned URL is short-lived and pre-authorised.
|
|
2275
|
+
*/
|
|
2276
|
+
async getDownloadUrl(recordingId) {
|
|
2277
|
+
this.ensureCloud();
|
|
2278
|
+
const url = `${this.getBaseURL().replace(/\/+$/, "")}/v1/recordings/${encodePathSegment(recordingId)}/download`;
|
|
2279
|
+
const response = await axios2.request({
|
|
2280
|
+
method: "GET",
|
|
2281
|
+
url,
|
|
2282
|
+
headers: {
|
|
2283
|
+
...this.auth(),
|
|
2284
|
+
...this.sdkHeaders()
|
|
2285
|
+
},
|
|
2286
|
+
maxRedirects: 0,
|
|
2287
|
+
validateStatus: (status) => status >= 200 && status < 400
|
|
2288
|
+
});
|
|
2289
|
+
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
2290
|
+
const location = response.headers?.location || response.headers?.Location;
|
|
2291
|
+
if (!location) {
|
|
2292
|
+
throw new InstaVMError("Recording download response missing Location header");
|
|
2293
|
+
}
|
|
2294
|
+
return location;
|
|
2295
|
+
}
|
|
2296
|
+
throw new InstaVMError(
|
|
2297
|
+
`Server did not redirect to a presigned URL (status=${response.status})`
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* Stream a recording to a local file using the presigned URL. The S3 URL
|
|
2302
|
+
* must NOT carry our X-API-Key header — fetch it with a clean axios call.
|
|
2303
|
+
*/
|
|
2304
|
+
async download(recordingId, outputPath) {
|
|
2305
|
+
this.ensureCloud();
|
|
2306
|
+
const presignedUrl = await this.getDownloadUrl(recordingId);
|
|
2307
|
+
const response = await axios2.request({
|
|
2308
|
+
method: "GET",
|
|
2309
|
+
url: presignedUrl,
|
|
2310
|
+
responseType: "stream",
|
|
2311
|
+
headers: this.sdkHeaders()
|
|
2312
|
+
});
|
|
2313
|
+
const writer = fs2.createWriteStream(outputPath);
|
|
2314
|
+
try {
|
|
2315
|
+
await pipeline(response.data, writer);
|
|
2316
|
+
} catch (error) {
|
|
2317
|
+
await unlink(outputPath).catch(() => void 0);
|
|
2318
|
+
throw error;
|
|
2319
|
+
}
|
|
2320
|
+
const fileStat = await stat(outputPath);
|
|
2321
|
+
return { recordingId, path: outputPath, bytes: fileStat.size };
|
|
2322
|
+
}
|
|
2323
|
+
async delete(recordingId) {
|
|
2324
|
+
this.ensureCloud();
|
|
2325
|
+
return this.httpClient.delete(
|
|
2326
|
+
`/v1/recordings/${encodePathSegment(recordingId)}`,
|
|
2327
|
+
this.auth()
|
|
2328
|
+
);
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2332
|
+
// src/client/CreditsManager.ts
|
|
2333
|
+
var CreditsManager = class {
|
|
2334
|
+
constructor(httpClient, local = false) {
|
|
2335
|
+
this.httpClient = httpClient;
|
|
2336
|
+
this.local = local;
|
|
2337
|
+
}
|
|
2338
|
+
ensureCloud() {
|
|
2339
|
+
if (this.local) {
|
|
2340
|
+
throw new UnsupportedOperationError(
|
|
2341
|
+
"Credits API is not supported in local mode."
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
auth() {
|
|
2346
|
+
return { "X-API-Key": this.httpClient.apiKey };
|
|
2347
|
+
}
|
|
2348
|
+
async allocation() {
|
|
2349
|
+
this.ensureCloud();
|
|
2350
|
+
return this.httpClient.get(
|
|
2351
|
+
"/v1/credits/allocation",
|
|
2352
|
+
void 0,
|
|
2353
|
+
this.auth()
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
async usage(period = "current_month", options = {}) {
|
|
2357
|
+
this.ensureCloud();
|
|
2358
|
+
const params = { period, limit: options.limit ?? 100 };
|
|
2359
|
+
if (options.usageType !== void 0) params.usage_type = options.usageType;
|
|
2360
|
+
const result = await this.httpClient.get("/v1/credits/usage", params, this.auth());
|
|
2361
|
+
return Array.isArray(result) ? result : [];
|
|
2362
|
+
}
|
|
2363
|
+
async summary(period = "current_month") {
|
|
2364
|
+
this.ensureCloud();
|
|
2365
|
+
return this.httpClient.get(
|
|
2366
|
+
"/v1/credits/summary",
|
|
2367
|
+
{ period },
|
|
2368
|
+
this.auth()
|
|
2369
|
+
);
|
|
2370
|
+
}
|
|
2371
|
+
async check(usageType, requiredCredits) {
|
|
2372
|
+
this.ensureCloud();
|
|
2373
|
+
return this.httpClient.get(
|
|
2374
|
+
"/v1/credits/check",
|
|
2375
|
+
{ usage_type: usageType, required_credits: requiredCredits },
|
|
2376
|
+
this.auth()
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
2379
|
+
async rates() {
|
|
2380
|
+
this.ensureCloud();
|
|
2381
|
+
return this.httpClient.get(
|
|
2382
|
+
"/v1/credits/rates",
|
|
2383
|
+
void 0,
|
|
2384
|
+
this.auth()
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
async usageTrends(period = "30d", granularity = "daily") {
|
|
2388
|
+
this.ensureCloud();
|
|
2389
|
+
return this.httpClient.get(
|
|
2390
|
+
"/v1/credits/usage/trends",
|
|
2391
|
+
{ period, granularity },
|
|
2392
|
+
this.auth()
|
|
2393
|
+
);
|
|
2394
|
+
}
|
|
2395
|
+
async usageForecast() {
|
|
2396
|
+
this.ensureCloud();
|
|
2397
|
+
return this.httpClient.get(
|
|
2398
|
+
"/v1/credits/usage/forecasting",
|
|
2399
|
+
void 0,
|
|
2400
|
+
this.auth()
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
|
|
2405
|
+
// src/client/TapeContext.ts
|
|
2406
|
+
var TapeContext = class {
|
|
2407
|
+
constructor(tapes, sessionId, startOpts) {
|
|
2408
|
+
this.tapes = tapes;
|
|
2409
|
+
this.sessionId = sessionId;
|
|
2410
|
+
this.startOpts = startOpts;
|
|
2411
|
+
this.info = null;
|
|
2412
|
+
}
|
|
2413
|
+
get id() {
|
|
2414
|
+
return this.info?.id;
|
|
2415
|
+
}
|
|
2416
|
+
async start() {
|
|
2417
|
+
if (!this.sessionId) {
|
|
2418
|
+
throw new SessionError("Session ID not set. Please create a session first.");
|
|
2419
|
+
}
|
|
2420
|
+
this.info = await this.tapes.start(this.sessionId, this.startOpts);
|
|
2421
|
+
}
|
|
2422
|
+
async stop() {
|
|
2423
|
+
if (!this.info?.id) {
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
this.info = await this.tapes.stop(this.info.id);
|
|
2427
|
+
}
|
|
2428
|
+
async [Symbol.asyncDispose]() {
|
|
2429
|
+
await this.stop();
|
|
2430
|
+
}
|
|
2431
|
+
};
|
|
2432
|
+
|
|
2433
|
+
// src/client/ToolCallRecorder.ts
|
|
2434
|
+
var ToolCallRecorder = class {
|
|
2435
|
+
constructor(tapes, tapeId) {
|
|
2436
|
+
this.tapes = tapes;
|
|
2437
|
+
this.tapeId = tapeId;
|
|
2438
|
+
}
|
|
2439
|
+
async event(kind, payload, stepId) {
|
|
2440
|
+
return this.tapes.appendEvent(this.tapeId, kind, {
|
|
2441
|
+
payload: payload || {},
|
|
2442
|
+
stepId
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
span(name, payload) {
|
|
2446
|
+
return new ToolCallSpan(this, name, payload || {});
|
|
2447
|
+
}
|
|
2448
|
+
};
|
|
2449
|
+
var ToolCallSpan = class {
|
|
2450
|
+
constructor(recorder, name, startPayload) {
|
|
2451
|
+
this.recorder = recorder;
|
|
2452
|
+
this.name = name;
|
|
2453
|
+
this.startPayload = startPayload;
|
|
2454
|
+
this.endPayload = {};
|
|
2455
|
+
}
|
|
2456
|
+
attach(payload) {
|
|
2457
|
+
Object.assign(this.endPayload, payload);
|
|
2458
|
+
}
|
|
2459
|
+
async enter() {
|
|
2460
|
+
const result = await this.recorder.event("tool_call_start", {
|
|
2461
|
+
name: this.name,
|
|
2462
|
+
...this.startPayload
|
|
2463
|
+
});
|
|
2464
|
+
this.stepId = result.step_id;
|
|
2465
|
+
}
|
|
2466
|
+
async exit(err) {
|
|
2467
|
+
const payload = {
|
|
2468
|
+
name: this.name,
|
|
2469
|
+
ok: err === void 0 || err === null,
|
|
2470
|
+
...this.endPayload
|
|
2471
|
+
};
|
|
2472
|
+
if (err) {
|
|
2473
|
+
payload.error = err instanceof Error ? err.message : String(err);
|
|
2474
|
+
}
|
|
2475
|
+
await this.recorder.event("tool_call_end", payload, this.stepId);
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
|
|
2479
|
+
// src/client/InstaVM.ts
|
|
2480
|
+
import FormData2 from "form-data";
|
|
2481
|
+
var InstaVM = class {
|
|
2482
|
+
constructor(apiKey, options = {}) {
|
|
2483
|
+
this._sessionId = null;
|
|
2484
|
+
this._vmUsed = false;
|
|
2485
|
+
this.local = options.local || false;
|
|
2486
|
+
this.timeout = options.timeout || 3e5;
|
|
2487
|
+
this.memory_mb = options.memory_mb;
|
|
2488
|
+
this.cpu_count = options.cpu_count;
|
|
2489
|
+
this.metadata = options.metadata;
|
|
2490
|
+
this.env = options.env;
|
|
2491
|
+
this._autoStartSession = options.auto_start_session !== false;
|
|
2492
|
+
if (!this.local && !apiKey) {
|
|
2493
|
+
throw new AuthenticationError("API key is required for cloud mode");
|
|
2494
|
+
}
|
|
2495
|
+
const config = {
|
|
2496
|
+
baseURL: this.local ? options.localURL || "http://coderunner.local:8222" : options.baseURL || "https://api.instavm.io",
|
|
2497
|
+
timeout: this.timeout,
|
|
2498
|
+
maxRetries: options.maxRetries || 3,
|
|
2499
|
+
retryDelay: options.retryDelay || 1e3,
|
|
2500
|
+
apiKey: apiKey || ""
|
|
2501
|
+
};
|
|
2502
|
+
this.httpClient = new HTTPClient(config);
|
|
2503
|
+
this.browser = new BrowserManager(this.httpClient, this.local, () => this);
|
|
2504
|
+
this.vms = new VMsManager(this.httpClient, this.local);
|
|
2505
|
+
this.snapshots = new SnapshotsManager(this.httpClient, this.local);
|
|
2506
|
+
this.shares = new SharesManager(this.httpClient, this.local, () => this._sessionId);
|
|
2507
|
+
this.customDomains = new CustomDomainsManager(this.httpClient, this.local);
|
|
2508
|
+
this.computerUse = new ComputerUseManager(this.httpClient, this.local);
|
|
2509
|
+
this.apiKeys = new APIKeysManager(this.httpClient, this.local);
|
|
2510
|
+
this.audit = new AuditManager(this.httpClient, this.local);
|
|
2511
|
+
this.webhooks = new WebhooksManager(this.httpClient, this.local);
|
|
2512
|
+
this.volumes = new VolumesManager(this.httpClient, this.local);
|
|
2513
|
+
this.tapes = new TapesManager(this.httpClient, this.local);
|
|
2514
|
+
this.vaults = new VaultsManager(this.httpClient, this.local);
|
|
2515
|
+
this.pty = new PTYManager(this.httpClient, this.local, () => this.httpClient.baseURL);
|
|
2516
|
+
this.recordings = new RecordingsManager(this.httpClient, this.local, () => this.httpClient.baseURL);
|
|
2517
|
+
this.credits = new CreditsManager(this.httpClient, this.local);
|
|
2518
|
+
if (this._autoStartSession && !this.local && apiKey) {
|
|
2519
|
+
this.sessionBootstrap = this.createSession();
|
|
2520
|
+
void this.sessionBootstrap.catch(() => void 0);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
async awaitSessionBootstrap() {
|
|
2524
|
+
if (this.sessionBootstrap) {
|
|
2525
|
+
try {
|
|
2526
|
+
await this.sessionBootstrap;
|
|
2527
|
+
} finally {
|
|
2528
|
+
this.sessionBootstrap = void 0;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Ensure operation is not called in local mode
|
|
2534
|
+
*/
|
|
2535
|
+
ensureNotLocal(operationName) {
|
|
2536
|
+
if (this.local) {
|
|
2537
|
+
throw new UnsupportedOperationError(
|
|
2538
|
+
`${operationName} is not supported in local mode. This operation is only available when using the cloud API.`
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
authHeaders() {
|
|
2543
|
+
return { "X-API-Key": this.httpClient.apiKey };
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Execute code synchronously
|
|
2547
|
+
*
|
|
2548
|
+
* @param command - Command to execute
|
|
2549
|
+
* @param options - Execution options
|
|
2550
|
+
* @param options.timeout - Request timeout in milliseconds (used for HTTP request timeout and sent to API in seconds)
|
|
2551
|
+
* @returns Execution result
|
|
2552
|
+
*/
|
|
2553
|
+
async execute(command, options = {}) {
|
|
2554
|
+
if (!this.local) {
|
|
2555
|
+
await this.awaitSessionBootstrap();
|
|
2556
|
+
}
|
|
2557
|
+
let sessionId = options.sessionId || this._sessionId;
|
|
1679
2558
|
if (!this.local && !sessionId) {
|
|
1680
2559
|
sessionId = await this.createSession();
|
|
1681
2560
|
}
|
|
@@ -1728,6 +2607,7 @@ var InstaVM = class {
|
|
|
1728
2607
|
*/
|
|
1729
2608
|
async executeAsync(command, options = {}) {
|
|
1730
2609
|
this.ensureNotLocal("Async execution");
|
|
2610
|
+
await this.awaitSessionBootstrap();
|
|
1731
2611
|
let sessionId = options.sessionId || this._sessionId;
|
|
1732
2612
|
if (!sessionId) {
|
|
1733
2613
|
sessionId = await this.createSession();
|
|
@@ -1814,6 +2694,7 @@ var InstaVM = class {
|
|
|
1814
2694
|
*/
|
|
1815
2695
|
async upload(files, options = {}) {
|
|
1816
2696
|
this.ensureNotLocal("File upload");
|
|
2697
|
+
await this.awaitSessionBootstrap();
|
|
1817
2698
|
const sessionId = options.sessionId || this._sessionId;
|
|
1818
2699
|
if (!sessionId) {
|
|
1819
2700
|
throw new SessionError("Session ID not set. Please create a session first using createSession().");
|
|
@@ -1880,7 +2761,7 @@ var InstaVM = class {
|
|
|
1880
2761
|
"/v1/sessions/session",
|
|
1881
2762
|
requestBody
|
|
1882
2763
|
);
|
|
1883
|
-
if (response.session_id) {
|
|
2764
|
+
if (response && response.session_id) {
|
|
1884
2765
|
this._sessionId = response.session_id;
|
|
1885
2766
|
return response.session_id;
|
|
1886
2767
|
}
|
|
@@ -1946,10 +2827,11 @@ var InstaVM = class {
|
|
|
1946
2827
|
if (port !== void 0) {
|
|
1947
2828
|
params.port = port;
|
|
1948
2829
|
}
|
|
2830
|
+
const safeSid = encodePathSegment(targetSessionId);
|
|
1949
2831
|
return this.httpClient.get(
|
|
1950
|
-
`/v1/sessions/app-url/${
|
|
2832
|
+
`/v1/sessions/app-url/${safeSid}`,
|
|
1951
2833
|
params,
|
|
1952
|
-
|
|
2834
|
+
this.authHeaders()
|
|
1953
2835
|
);
|
|
1954
2836
|
}
|
|
1955
2837
|
/**
|
|
@@ -1983,10 +2865,11 @@ var InstaVM = class {
|
|
|
1983
2865
|
throw new SessionError("No active session to get usage for");
|
|
1984
2866
|
}
|
|
1985
2867
|
try {
|
|
2868
|
+
const safeSid = encodePathSegment(targetSessionId);
|
|
1986
2869
|
const response = await this.httpClient.get(
|
|
1987
|
-
`/v1/sessions/usage/${
|
|
2870
|
+
`/v1/sessions/usage/${safeSid}`,
|
|
1988
2871
|
void 0,
|
|
1989
|
-
|
|
2872
|
+
this.authHeaders()
|
|
1990
2873
|
);
|
|
1991
2874
|
return {
|
|
1992
2875
|
sessionsUsed: response.sessions_used || 0,
|
|
@@ -2022,12 +2905,429 @@ var InstaVM = class {
|
|
|
2022
2905
|
if (!targetSessionId) {
|
|
2023
2906
|
throw new SessionError("Session ID not set. Please create a session first.");
|
|
2024
2907
|
}
|
|
2908
|
+
const safeSid = encodePathSegment(targetSessionId);
|
|
2025
2909
|
return this.httpClient.get(
|
|
2026
|
-
`/v1/sessions/status/${
|
|
2910
|
+
`/v1/sessions/status/${safeSid}`,
|
|
2027
2911
|
void 0,
|
|
2028
|
-
|
|
2912
|
+
this.authHeaders()
|
|
2029
2913
|
);
|
|
2030
2914
|
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Get session record details from ``GET /v1/sessions/session/{session_id}`` (Python: ``get_session_info``).
|
|
2917
|
+
*/
|
|
2918
|
+
async getSessionInfo(sessionId) {
|
|
2919
|
+
this.ensureNotLocal("Session management");
|
|
2920
|
+
const sid = sessionId || this._sessionId;
|
|
2921
|
+
if (!sid) {
|
|
2922
|
+
throw new SessionError("Session ID not set. Please create a session first.");
|
|
2923
|
+
}
|
|
2924
|
+
try {
|
|
2925
|
+
const safeSid = encodePathSegment(sid);
|
|
2926
|
+
const result = await this.httpClient.get(
|
|
2927
|
+
`/v1/sessions/session/${safeSid}`,
|
|
2928
|
+
void 0,
|
|
2929
|
+
this.authHeaders()
|
|
2930
|
+
);
|
|
2931
|
+
if (typeof result !== "object" || result === null) {
|
|
2932
|
+
throw new SessionError("Failed to get session info: non-JSON response");
|
|
2933
|
+
}
|
|
2934
|
+
return result;
|
|
2935
|
+
} catch (error) {
|
|
2936
|
+
if (error instanceof InstaVMError) {
|
|
2937
|
+
throw error;
|
|
2938
|
+
}
|
|
2939
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2940
|
+
throw new SessionError(`Failed to get session info: ${errorMessage}`, { cause: error });
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* Day-wise usage breakdown from ``GET /v1/users/me/usage-breakdown`` (Python: ``get_usage_breakdown``).
|
|
2945
|
+
*/
|
|
2946
|
+
async getUsageBreakdown(options = {}) {
|
|
2947
|
+
this.ensureNotLocal("Usage breakdown lookup");
|
|
2948
|
+
const params = { days: options.days ?? 30 };
|
|
2949
|
+
if (options.startDate !== void 0) {
|
|
2950
|
+
params.start_date = options.startDate;
|
|
2951
|
+
}
|
|
2952
|
+
if (options.endDate !== void 0) {
|
|
2953
|
+
params.end_date = options.endDate;
|
|
2954
|
+
}
|
|
2955
|
+
try {
|
|
2956
|
+
const result = await this.httpClient.get(
|
|
2957
|
+
"/v1/users/me/usage-breakdown",
|
|
2958
|
+
params,
|
|
2959
|
+
this.authHeaders()
|
|
2960
|
+
);
|
|
2961
|
+
if (typeof result !== "object" || result === null) {
|
|
2962
|
+
throw new InstaVMError("Failed to fetch usage breakdown: non-JSON response");
|
|
2963
|
+
}
|
|
2964
|
+
return result;
|
|
2965
|
+
} catch (error) {
|
|
2966
|
+
if (error instanceof InstaVMError) {
|
|
2967
|
+
throw error;
|
|
2968
|
+
}
|
|
2969
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2970
|
+
throw new InstaVMError(`Failed to fetch usage breakdown: ${errorMessage}`, { cause: error });
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* Time-travel tape context manager (Python: ``tape()`` / ``TapeContext``).
|
|
2975
|
+
*/
|
|
2976
|
+
tape(sessionId, options = {}) {
|
|
2977
|
+
const sid = sessionId ?? this._sessionId ?? void 0;
|
|
2978
|
+
return new TapeContext(this.tapes, sid, options);
|
|
2979
|
+
}
|
|
2980
|
+
/** Build a tool-call recorder for an active tape id (Python: ``ToolCallRecorder``). */
|
|
2981
|
+
toolCallRecorder(tapeId) {
|
|
2982
|
+
return new ToolCallRecorder(this.tapes, tapeId);
|
|
2983
|
+
}
|
|
2984
|
+
/**
|
|
2985
|
+
* @deprecated Streaming is not supported by the API; yields a single chunk from ``execute``.
|
|
2986
|
+
*/
|
|
2987
|
+
async *executeStreaming(command, onOutput) {
|
|
2988
|
+
console.warn(
|
|
2989
|
+
"executeStreaming is deprecated. The API does not support streaming execution. Use execute() or executeAsync() instead."
|
|
2990
|
+
);
|
|
2991
|
+
const result = await this.execute(command);
|
|
2992
|
+
const output = result.stdout || "";
|
|
2993
|
+
if (onOutput) {
|
|
2994
|
+
onOutput(output);
|
|
2995
|
+
}
|
|
2996
|
+
yield output;
|
|
2997
|
+
}
|
|
2998
|
+
// --- Vault (flat API parity with Python InstaVM) ---
|
|
2999
|
+
async listVaults() {
|
|
3000
|
+
return this.vaults.list();
|
|
3001
|
+
}
|
|
3002
|
+
async createVault(name, description) {
|
|
3003
|
+
return this.vaults.create(name, description);
|
|
3004
|
+
}
|
|
3005
|
+
async getVault(vaultId) {
|
|
3006
|
+
return this.vaults.get(vaultId);
|
|
3007
|
+
}
|
|
3008
|
+
async updateVault(vaultId, updates) {
|
|
3009
|
+
return this.vaults.update(vaultId, updates);
|
|
3010
|
+
}
|
|
3011
|
+
async deleteVault(vaultId) {
|
|
3012
|
+
return this.vaults.delete(vaultId);
|
|
3013
|
+
}
|
|
3014
|
+
async discoverVault(vaultId) {
|
|
3015
|
+
return this.vaults.discover(vaultId);
|
|
3016
|
+
}
|
|
3017
|
+
async listVaultCredentials(vaultId) {
|
|
3018
|
+
return this.vaults.listCredentials(vaultId);
|
|
3019
|
+
}
|
|
3020
|
+
async addVaultCredential(vaultId, name, value, options = {}) {
|
|
3021
|
+
return this.vaults.addCredential(vaultId, name, value, options);
|
|
3022
|
+
}
|
|
3023
|
+
async rotateVaultCredential(vaultId, credentialId, value) {
|
|
3024
|
+
return this.vaults.rotateCredential(vaultId, credentialId, value);
|
|
3025
|
+
}
|
|
3026
|
+
async deleteVaultCredential(vaultId, credentialId) {
|
|
3027
|
+
return this.vaults.deleteCredential(vaultId, credentialId);
|
|
3028
|
+
}
|
|
3029
|
+
async listVaultServices(vaultId) {
|
|
3030
|
+
return this.vaults.listServices(vaultId);
|
|
3031
|
+
}
|
|
3032
|
+
async addVaultService(vaultId, host, authConfig, options = {}) {
|
|
3033
|
+
return this.vaults.addService(vaultId, host, authConfig, options);
|
|
3034
|
+
}
|
|
3035
|
+
async deleteVaultService(vaultId, serviceId) {
|
|
3036
|
+
return this.vaults.deleteService(vaultId, serviceId);
|
|
3037
|
+
}
|
|
3038
|
+
async addVaultServicesFromTemplates(vaultId, templateIds) {
|
|
3039
|
+
return this.vaults.addServicesFromTemplates(vaultId, templateIds);
|
|
3040
|
+
}
|
|
3041
|
+
async getVaultCatalog() {
|
|
3042
|
+
return this.vaults.getCatalog();
|
|
3043
|
+
}
|
|
3044
|
+
async getVaultRequestLogs(vaultId, limit = 100) {
|
|
3045
|
+
return this.vaults.getRequestLogs(vaultId, limit);
|
|
3046
|
+
}
|
|
3047
|
+
// --- Browser (client-level parity with Python InstaVM) ---
|
|
3048
|
+
async createBrowserSession(viewportWidth = 1920, viewportHeight = 1080, userAgent) {
|
|
3049
|
+
this.ensureNotLocal("Browser session management");
|
|
3050
|
+
const body = {
|
|
3051
|
+
viewport_width: viewportWidth,
|
|
3052
|
+
viewport_height: viewportHeight
|
|
3053
|
+
};
|
|
3054
|
+
if (userAgent) {
|
|
3055
|
+
body.user_agent = userAgent;
|
|
3056
|
+
}
|
|
3057
|
+
try {
|
|
3058
|
+
const result = await this.httpClient.post(
|
|
3059
|
+
"/v1/browser/sessions/",
|
|
3060
|
+
body,
|
|
3061
|
+
this.authHeaders()
|
|
3062
|
+
);
|
|
3063
|
+
const sid = result.session_id;
|
|
3064
|
+
if (!sid) {
|
|
3065
|
+
throw new BrowserSessionError("No session ID returned from server");
|
|
3066
|
+
}
|
|
3067
|
+
return sid;
|
|
3068
|
+
} catch (error) {
|
|
3069
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3070
|
+
throw new BrowserSessionError(`Failed to create browser session: ${msg}`, { cause: error });
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
async getBrowserSession(sessionId) {
|
|
3074
|
+
this.ensureNotLocal("Browser session management");
|
|
3075
|
+
const safe = encodePathSegment(sessionId);
|
|
3076
|
+
try {
|
|
3077
|
+
return await this.httpClient.get(
|
|
3078
|
+
`/v1/browser/sessions/${safe}`,
|
|
3079
|
+
void 0,
|
|
3080
|
+
this.authHeaders()
|
|
3081
|
+
);
|
|
3082
|
+
} catch (error) {
|
|
3083
|
+
const status = error.statusCode;
|
|
3084
|
+
if (status === 404) {
|
|
3085
|
+
throw new BrowserSessionError("Browser session not found or expired", { cause: error });
|
|
3086
|
+
}
|
|
3087
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3088
|
+
throw new BrowserSessionError(`Failed to get browser session: ${msg}`, { cause: error });
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
async closeBrowserSession(sessionId) {
|
|
3092
|
+
this.ensureNotLocal("Browser session management");
|
|
3093
|
+
const safe = encodePathSegment(sessionId);
|
|
3094
|
+
try {
|
|
3095
|
+
await this.httpClient.delete(`/v1/browser/sessions/${safe}`, this.authHeaders());
|
|
3096
|
+
return true;
|
|
3097
|
+
} catch (error) {
|
|
3098
|
+
const status = error.statusCode;
|
|
3099
|
+
if (status === 404) {
|
|
3100
|
+
return true;
|
|
3101
|
+
}
|
|
3102
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3103
|
+
throw new BrowserSessionError(`Failed to close browser session: ${msg}`, { cause: error });
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
async listBrowserSessions() {
|
|
3107
|
+
this.ensureNotLocal("Browser session management");
|
|
3108
|
+
const result = await this.httpClient.get("/v1/browser/sessions/", void 0, this.authHeaders());
|
|
3109
|
+
if (Array.isArray(result)) {
|
|
3110
|
+
return result;
|
|
3111
|
+
}
|
|
3112
|
+
return result?.sessions || [];
|
|
3113
|
+
}
|
|
3114
|
+
async browserNavigate(url, sessionId, waitTimeout = 3e4) {
|
|
3115
|
+
if (!this.local) {
|
|
3116
|
+
if (!sessionId) {
|
|
3117
|
+
throw new BrowserSessionError("session_id is required in cloud mode");
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
const data = { url, wait_timeout: waitTimeout };
|
|
3121
|
+
if (!this.local && sessionId) {
|
|
3122
|
+
data.session_id = sessionId;
|
|
3123
|
+
}
|
|
3124
|
+
try {
|
|
3125
|
+
return await this.httpClient.post(
|
|
3126
|
+
"/v1/browser/interactions/navigate",
|
|
3127
|
+
data,
|
|
3128
|
+
this.authHeaders()
|
|
3129
|
+
);
|
|
3130
|
+
} catch (error) {
|
|
3131
|
+
const status = error.statusCode;
|
|
3132
|
+
if (status === 404) {
|
|
3133
|
+
throw new BrowserSessionError("Browser session not found or expired", { cause: error });
|
|
3134
|
+
}
|
|
3135
|
+
if (status === 408) {
|
|
3136
|
+
throw new BrowserTimeoutError("Navigation timeout", { cause: error });
|
|
3137
|
+
}
|
|
3138
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3139
|
+
throw new BrowserNavigationError(`Failed to navigate: ${msg}`, { cause: error });
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
async browserClick(selector, sessionId, force = false, timeout = 3e4) {
|
|
3143
|
+
this.ensureNotLocal("Browser click interaction");
|
|
3144
|
+
try {
|
|
3145
|
+
return await this.httpClient.post(
|
|
3146
|
+
"/v1/browser/interactions/click",
|
|
3147
|
+
{ selector, session_id: sessionId, force, timeout },
|
|
3148
|
+
this.authHeaders()
|
|
3149
|
+
);
|
|
3150
|
+
} catch (error) {
|
|
3151
|
+
const status = error.statusCode;
|
|
3152
|
+
if (status === 404) {
|
|
3153
|
+
throw new ElementNotFoundError(`Element not found: ${selector}`, selector, { cause: error });
|
|
3154
|
+
}
|
|
3155
|
+
if (status === 408) {
|
|
3156
|
+
throw new BrowserTimeoutError("Click timeout", { cause: error });
|
|
3157
|
+
}
|
|
3158
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3159
|
+
throw new BrowserInteractionError(`Failed to click element: ${msg}`, { cause: error });
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
async browserType(selector, text, sessionId, delay = 100, timeout = 3e4) {
|
|
3163
|
+
this.ensureNotLocal("Browser type interaction");
|
|
3164
|
+
try {
|
|
3165
|
+
return await this.httpClient.post(
|
|
3166
|
+
"/v1/browser/interactions/type",
|
|
3167
|
+
{ selector, text, session_id: sessionId, delay, timeout },
|
|
3168
|
+
this.authHeaders()
|
|
3169
|
+
);
|
|
3170
|
+
} catch (error) {
|
|
3171
|
+
const status = error.statusCode;
|
|
3172
|
+
if (status === 404) {
|
|
3173
|
+
throw new ElementNotFoundError(`Element not found: ${selector}`, selector, { cause: error });
|
|
3174
|
+
}
|
|
3175
|
+
if (status === 408) {
|
|
3176
|
+
throw new BrowserTimeoutError("Type timeout", { cause: error });
|
|
3177
|
+
}
|
|
3178
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3179
|
+
throw new BrowserInteractionError(`Failed to type text: ${msg}`, { cause: error });
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
async browserFill(selector, value, sessionId, timeout = 3e4) {
|
|
3183
|
+
this.ensureNotLocal("Browser fill interaction");
|
|
3184
|
+
try {
|
|
3185
|
+
return await this.httpClient.post(
|
|
3186
|
+
"/v1/browser/interactions/fill",
|
|
3187
|
+
{ selector, value, session_id: sessionId, timeout },
|
|
3188
|
+
this.authHeaders()
|
|
3189
|
+
);
|
|
3190
|
+
} catch (error) {
|
|
3191
|
+
const status = error.statusCode;
|
|
3192
|
+
if (status === 404) {
|
|
3193
|
+
throw new ElementNotFoundError(`Element not found: ${selector}`, selector, { cause: error });
|
|
3194
|
+
}
|
|
3195
|
+
if (status === 408) {
|
|
3196
|
+
throw new BrowserTimeoutError("Fill timeout", { cause: error });
|
|
3197
|
+
}
|
|
3198
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3199
|
+
throw new BrowserInteractionError(`Failed to fill form: ${msg}`, { cause: error });
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
async browserScroll(sessionId, selector, x, y) {
|
|
3203
|
+
this.ensureNotLocal("Browser scroll interaction");
|
|
3204
|
+
if (selector == null && x == null && y == null) {
|
|
3205
|
+
throw new BrowserInteractionError(
|
|
3206
|
+
"At least one of 'selector', 'x', or 'y' must be provided for scrolling."
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
const data = { session_id: sessionId };
|
|
3210
|
+
if (selector) {
|
|
3211
|
+
data.selector = selector;
|
|
3212
|
+
}
|
|
3213
|
+
if (x != null) {
|
|
3214
|
+
data.x = x;
|
|
3215
|
+
}
|
|
3216
|
+
if (y != null) {
|
|
3217
|
+
data.y = y;
|
|
3218
|
+
}
|
|
3219
|
+
try {
|
|
3220
|
+
return await this.httpClient.post(
|
|
3221
|
+
"/v1/browser/interactions/scroll",
|
|
3222
|
+
data,
|
|
3223
|
+
this.authHeaders()
|
|
3224
|
+
);
|
|
3225
|
+
} catch (error) {
|
|
3226
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3227
|
+
throw new BrowserInteractionError(`Failed to scroll: ${msg}`, { cause: error });
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
async browserWait(condition, sessionId, selector, timeout = 3e4) {
|
|
3231
|
+
this.ensureNotLocal("Browser wait interaction");
|
|
3232
|
+
const data = { condition, session_id: sessionId, timeout };
|
|
3233
|
+
if (selector) {
|
|
3234
|
+
data.selector = selector;
|
|
3235
|
+
}
|
|
3236
|
+
try {
|
|
3237
|
+
return await this.httpClient.post(
|
|
3238
|
+
"/v1/browser/interactions/wait",
|
|
3239
|
+
data,
|
|
3240
|
+
this.authHeaders()
|
|
3241
|
+
);
|
|
3242
|
+
} catch (error) {
|
|
3243
|
+
const status = error.statusCode;
|
|
3244
|
+
if (status === 408) {
|
|
3245
|
+
throw new BrowserTimeoutError(`Wait timeout for condition: ${condition}`, { cause: error });
|
|
3246
|
+
}
|
|
3247
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3248
|
+
throw new BrowserInteractionError(`Failed to wait for condition: ${msg}`, { cause: error });
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
async browserScreenshot(sessionId, fullPage = true, clip, format = "png", quality) {
|
|
3252
|
+
this.ensureNotLocal("Browser screenshot");
|
|
3253
|
+
const data = {
|
|
3254
|
+
session_id: sessionId,
|
|
3255
|
+
full_page: fullPage,
|
|
3256
|
+
format
|
|
3257
|
+
};
|
|
3258
|
+
if (clip) {
|
|
3259
|
+
data.clip = clip;
|
|
3260
|
+
}
|
|
3261
|
+
if (quality != null) {
|
|
3262
|
+
data.quality = quality;
|
|
3263
|
+
}
|
|
3264
|
+
try {
|
|
3265
|
+
const result = await this.httpClient.post(
|
|
3266
|
+
"/v1/browser/interactions/screenshot",
|
|
3267
|
+
data,
|
|
3268
|
+
this.authHeaders()
|
|
3269
|
+
);
|
|
3270
|
+
return result.screenshot || "";
|
|
3271
|
+
} catch (error) {
|
|
3272
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3273
|
+
throw new BrowserInteractionError(`Failed to take screenshot: ${msg}`, { cause: error });
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
async browserExtractElements(sessionId, selector, attributes) {
|
|
3277
|
+
this.ensureNotLocal("Browser element extraction");
|
|
3278
|
+
const data = { session_id: sessionId };
|
|
3279
|
+
if (selector) {
|
|
3280
|
+
data.selector = selector;
|
|
3281
|
+
}
|
|
3282
|
+
if (attributes?.length) {
|
|
3283
|
+
data.attributes = attributes;
|
|
3284
|
+
}
|
|
3285
|
+
try {
|
|
3286
|
+
const result = await this.httpClient.post(
|
|
3287
|
+
"/v1/browser/interactions/extract",
|
|
3288
|
+
data,
|
|
3289
|
+
this.authHeaders()
|
|
3290
|
+
);
|
|
3291
|
+
return result.elements || [];
|
|
3292
|
+
} catch (error) {
|
|
3293
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3294
|
+
throw new BrowserInteractionError(`Failed to extract elements: ${msg}`, { cause: error });
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
async browserExtractContent(sessionId, url, includeInteractive = true, includeAnchors = true, maxAnchors = 50) {
|
|
3298
|
+
if (!this.local) {
|
|
3299
|
+
if (!sessionId) {
|
|
3300
|
+
throw new BrowserSessionError("session_id is required in cloud mode");
|
|
3301
|
+
}
|
|
3302
|
+
} else if (!url) {
|
|
3303
|
+
throw new BrowserInteractionError("url is required in local mode");
|
|
3304
|
+
}
|
|
3305
|
+
const data = {
|
|
3306
|
+
include_interactive: includeInteractive,
|
|
3307
|
+
include_anchors: includeAnchors,
|
|
3308
|
+
max_anchors: maxAnchors
|
|
3309
|
+
};
|
|
3310
|
+
if (!this.local && sessionId) {
|
|
3311
|
+
data.session_id = sessionId;
|
|
3312
|
+
}
|
|
3313
|
+
if (this.local && url) {
|
|
3314
|
+
data.url = url;
|
|
3315
|
+
}
|
|
3316
|
+
try {
|
|
3317
|
+
return await this.httpClient.post(
|
|
3318
|
+
"/v1/browser/interactions/content",
|
|
3319
|
+
data,
|
|
3320
|
+
this.authHeaders()
|
|
3321
|
+
);
|
|
3322
|
+
} catch (error) {
|
|
3323
|
+
const status = error.statusCode;
|
|
3324
|
+
if (status === 404) {
|
|
3325
|
+
throw new BrowserSessionError("Browser session not found or expired", { cause: error });
|
|
3326
|
+
}
|
|
3327
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3328
|
+
throw new BrowserInteractionError(`Failed to extract content: ${msg}`, { cause: error });
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
2031
3331
|
/**
|
|
2032
3332
|
* Download a file from the remote VM
|
|
2033
3333
|
*/
|
|
@@ -2113,8 +3413,9 @@ var InstaVM = class {
|
|
|
2113
3413
|
if (!targetSessionId) {
|
|
2114
3414
|
throw new SessionError("Session ID not set. Please create a session first.");
|
|
2115
3415
|
}
|
|
3416
|
+
const safeSid = encodePathSegment(targetSessionId);
|
|
2116
3417
|
const response = await this.httpClient.post(
|
|
2117
|
-
`/v1/egress/session/${
|
|
3418
|
+
`/v1/egress/session/${safeSid}`,
|
|
2118
3419
|
{
|
|
2119
3420
|
allow_public_repos: options.allowPackageManagers ?? true,
|
|
2120
3421
|
allow_http: options.allowHttp ?? true,
|
|
@@ -2135,8 +3436,9 @@ var InstaVM = class {
|
|
|
2135
3436
|
if (!targetSessionId) {
|
|
2136
3437
|
throw new SessionError("Session ID not set. Please create a session first.");
|
|
2137
3438
|
}
|
|
3439
|
+
const safeSid = encodePathSegment(targetSessionId);
|
|
2138
3440
|
const response = await this.httpClient.get(
|
|
2139
|
-
`/v1/egress/session/${
|
|
3441
|
+
`/v1/egress/session/${safeSid}`,
|
|
2140
3442
|
void 0,
|
|
2141
3443
|
{ "X-API-Key": this.httpClient.apiKey }
|
|
2142
3444
|
);
|
|
@@ -2153,8 +3455,9 @@ var InstaVM = class {
|
|
|
2153
3455
|
*/
|
|
2154
3456
|
async setVmEgress(vmId, options = {}) {
|
|
2155
3457
|
this.ensureNotLocal("Egress policy management");
|
|
3458
|
+
const safeVmId = encodePathSegment(vmId);
|
|
2156
3459
|
const response = await this.httpClient.post(
|
|
2157
|
-
`/v1/egress/vm/${
|
|
3460
|
+
`/v1/egress/vm/${safeVmId}`,
|
|
2158
3461
|
{
|
|
2159
3462
|
allow_public_repos: options.allowPackageManagers ?? true,
|
|
2160
3463
|
allow_http: options.allowHttp ?? true,
|
|
@@ -2171,8 +3474,9 @@ var InstaVM = class {
|
|
|
2171
3474
|
*/
|
|
2172
3475
|
async getVmEgress(vmId) {
|
|
2173
3476
|
this.ensureNotLocal("Egress policy management");
|
|
3477
|
+
const safeVmId = encodePathSegment(vmId);
|
|
2174
3478
|
const response = await this.httpClient.get(
|
|
2175
|
-
`/v1/egress/vm/${
|
|
3479
|
+
`/v1/egress/vm/${safeVmId}`,
|
|
2176
3480
|
void 0,
|
|
2177
3481
|
{ "X-API-Key": this.httpClient.apiKey }
|
|
2178
3482
|
);
|
|
@@ -2260,19 +3564,29 @@ export {
|
|
|
2260
3564
|
BrowserSessionError,
|
|
2261
3565
|
BrowserTimeoutError,
|
|
2262
3566
|
ComputerUseManager,
|
|
3567
|
+
CreditsManager,
|
|
2263
3568
|
CustomDomainsManager,
|
|
2264
3569
|
ElementNotFoundError,
|
|
2265
3570
|
ExecutionError,
|
|
3571
|
+
ForbiddenError,
|
|
3572
|
+
INSTAVM_JS_SDK_VERSION,
|
|
2266
3573
|
InstaVM,
|
|
2267
3574
|
InstaVMError,
|
|
2268
3575
|
NetworkError,
|
|
3576
|
+
PTYManager,
|
|
2269
3577
|
QuotaExceededError,
|
|
2270
3578
|
RateLimitError,
|
|
3579
|
+
RecordingsManager,
|
|
2271
3580
|
SessionError,
|
|
2272
3581
|
SharesManager,
|
|
2273
3582
|
SnapshotsManager,
|
|
3583
|
+
TapeContext,
|
|
3584
|
+
TapesManager,
|
|
3585
|
+
ToolCallRecorder,
|
|
3586
|
+
ToolCallSpan,
|
|
2274
3587
|
UnsupportedOperationError,
|
|
2275
3588
|
VMsManager,
|
|
3589
|
+
VaultsManager,
|
|
2276
3590
|
VolumesManager,
|
|
2277
3591
|
WebhooksManager
|
|
2278
3592
|
};
|