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