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/LICENSE +21 -0
- package/README.md +219 -132
- package/dist/index.d.mts +116 -3
- package/dist/index.d.ts +116 -3
- package/dist/index.js +273 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +272 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -7
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.
|
|
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
|
-
|
|
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(
|
|
977
|
-
const segments =
|
|
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,
|
|
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(
|
|
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,
|
|
1262
|
-
return this.proxy(sessionId,
|
|
1297
|
+
async get(sessionId, path2, params) {
|
|
1298
|
+
return this.proxy(sessionId, path2, "GET", { params });
|
|
1263
1299
|
}
|
|
1264
|
-
async post(sessionId,
|
|
1265
|
-
return this.proxy(sessionId,
|
|
1300
|
+
async post(sessionId, path2, body) {
|
|
1301
|
+
return this.proxy(sessionId, path2, "POST", { body });
|
|
1266
1302
|
}
|
|
1267
|
-
async put(sessionId,
|
|
1268
|
-
return this.proxy(sessionId,
|
|
1303
|
+
async put(sessionId, path2, body) {
|
|
1304
|
+
return this.proxy(sessionId, path2, "PUT", { body });
|
|
1269
1305
|
}
|
|
1270
|
-
async patch(sessionId,
|
|
1271
|
-
return this.proxy(sessionId,
|
|
1306
|
+
async patch(sessionId, path2, body) {
|
|
1307
|
+
return this.proxy(sessionId, path2, "PATCH", { body });
|
|
1272
1308
|
}
|
|
1273
|
-
async delete(sessionId,
|
|
1274
|
-
return this.proxy(sessionId,
|
|
1309
|
+
async delete(sessionId, path2, params) {
|
|
1310
|
+
return this.proxy(sessionId, path2, "DELETE", { params });
|
|
1275
1311
|
}
|
|
1276
|
-
async options(sessionId,
|
|
1277
|
-
return this.proxy(sessionId,
|
|
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/
|
|
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
|
|
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
|