instavm 0.11.0 → 0.15.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/dist/index.mjs CHANGED
@@ -1,5 +1,3 @@
1
- "use strict";
2
-
3
1
  // src/client/HTTPClient.ts
4
2
  import axios from "axios";
5
3
 
@@ -139,7 +137,7 @@ var HTTPClient = class {
139
137
  timeout: config.timeout,
140
138
  headers: {
141
139
  "Content-Type": "application/json",
142
- "User-Agent": "instavm-js-sdk/0.11.0"
140
+ "User-Agent": "instavm-js-sdk/0.15.0"
143
141
  }
144
142
  });
145
143
  this.setupInterceptors();
@@ -912,10 +910,14 @@ var BrowserManager = class {
912
910
 
913
911
  // src/utils/path.ts
914
912
  function encodePathSegment(value) {
915
- return encodeURIComponent(String(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);
916
918
  }
917
- function encodePathSegments(path) {
918
- const segments = path.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
919
+ function encodePathSegments(path2) {
920
+ const segments = path2.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
919
921
  if (segments.some((segment) => segment === "..")) {
920
922
  throw new Error("Path traversal not allowed in proxy path");
921
923
  }
@@ -1015,6 +1017,37 @@ var VMsManager = class {
1015
1017
  headers: { "X-API-Key": this.httpClient.apiKey }
1016
1018
  });
1017
1019
  }
1020
+ async mountVolume(vmId, payload, wait = true) {
1021
+ this.ensureCloud("VM volume mount");
1022
+ const safeVmId = encodePathSegment(vmId);
1023
+ return this.httpClient.request({
1024
+ method: "POST",
1025
+ url: `/v1/vms/${safeVmId}/volumes`,
1026
+ params: { wait },
1027
+ data: payload,
1028
+ headers: { "X-API-Key": this.httpClient.apiKey }
1029
+ });
1030
+ }
1031
+ async listVolumes(vmId) {
1032
+ this.ensureCloud("VM volume listing");
1033
+ const safeVmId = encodePathSegment(vmId);
1034
+ return this.httpClient.get(
1035
+ `/v1/vms/${safeVmId}/volumes`,
1036
+ void 0,
1037
+ { "X-API-Key": this.httpClient.apiKey }
1038
+ );
1039
+ }
1040
+ async unmountVolume(vmId, volumeId, mountPath, wait = true) {
1041
+ this.ensureCloud("VM volume unmount");
1042
+ const safeVmId = encodePathSegment(vmId);
1043
+ const safeVolumeId = encodePathSegment(volumeId);
1044
+ return this.httpClient.request({
1045
+ method: "DELETE",
1046
+ url: `/v1/vms/${safeVmId}/volumes/${safeVolumeId}`,
1047
+ params: { mount_path: mountPath, wait },
1048
+ headers: { "X-API-Key": this.httpClient.apiKey }
1049
+ });
1050
+ }
1018
1051
  };
1019
1052
 
1020
1053
  // src/client/SnapshotsManager.ts
@@ -1187,10 +1220,10 @@ var ComputerUseManager = class {
1187
1220
  { "X-API-Key": this.httpClient.apiKey }
1188
1221
  );
1189
1222
  }
1190
- async proxy(sessionId, path, method = "GET", options = {}) {
1223
+ async proxy(sessionId, path2, method = "GET", options = {}) {
1191
1224
  this.ensureCloud("Computer-use proxy");
1192
1225
  const safeSessionId = encodePathSegment(sessionId);
1193
- const cleanPath = encodePathSegments(path);
1226
+ const cleanPath = encodePathSegments(path2);
1194
1227
  return this.httpClient.request({
1195
1228
  method,
1196
1229
  url: `/v1/computeruse/${safeSessionId}/${cleanPath}`,
@@ -1199,23 +1232,39 @@ var ComputerUseManager = class {
1199
1232
  headers: { "X-API-Key": this.httpClient.apiKey }
1200
1233
  });
1201
1234
  }
1202
- async get(sessionId, path, params) {
1203
- return this.proxy(sessionId, path, "GET", { params });
1235
+ async get(sessionId, path2, params) {
1236
+ return this.proxy(sessionId, path2, "GET", { params });
1204
1237
  }
1205
- async post(sessionId, path, body) {
1206
- return this.proxy(sessionId, path, "POST", { body });
1238
+ async post(sessionId, path2, body) {
1239
+ return this.proxy(sessionId, path2, "POST", { body });
1207
1240
  }
1208
- async put(sessionId, path, body) {
1209
- return this.proxy(sessionId, path, "PUT", { body });
1241
+ async put(sessionId, path2, body) {
1242
+ return this.proxy(sessionId, path2, "PUT", { body });
1210
1243
  }
1211
- async patch(sessionId, path, body) {
1212
- return this.proxy(sessionId, path, "PATCH", { body });
1244
+ async patch(sessionId, path2, body) {
1245
+ return this.proxy(sessionId, path2, "PATCH", { body });
1213
1246
  }
1214
- async delete(sessionId, path, params) {
1215
- return this.proxy(sessionId, path, "DELETE", { params });
1247
+ async delete(sessionId, path2, params) {
1248
+ return this.proxy(sessionId, path2, "DELETE", { params });
1216
1249
  }
1217
- async options(sessionId, path, params) {
1218
- return this.proxy(sessionId, path, "OPTIONS", { params });
1250
+ async options(sessionId, path2, params) {
1251
+ return this.proxy(sessionId, path2, "OPTIONS", { params });
1252
+ }
1253
+ async head(sessionId, path2, params) {
1254
+ return this.proxy(sessionId, path2, "HEAD", { params });
1255
+ }
1256
+ /**
1257
+ * Get the VNC WebSocket URL for a computer-use session.
1258
+ * Returns the URL to connect to; the actual connection requires a WebSocket client.
1259
+ */
1260
+ async vncWebsockify(sessionId) {
1261
+ this.ensureCloud("Computer-use VNC websockify");
1262
+ const safeSessionId = encodePathSegment(sessionId);
1263
+ return this.httpClient.get(
1264
+ `/v1/computeruse/${safeSessionId}/vnc/websockify`,
1265
+ void 0,
1266
+ { "X-API-Key": this.httpClient.apiKey }
1267
+ );
1219
1268
  }
1220
1269
  };
1221
1270
 
@@ -1417,8 +1466,164 @@ var WebhooksManager = class {
1417
1466
  }
1418
1467
  };
1419
1468
 
1420
- // src/client/InstaVM.ts
1469
+ // src/client/VolumesManager.ts
1470
+ import fs from "fs";
1471
+ import path from "path";
1421
1472
  import FormData from "form-data";
1473
+ var VolumesManager = class {
1474
+ constructor(httpClient, local = false) {
1475
+ this.httpClient = httpClient;
1476
+ this.local = local;
1477
+ }
1478
+ ensureCloud(operation) {
1479
+ if (this.local) {
1480
+ throw new UnsupportedOperationError(
1481
+ `${operation} is not supported in local mode.`
1482
+ );
1483
+ }
1484
+ }
1485
+ async create(payload) {
1486
+ this.ensureCloud("Volume creation");
1487
+ return this.httpClient.post(
1488
+ "/v1/volumes",
1489
+ payload,
1490
+ { "X-API-Key": this.httpClient.apiKey }
1491
+ );
1492
+ }
1493
+ async list(refreshUsage = false) {
1494
+ this.ensureCloud("Volume listing");
1495
+ return this.httpClient.get(
1496
+ "/v1/volumes",
1497
+ { refresh_usage: refreshUsage },
1498
+ { "X-API-Key": this.httpClient.apiKey }
1499
+ );
1500
+ }
1501
+ async get(volumeId, refreshUsage = false) {
1502
+ this.ensureCloud("Volume lookup");
1503
+ const safeVolumeId = encodePathSegment(volumeId);
1504
+ return this.httpClient.get(
1505
+ `/v1/volumes/${safeVolumeId}`,
1506
+ { refresh_usage: refreshUsage },
1507
+ { "X-API-Key": this.httpClient.apiKey }
1508
+ );
1509
+ }
1510
+ async update(volumeId, payload) {
1511
+ this.ensureCloud("Volume update");
1512
+ const safeVolumeId = encodePathSegment(volumeId);
1513
+ return this.httpClient.patch(
1514
+ `/v1/volumes/${safeVolumeId}`,
1515
+ payload,
1516
+ { "X-API-Key": this.httpClient.apiKey }
1517
+ );
1518
+ }
1519
+ async delete(volumeId) {
1520
+ this.ensureCloud("Volume deletion");
1521
+ const safeVolumeId = encodePathSegment(volumeId);
1522
+ return this.httpClient.delete(
1523
+ `/v1/volumes/${safeVolumeId}`,
1524
+ { "X-API-Key": this.httpClient.apiKey }
1525
+ );
1526
+ }
1527
+ async createCheckpoint(volumeId, payload = {}) {
1528
+ this.ensureCloud("Volume checkpoint creation");
1529
+ const safeVolumeId = encodePathSegment(volumeId);
1530
+ return this.httpClient.post(
1531
+ `/v1/volumes/${safeVolumeId}/checkpoints`,
1532
+ payload,
1533
+ { "X-API-Key": this.httpClient.apiKey }
1534
+ );
1535
+ }
1536
+ async listCheckpoints(volumeId) {
1537
+ this.ensureCloud("Volume checkpoint listing");
1538
+ const safeVolumeId = encodePathSegment(volumeId);
1539
+ return this.httpClient.get(
1540
+ `/v1/volumes/${safeVolumeId}/checkpoints`,
1541
+ void 0,
1542
+ { "X-API-Key": this.httpClient.apiKey }
1543
+ );
1544
+ }
1545
+ async deleteCheckpoint(volumeId, checkpointId) {
1546
+ this.ensureCloud("Volume checkpoint deletion");
1547
+ const safeVolumeId = encodePathSegment(volumeId);
1548
+ const safeCheckpointId = encodePathSegment(checkpointId);
1549
+ return this.httpClient.delete(
1550
+ `/v1/volumes/${safeVolumeId}/checkpoints/${safeCheckpointId}`,
1551
+ { "X-API-Key": this.httpClient.apiKey }
1552
+ );
1553
+ }
1554
+ async uploadFile(volumeId, payload) {
1555
+ this.ensureCloud("Volume file upload");
1556
+ if (!payload.path || payload.path.trim().length === 0) {
1557
+ throw new Error("payload.path is required");
1558
+ }
1559
+ const hasFilePath = typeof payload.filePath === "string" && payload.filePath.length > 0;
1560
+ const hasFileContent = payload.fileContent !== void 0;
1561
+ if (hasFilePath === hasFileContent) {
1562
+ throw new Error("Provide exactly one of payload.filePath or payload.fileContent");
1563
+ }
1564
+ const safeVolumeId = encodePathSegment(volumeId);
1565
+ const formData = new FormData();
1566
+ if (hasFilePath) {
1567
+ const sourcePath = payload.filePath;
1568
+ const filename = payload.filename || path.basename(sourcePath);
1569
+ formData.append("file", fs.createReadStream(sourcePath), { filename });
1570
+ } else {
1571
+ const filename = payload.filename || "upload.bin";
1572
+ const raw = payload.fileContent;
1573
+ const buffer = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
1574
+ formData.append("file", buffer, { filename });
1575
+ }
1576
+ formData.append("path", payload.path);
1577
+ if (payload.overwrite !== void 0) {
1578
+ formData.append("overwrite", String(payload.overwrite));
1579
+ }
1580
+ return this.httpClient.postFormData(
1581
+ `/v1/volumes/${safeVolumeId}/files/upload`,
1582
+ formData
1583
+ );
1584
+ }
1585
+ async downloadFile(volumeId, path2) {
1586
+ this.ensureCloud("Volume file download");
1587
+ const safeVolumeId = encodePathSegment(volumeId);
1588
+ const response = await this.httpClient.post(
1589
+ `/v1/volumes/${safeVolumeId}/files/download`,
1590
+ { path: path2 },
1591
+ { "X-API-Key": this.httpClient.apiKey }
1592
+ );
1593
+ return {
1594
+ path: response.path,
1595
+ filename: response.filename,
1596
+ size: response.size,
1597
+ content: Buffer.from(response.content || "", "base64")
1598
+ };
1599
+ }
1600
+ async listFiles(volumeId, options = {}) {
1601
+ this.ensureCloud("Volume file listing");
1602
+ const safeVolumeId = encodePathSegment(volumeId);
1603
+ return this.httpClient.get(
1604
+ `/v1/volumes/${safeVolumeId}/files`,
1605
+ {
1606
+ prefix: options.prefix ?? "",
1607
+ recursive: options.recursive ?? true,
1608
+ limit: options.limit ?? 1e3
1609
+ },
1610
+ { "X-API-Key": this.httpClient.apiKey }
1611
+ );
1612
+ }
1613
+ async deleteFile(volumeId, targetPath) {
1614
+ this.ensureCloud("Volume file deletion");
1615
+ const safeVolumeId = encodePathSegment(volumeId);
1616
+ return this.httpClient.request({
1617
+ method: "DELETE",
1618
+ url: `/v1/volumes/${safeVolumeId}/files`,
1619
+ params: { path: targetPath },
1620
+ headers: { "X-API-Key": this.httpClient.apiKey }
1621
+ });
1622
+ }
1623
+ };
1624
+
1625
+ // src/client/InstaVM.ts
1626
+ import FormData2 from "form-data";
1422
1627
  var InstaVM = class {
1423
1628
  constructor(apiKey, options = {}) {
1424
1629
  this._sessionId = null;
@@ -1449,6 +1654,7 @@ var InstaVM = class {
1449
1654
  this.apiKeys = new APIKeysManager(this.httpClient, this.local);
1450
1655
  this.audit = new AuditManager(this.httpClient, this.local);
1451
1656
  this.webhooks = new WebhooksManager(this.httpClient, this.local);
1657
+ this.volumes = new VolumesManager(this.httpClient, this.local);
1452
1658
  }
1453
1659
  /**
1454
1660
  * Ensure operation is not called in local mode
@@ -1612,7 +1818,7 @@ var InstaVM = class {
1612
1818
  if (!sessionId) {
1613
1819
  throw new SessionError("Session ID not set. Please create a session first using createSession().");
1614
1820
  }
1615
- const formData = new FormData();
1821
+ const formData = new FormData2();
1616
1822
  for (const file of files) {
1617
1823
  if (Buffer.isBuffer(file.content)) {
1618
1824
  formData.append("files", file.content, file.name);
@@ -1724,6 +1930,49 @@ var InstaVM = class {
1724
1930
  return false;
1725
1931
  }
1726
1932
  }
1933
+ /**
1934
+ * Get the app URL for a session
1935
+ *
1936
+ * @param sessionId - Session ID (uses current session if not provided)
1937
+ * @param port - Port to expose (1-65535, default: 80)
1938
+ */
1939
+ async getSessionAppUrl(sessionId, port) {
1940
+ this.ensureNotLocal("Session app URL");
1941
+ const targetSessionId = sessionId || this._sessionId;
1942
+ if (!targetSessionId) {
1943
+ throw new SessionError("Session ID not set. Please create a session first.");
1944
+ }
1945
+ const params = {};
1946
+ if (port !== void 0) {
1947
+ params.port = port;
1948
+ }
1949
+ return this.httpClient.get(
1950
+ `/v1/sessions/app-url/${targetSessionId}`,
1951
+ params,
1952
+ { "X-API-Key": this.httpClient.apiKey }
1953
+ );
1954
+ }
1955
+ /**
1956
+ * List sandbox records with optional filtering
1957
+ *
1958
+ * @param options.metadata - JSON-serializable metadata filters
1959
+ * @param options.limit - Maximum number of results (1-1000, default: 100)
1960
+ */
1961
+ async listSandboxes(options = {}) {
1962
+ this.ensureNotLocal("Sandbox listing");
1963
+ const params = {};
1964
+ if (options.metadata !== void 0) {
1965
+ params.metadata = JSON.stringify(options.metadata);
1966
+ }
1967
+ if (options.limit !== void 0) {
1968
+ params.limit = options.limit;
1969
+ }
1970
+ return this.httpClient.get(
1971
+ "/v1/sessions/sandboxes",
1972
+ params,
1973
+ { "X-API-Key": this.httpClient.apiKey }
1974
+ );
1975
+ }
1727
1976
  /**
1728
1977
  * Get usage statistics for a session
1729
1978
  */
@@ -1735,7 +1984,9 @@ var InstaVM = class {
1735
1984
  }
1736
1985
  try {
1737
1986
  const response = await this.httpClient.get(
1738
- `/v1/sessions/usage/${targetSessionId}`
1987
+ `/v1/sessions/usage/${targetSessionId}`,
1988
+ void 0,
1989
+ { "X-API-Key": this.httpClient.apiKey }
1739
1990
  );
1740
1991
  return {
1741
1992
  sessionsUsed: response.sessions_used || 0,
@@ -1751,6 +2002,32 @@ var InstaVM = class {
1751
2002
  );
1752
2003
  }
1753
2004
  }
2005
+ /**
2006
+ * Get the current user profile.
2007
+ */
2008
+ async getCurrentUser() {
2009
+ this.ensureNotLocal("User profile lookup");
2010
+ return this.httpClient.get(
2011
+ "/v1/me",
2012
+ void 0,
2013
+ { "X-API-Key": this.httpClient.apiKey }
2014
+ );
2015
+ }
2016
+ /**
2017
+ * Get the current status of a session.
2018
+ */
2019
+ async getSessionStatus(sessionId) {
2020
+ this.ensureNotLocal("Session management");
2021
+ const targetSessionId = sessionId || this._sessionId;
2022
+ if (!targetSessionId) {
2023
+ throw new SessionError("Session ID not set. Please create a session first.");
2024
+ }
2025
+ return this.httpClient.get(
2026
+ `/v1/sessions/status/${targetSessionId}`,
2027
+ void 0,
2028
+ { "X-API-Key": this.httpClient.apiKey }
2029
+ );
2030
+ }
1754
2031
  /**
1755
2032
  * Download a file from the remote VM
1756
2033
  */
@@ -1996,6 +2273,7 @@ export {
1996
2273
  SnapshotsManager,
1997
2274
  UnsupportedOperationError,
1998
2275
  VMsManager,
2276
+ VolumesManager,
1999
2277
  WebhooksManager
2000
2278
  };
2001
2279
  //# sourceMappingURL=index.mjs.map