instavm 0.11.0 → 0.13.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.js CHANGED
@@ -55,6 +55,7 @@ __export(index_exports, {
55
55
  SnapshotsManager: () => SnapshotsManager,
56
56
  UnsupportedOperationError: () => UnsupportedOperationError,
57
57
  VMsManager: () => VMsManager,
58
+ VolumesManager: () => VolumesManager,
58
59
  WebhooksManager: () => WebhooksManager
59
60
  });
60
61
  module.exports = __toCommonJS(index_exports);
@@ -198,7 +199,7 @@ var HTTPClient = class {
198
199
  timeout: config.timeout,
199
200
  headers: {
200
201
  "Content-Type": "application/json",
201
- "User-Agent": "instavm-js-sdk/0.11.0"
202
+ "User-Agent": "instavm-js-sdk/0.12.0"
202
203
  }
203
204
  });
204
205
  this.setupInterceptors();
@@ -971,10 +972,14 @@ var BrowserManager = class {
971
972
 
972
973
  // src/utils/path.ts
973
974
  function encodePathSegment(value) {
974
- return encodeURIComponent(String(value));
975
+ const segment = String(value);
976
+ if (segment === "." || segment === "..") {
977
+ throw new Error("Path traversal not allowed in path segment");
978
+ }
979
+ return encodeURIComponent(segment);
975
980
  }
976
- function encodePathSegments(path) {
977
- const segments = path.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
981
+ function encodePathSegments(path2) {
982
+ const segments = path2.replace(/^\/+/, "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
978
983
  if (segments.some((segment) => segment === "..")) {
979
984
  throw new Error("Path traversal not allowed in proxy path");
980
985
  }
@@ -1074,6 +1079,37 @@ var VMsManager = class {
1074
1079
  headers: { "X-API-Key": this.httpClient.apiKey }
1075
1080
  });
1076
1081
  }
1082
+ async mountVolume(vmId, payload, wait = true) {
1083
+ this.ensureCloud("VM volume mount");
1084
+ const safeVmId = encodePathSegment(vmId);
1085
+ return this.httpClient.request({
1086
+ method: "POST",
1087
+ url: `/v1/vms/${safeVmId}/volumes`,
1088
+ params: { wait },
1089
+ data: payload,
1090
+ headers: { "X-API-Key": this.httpClient.apiKey }
1091
+ });
1092
+ }
1093
+ async listVolumes(vmId) {
1094
+ this.ensureCloud("VM volume listing");
1095
+ const safeVmId = encodePathSegment(vmId);
1096
+ return this.httpClient.get(
1097
+ `/v1/vms/${safeVmId}/volumes`,
1098
+ void 0,
1099
+ { "X-API-Key": this.httpClient.apiKey }
1100
+ );
1101
+ }
1102
+ async unmountVolume(vmId, volumeId, mountPath, wait = true) {
1103
+ this.ensureCloud("VM volume unmount");
1104
+ const safeVmId = encodePathSegment(vmId);
1105
+ const safeVolumeId = encodePathSegment(volumeId);
1106
+ return this.httpClient.request({
1107
+ method: "DELETE",
1108
+ url: `/v1/vms/${safeVmId}/volumes/${safeVolumeId}`,
1109
+ params: { mount_path: mountPath, wait },
1110
+ headers: { "X-API-Key": this.httpClient.apiKey }
1111
+ });
1112
+ }
1077
1113
  };
1078
1114
 
1079
1115
  // src/client/SnapshotsManager.ts
@@ -1246,10 +1282,10 @@ var ComputerUseManager = class {
1246
1282
  { "X-API-Key": this.httpClient.apiKey }
1247
1283
  );
1248
1284
  }
1249
- async proxy(sessionId, path, method = "GET", options = {}) {
1285
+ async proxy(sessionId, path2, method = "GET", options = {}) {
1250
1286
  this.ensureCloud("Computer-use proxy");
1251
1287
  const safeSessionId = encodePathSegment(sessionId);
1252
- const cleanPath = encodePathSegments(path);
1288
+ const cleanPath = encodePathSegments(path2);
1253
1289
  return this.httpClient.request({
1254
1290
  method,
1255
1291
  url: `/v1/computeruse/${safeSessionId}/${cleanPath}`,
@@ -1258,23 +1294,39 @@ var ComputerUseManager = class {
1258
1294
  headers: { "X-API-Key": this.httpClient.apiKey }
1259
1295
  });
1260
1296
  }
1261
- async get(sessionId, path, params) {
1262
- return this.proxy(sessionId, path, "GET", { params });
1297
+ async get(sessionId, path2, params) {
1298
+ return this.proxy(sessionId, path2, "GET", { params });
1263
1299
  }
1264
- async post(sessionId, path, body) {
1265
- return this.proxy(sessionId, path, "POST", { body });
1300
+ async post(sessionId, path2, body) {
1301
+ return this.proxy(sessionId, path2, "POST", { body });
1266
1302
  }
1267
- async put(sessionId, path, body) {
1268
- return this.proxy(sessionId, path, "PUT", { body });
1303
+ async put(sessionId, path2, body) {
1304
+ return this.proxy(sessionId, path2, "PUT", { body });
1269
1305
  }
1270
- async patch(sessionId, path, body) {
1271
- return this.proxy(sessionId, path, "PATCH", { body });
1306
+ async patch(sessionId, path2, body) {
1307
+ return this.proxy(sessionId, path2, "PATCH", { body });
1272
1308
  }
1273
- async delete(sessionId, path, params) {
1274
- return this.proxy(sessionId, path, "DELETE", { params });
1309
+ async delete(sessionId, path2, params) {
1310
+ return this.proxy(sessionId, path2, "DELETE", { params });
1275
1311
  }
1276
- async options(sessionId, path, params) {
1277
- return this.proxy(sessionId, path, "OPTIONS", { params });
1312
+ async options(sessionId, path2, params) {
1313
+ return this.proxy(sessionId, path2, "OPTIONS", { params });
1314
+ }
1315
+ async head(sessionId, path2, params) {
1316
+ return this.proxy(sessionId, path2, "HEAD", { params });
1317
+ }
1318
+ /**
1319
+ * Get the VNC WebSocket URL for a computer-use session.
1320
+ * Returns the URL to connect to; the actual connection requires a WebSocket client.
1321
+ */
1322
+ async vncWebsockify(sessionId) {
1323
+ this.ensureCloud("Computer-use VNC websockify");
1324
+ const safeSessionId = encodePathSegment(sessionId);
1325
+ return this.httpClient.get(
1326
+ `/v1/computeruse/${safeSessionId}/vnc/websockify`,
1327
+ void 0,
1328
+ { "X-API-Key": this.httpClient.apiKey }
1329
+ );
1278
1330
  }
1279
1331
  };
1280
1332
 
@@ -1476,8 +1528,164 @@ var WebhooksManager = class {
1476
1528
  }
1477
1529
  };
1478
1530
 
1479
- // src/client/InstaVM.ts
1531
+ // src/client/VolumesManager.ts
1532
+ var import_fs = __toESM(require("fs"));
1533
+ var import_path9 = __toESM(require("path"));
1480
1534
  var import_form_data = __toESM(require("form-data"));
1535
+ var VolumesManager = class {
1536
+ constructor(httpClient, local = false) {
1537
+ this.httpClient = httpClient;
1538
+ this.local = local;
1539
+ }
1540
+ ensureCloud(operation) {
1541
+ if (this.local) {
1542
+ throw new UnsupportedOperationError(
1543
+ `${operation} is not supported in local mode.`
1544
+ );
1545
+ }
1546
+ }
1547
+ async create(payload) {
1548
+ this.ensureCloud("Volume creation");
1549
+ return this.httpClient.post(
1550
+ "/v1/volumes",
1551
+ payload,
1552
+ { "X-API-Key": this.httpClient.apiKey }
1553
+ );
1554
+ }
1555
+ async list(refreshUsage = false) {
1556
+ this.ensureCloud("Volume listing");
1557
+ return this.httpClient.get(
1558
+ "/v1/volumes",
1559
+ { refresh_usage: refreshUsage },
1560
+ { "X-API-Key": this.httpClient.apiKey }
1561
+ );
1562
+ }
1563
+ async get(volumeId, refreshUsage = false) {
1564
+ this.ensureCloud("Volume lookup");
1565
+ const safeVolumeId = encodePathSegment(volumeId);
1566
+ return this.httpClient.get(
1567
+ `/v1/volumes/${safeVolumeId}`,
1568
+ { refresh_usage: refreshUsage },
1569
+ { "X-API-Key": this.httpClient.apiKey }
1570
+ );
1571
+ }
1572
+ async update(volumeId, payload) {
1573
+ this.ensureCloud("Volume update");
1574
+ const safeVolumeId = encodePathSegment(volumeId);
1575
+ return this.httpClient.patch(
1576
+ `/v1/volumes/${safeVolumeId}`,
1577
+ payload,
1578
+ { "X-API-Key": this.httpClient.apiKey }
1579
+ );
1580
+ }
1581
+ async delete(volumeId) {
1582
+ this.ensureCloud("Volume deletion");
1583
+ const safeVolumeId = encodePathSegment(volumeId);
1584
+ return this.httpClient.delete(
1585
+ `/v1/volumes/${safeVolumeId}`,
1586
+ { "X-API-Key": this.httpClient.apiKey }
1587
+ );
1588
+ }
1589
+ async createCheckpoint(volumeId, payload = {}) {
1590
+ this.ensureCloud("Volume checkpoint creation");
1591
+ const safeVolumeId = encodePathSegment(volumeId);
1592
+ return this.httpClient.post(
1593
+ `/v1/volumes/${safeVolumeId}/checkpoints`,
1594
+ payload,
1595
+ { "X-API-Key": this.httpClient.apiKey }
1596
+ );
1597
+ }
1598
+ async listCheckpoints(volumeId) {
1599
+ this.ensureCloud("Volume checkpoint listing");
1600
+ const safeVolumeId = encodePathSegment(volumeId);
1601
+ return this.httpClient.get(
1602
+ `/v1/volumes/${safeVolumeId}/checkpoints`,
1603
+ void 0,
1604
+ { "X-API-Key": this.httpClient.apiKey }
1605
+ );
1606
+ }
1607
+ async deleteCheckpoint(volumeId, checkpointId) {
1608
+ this.ensureCloud("Volume checkpoint deletion");
1609
+ const safeVolumeId = encodePathSegment(volumeId);
1610
+ const safeCheckpointId = encodePathSegment(checkpointId);
1611
+ return this.httpClient.delete(
1612
+ `/v1/volumes/${safeVolumeId}/checkpoints/${safeCheckpointId}`,
1613
+ { "X-API-Key": this.httpClient.apiKey }
1614
+ );
1615
+ }
1616
+ async uploadFile(volumeId, payload) {
1617
+ this.ensureCloud("Volume file upload");
1618
+ if (!payload.path || payload.path.trim().length === 0) {
1619
+ throw new Error("payload.path is required");
1620
+ }
1621
+ const hasFilePath = typeof payload.filePath === "string" && payload.filePath.length > 0;
1622
+ const hasFileContent = payload.fileContent !== void 0;
1623
+ if (hasFilePath === hasFileContent) {
1624
+ throw new Error("Provide exactly one of payload.filePath or payload.fileContent");
1625
+ }
1626
+ const safeVolumeId = encodePathSegment(volumeId);
1627
+ const formData = new import_form_data.default();
1628
+ if (hasFilePath) {
1629
+ const sourcePath = payload.filePath;
1630
+ const filename = payload.filename || import_path9.default.basename(sourcePath);
1631
+ formData.append("file", import_fs.default.createReadStream(sourcePath), { filename });
1632
+ } else {
1633
+ const filename = payload.filename || "upload.bin";
1634
+ const raw = payload.fileContent;
1635
+ const buffer = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
1636
+ formData.append("file", buffer, { filename });
1637
+ }
1638
+ formData.append("path", payload.path);
1639
+ if (payload.overwrite !== void 0) {
1640
+ formData.append("overwrite", String(payload.overwrite));
1641
+ }
1642
+ return this.httpClient.postFormData(
1643
+ `/v1/volumes/${safeVolumeId}/files/upload`,
1644
+ formData
1645
+ );
1646
+ }
1647
+ async downloadFile(volumeId, path2) {
1648
+ this.ensureCloud("Volume file download");
1649
+ const safeVolumeId = encodePathSegment(volumeId);
1650
+ const response = await this.httpClient.post(
1651
+ `/v1/volumes/${safeVolumeId}/files/download`,
1652
+ { path: path2 },
1653
+ { "X-API-Key": this.httpClient.apiKey }
1654
+ );
1655
+ return {
1656
+ path: response.path,
1657
+ filename: response.filename,
1658
+ size: response.size,
1659
+ content: Buffer.from(response.content || "", "base64")
1660
+ };
1661
+ }
1662
+ async listFiles(volumeId, options = {}) {
1663
+ this.ensureCloud("Volume file listing");
1664
+ const safeVolumeId = encodePathSegment(volumeId);
1665
+ return this.httpClient.get(
1666
+ `/v1/volumes/${safeVolumeId}/files`,
1667
+ {
1668
+ prefix: options.prefix ?? "",
1669
+ recursive: options.recursive ?? true,
1670
+ limit: options.limit ?? 1e3
1671
+ },
1672
+ { "X-API-Key": this.httpClient.apiKey }
1673
+ );
1674
+ }
1675
+ async deleteFile(volumeId, targetPath) {
1676
+ this.ensureCloud("Volume file deletion");
1677
+ const safeVolumeId = encodePathSegment(volumeId);
1678
+ return this.httpClient.request({
1679
+ method: "DELETE",
1680
+ url: `/v1/volumes/${safeVolumeId}/files`,
1681
+ params: { path: targetPath },
1682
+ headers: { "X-API-Key": this.httpClient.apiKey }
1683
+ });
1684
+ }
1685
+ };
1686
+
1687
+ // src/client/InstaVM.ts
1688
+ var import_form_data2 = __toESM(require("form-data"));
1481
1689
  var InstaVM = class {
1482
1690
  constructor(apiKey, options = {}) {
1483
1691
  this._sessionId = null;
@@ -1508,6 +1716,7 @@ var InstaVM = class {
1508
1716
  this.apiKeys = new APIKeysManager(this.httpClient, this.local);
1509
1717
  this.audit = new AuditManager(this.httpClient, this.local);
1510
1718
  this.webhooks = new WebhooksManager(this.httpClient, this.local);
1719
+ this.volumes = new VolumesManager(this.httpClient, this.local);
1511
1720
  }
1512
1721
  /**
1513
1722
  * Ensure operation is not called in local mode
@@ -1671,7 +1880,7 @@ var InstaVM = class {
1671
1880
  if (!sessionId) {
1672
1881
  throw new SessionError("Session ID not set. Please create a session first using createSession().");
1673
1882
  }
1674
- const formData = new import_form_data.default();
1883
+ const formData = new import_form_data2.default();
1675
1884
  for (const file of files) {
1676
1885
  if (Buffer.isBuffer(file.content)) {
1677
1886
  formData.append("files", file.content, file.name);
@@ -1783,6 +1992,49 @@ var InstaVM = class {
1783
1992
  return false;
1784
1993
  }
1785
1994
  }
1995
+ /**
1996
+ * Get the app URL for a session
1997
+ *
1998
+ * @param sessionId - Session ID (uses current session if not provided)
1999
+ * @param port - Port to expose (1-65535, default: 80)
2000
+ */
2001
+ async getSessionAppUrl(sessionId, port) {
2002
+ this.ensureNotLocal("Session app URL");
2003
+ const targetSessionId = sessionId || this._sessionId;
2004
+ if (!targetSessionId) {
2005
+ throw new SessionError("Session ID not set. Please create a session first.");
2006
+ }
2007
+ const params = {};
2008
+ if (port !== void 0) {
2009
+ params.port = port;
2010
+ }
2011
+ return this.httpClient.get(
2012
+ `/v1/sessions/app-url/${targetSessionId}`,
2013
+ params,
2014
+ { "X-API-Key": this.httpClient.apiKey }
2015
+ );
2016
+ }
2017
+ /**
2018
+ * List sandbox records with optional filtering
2019
+ *
2020
+ * @param options.metadata - JSON-serializable metadata filters
2021
+ * @param options.limit - Maximum number of results (1-1000, default: 100)
2022
+ */
2023
+ async listSandboxes(options = {}) {
2024
+ this.ensureNotLocal("Sandbox listing");
2025
+ const params = {};
2026
+ if (options.metadata !== void 0) {
2027
+ params.metadata = JSON.stringify(options.metadata);
2028
+ }
2029
+ if (options.limit !== void 0) {
2030
+ params.limit = options.limit;
2031
+ }
2032
+ return this.httpClient.get(
2033
+ "/v1/sessions/sandboxes",
2034
+ params,
2035
+ { "X-API-Key": this.httpClient.apiKey }
2036
+ );
2037
+ }
1786
2038
  /**
1787
2039
  * Get usage statistics for a session
1788
2040
  */
@@ -2056,6 +2308,7 @@ var InstaVM = class {
2056
2308
  SnapshotsManager,
2057
2309
  UnsupportedOperationError,
2058
2310
  VMsManager,
2311
+ VolumesManager,
2059
2312
  WebhooksManager
2060
2313
  });
2061
2314
  //# sourceMappingURL=index.js.map