@run-cloud/sdk 0.27.0 → 0.29.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 +19 -5
- package/dist/index.d.ts +179 -8
- package/dist/index.js +263 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -665,9 +665,11 @@ async function parseApiError(response) {
|
|
|
665
665
|
? normalizedScreenshotErrorCode(rawCode)
|
|
666
666
|
: action === "accessibility"
|
|
667
667
|
? normalizedAccessibilityErrorCode(rawCode)
|
|
668
|
-
: action
|
|
669
|
-
?
|
|
670
|
-
:
|
|
668
|
+
: action === "camera-video" || action === "microphone-audio"
|
|
669
|
+
? optionalString(rawCode)
|
|
670
|
+
: action !== "open-url" && (action !== undefined || interactionStatus !== undefined)
|
|
671
|
+
? normalizedInteractionErrorCode(rawCode)
|
|
672
|
+
: optionalString(rawCode);
|
|
671
673
|
return {
|
|
672
674
|
detail,
|
|
673
675
|
code,
|
|
@@ -885,6 +887,25 @@ function validateXcodeBuildOptions(options) {
|
|
|
885
887
|
throw new Error("Xcode build accepts either workspace or project, not both");
|
|
886
888
|
if (options.xcodegen && options.reactNative)
|
|
887
889
|
throw new Error("XcodeGen options cannot be combined with Expo or React Native options");
|
|
890
|
+
if (options.reuse) {
|
|
891
|
+
if (!/^xcb_[A-Za-z0-9]+$/.test(options.reuse.basisBuildId)) {
|
|
892
|
+
throw new Error("reuse.basisBuildId must be a run.cloud Xcode build ID");
|
|
893
|
+
}
|
|
894
|
+
if (!options.reactNative)
|
|
895
|
+
throw new Error("reuse requires reactNative options");
|
|
896
|
+
if (options.workspace || options.project || options.scheme || options.xcodegen) {
|
|
897
|
+
throw new Error("reuse cannot select a workspace, project, scheme, or XcodeGen spec");
|
|
898
|
+
}
|
|
899
|
+
if (options.sdk && options.sdk !== "iphonesimulator") {
|
|
900
|
+
throw new Error("reuse requires sdk iphonesimulator");
|
|
901
|
+
}
|
|
902
|
+
if (options.configuration && options.configuration !== "Release") {
|
|
903
|
+
throw new Error("reuse requires configuration Release");
|
|
904
|
+
}
|
|
905
|
+
if (options.signing || options.cloudSigning || options.appStore || options.appstore) {
|
|
906
|
+
throw new Error("reuse cannot be combined with signing or App Store upload");
|
|
907
|
+
}
|
|
908
|
+
}
|
|
888
909
|
const devServerUrl = options.reactNative?.devServerUrl ?? options.reactNative?.devServerURL;
|
|
889
910
|
if (devServerUrl && options.configuration === "Release")
|
|
890
911
|
throw new Error("reactNative.devServerUrl is only supported for Debug builds");
|
|
@@ -919,6 +940,8 @@ export class Client {
|
|
|
919
940
|
images;
|
|
920
941
|
remotion;
|
|
921
942
|
xcode;
|
|
943
|
+
capabilities;
|
|
944
|
+
mobile;
|
|
922
945
|
apiKey;
|
|
923
946
|
apiUrl;
|
|
924
947
|
fetchImpl;
|
|
@@ -946,6 +969,8 @@ export class Client {
|
|
|
946
969
|
this.images = new ImageClient(this);
|
|
947
970
|
this.remotion = new RemotionClient(this.sandboxes, this.snapshots);
|
|
948
971
|
this.xcode = new XcodeBuildsClient(this);
|
|
972
|
+
this.capabilities = new CapabilitiesClient(this);
|
|
973
|
+
this.mobile = new MobileClient(this);
|
|
949
974
|
}
|
|
950
975
|
async usage(options = {}) {
|
|
951
976
|
const query = options.orgId ? `?orgId=${encodeURIComponent(options.orgId)}` : "";
|
|
@@ -1283,6 +1308,16 @@ export class XcodeBuildsClient {
|
|
|
1283
1308
|
const idempotencyKey = options.idempotencyKey ?? `xcode_${randomUUID()}`;
|
|
1284
1309
|
return await retryXcodeOperation(async () => await this.client.uploadForm("/run-cloud/xcode/builds", form, { "Idempotency-Key": idempotencyKey }));
|
|
1285
1310
|
}
|
|
1311
|
+
/** Reuse a compatible simulator native shell and replace only its embedded JavaScript bundle. */
|
|
1312
|
+
async rebundle(sourcePath, basisBuildId, options = {}) {
|
|
1313
|
+
return await this.build(sourcePath, {
|
|
1314
|
+
...options,
|
|
1315
|
+
sdk: "iphonesimulator",
|
|
1316
|
+
configuration: "Release",
|
|
1317
|
+
reactNative: options.reactNative ?? {},
|
|
1318
|
+
reuse: { basisBuildId },
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1286
1321
|
async get(id) {
|
|
1287
1322
|
return await retryXcodeOperation(async () => await this.client.request("GET", `/run-cloud/xcode/builds/${encodeURIComponent(id)}`));
|
|
1288
1323
|
}
|
|
@@ -1295,8 +1330,21 @@ export class XcodeBuildsClient {
|
|
|
1295
1330
|
const result = await retryXcodeOperation(async () => await this.client.request("GET", `/run-cloud/xcode/builds${query.size ? `?${query}` : ""}`));
|
|
1296
1331
|
return result.items;
|
|
1297
1332
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1333
|
+
/** Read an ordered, replayable event page. Persist nextCursor after processing the page. */
|
|
1334
|
+
async events(options = {}) {
|
|
1335
|
+
const query = new URLSearchParams();
|
|
1336
|
+
if (options.orgId)
|
|
1337
|
+
query.set("orgId", options.orgId);
|
|
1338
|
+
if (options.after !== undefined)
|
|
1339
|
+
query.set("after", String(options.after));
|
|
1340
|
+
if (options.buildId)
|
|
1341
|
+
query.set("buildId", options.buildId);
|
|
1342
|
+
if (options.limit !== undefined)
|
|
1343
|
+
query.set("limit", String(options.limit));
|
|
1344
|
+
return await retryXcodeOperation(async () => await this.client.request("GET", `/run-cloud/xcode/events${query.size ? `?${query}` : ""}`));
|
|
1345
|
+
}
|
|
1346
|
+
async logs(id, offset = 0, source) {
|
|
1347
|
+
return await retryXcodeOperation(async () => await this.client.request("GET", `/run-cloud/xcode/builds/${encodeURIComponent(id)}/logs?offset=${offset}${source ? `&source=${source}` : ""}`));
|
|
1300
1348
|
}
|
|
1301
1349
|
/** Poll state and emit sanitized log chunks until the build is terminal. */
|
|
1302
1350
|
async wait(id, options = {}) {
|
|
@@ -1304,15 +1352,17 @@ export class XcodeBuildsClient {
|
|
|
1304
1352
|
const deadline = Date.now() + timeoutSeconds * 1_000;
|
|
1305
1353
|
const interval = options.pollIntervalMs ?? 1_000;
|
|
1306
1354
|
let offset = 0;
|
|
1355
|
+
let source;
|
|
1307
1356
|
for (;;) {
|
|
1308
1357
|
if (options.signal?.aborted)
|
|
1309
1358
|
throw new DOMException("The operation was aborted", "AbortError");
|
|
1310
1359
|
let build = await this.get(id);
|
|
1311
1360
|
if (build.logs.available || build.status === "running" || build.status === "queued") {
|
|
1312
|
-
const chunk = await this.logs(id, offset);
|
|
1361
|
+
const chunk = await this.logs(id, offset, source);
|
|
1313
1362
|
if (chunk.text)
|
|
1314
1363
|
options.onLog?.(chunk.text);
|
|
1315
1364
|
offset = chunk.nextOffset;
|
|
1365
|
+
source = chunk.source ?? source;
|
|
1316
1366
|
if (["succeeded", "failed", "cancelled"].includes(build.status) && chunk.complete)
|
|
1317
1367
|
return build;
|
|
1318
1368
|
}
|
|
@@ -1362,8 +1412,12 @@ export class SimulatorClient {
|
|
|
1362
1412
|
return await this.client.request("PATCH", `/run-cloud/${this.platform}/${encodeURIComponent(id)}/tags`, { tags });
|
|
1363
1413
|
}
|
|
1364
1414
|
async list(options = {}) {
|
|
1365
|
-
const query =
|
|
1366
|
-
|
|
1415
|
+
const query = new URLSearchParams();
|
|
1416
|
+
if (options.all)
|
|
1417
|
+
query.set("all", "1");
|
|
1418
|
+
if (options.orgId)
|
|
1419
|
+
query.set("orgId", options.orgId);
|
|
1420
|
+
return await this.client.request("GET", `/run-cloud/${this.platform}${query.size ? `?${query}` : ""}`);
|
|
1367
1421
|
}
|
|
1368
1422
|
async get(id) {
|
|
1369
1423
|
return await this.client.request("GET", `/run-cloud/${this.platform}/${encodeURIComponent(id)}`);
|
|
@@ -1405,6 +1459,141 @@ export class SimulatorClient {
|
|
|
1405
1459
|
});
|
|
1406
1460
|
return await this.installAsset(id, asset.id);
|
|
1407
1461
|
}
|
|
1462
|
+
/** Upload a deterministic video and present it to the app as live camera input. */
|
|
1463
|
+
async uploadCameraVideo(id, video, options = {}) {
|
|
1464
|
+
const contentType = video.type.toLowerCase();
|
|
1465
|
+
if (contentType !== "video/mp4" && contentType !== "video/quicktime") {
|
|
1466
|
+
throw new Error("camera video must have content type video/mp4 or video/quicktime");
|
|
1467
|
+
}
|
|
1468
|
+
const bundleId = options.bundleId?.trim();
|
|
1469
|
+
if (bundleId && bundleId.length > 256) {
|
|
1470
|
+
throw new Error("bundleId must be at most 256 characters");
|
|
1471
|
+
}
|
|
1472
|
+
const inferredName = video.name;
|
|
1473
|
+
const filename = options.filename
|
|
1474
|
+
?? (typeof inferredName === "string" && inferredName ? inferredName : undefined)
|
|
1475
|
+
?? (contentType === "video/quicktime" ? "camera.mov" : "camera.mp4");
|
|
1476
|
+
const asset = await this.client.assets.upload(video, {
|
|
1477
|
+
filename,
|
|
1478
|
+
name: options.name,
|
|
1479
|
+
});
|
|
1480
|
+
try {
|
|
1481
|
+
const result = await this.client.request("POST", `/run-cloud/${this.platform}/${encodeURIComponent(id)}/camera-video`, {
|
|
1482
|
+
assetId: asset.id,
|
|
1483
|
+
...(bundleId ? { bundleId } : {}),
|
|
1484
|
+
}, undefined, {
|
|
1485
|
+
errorContext: { sessionId: id, platform: this.platform, action: "camera-video" },
|
|
1486
|
+
transportErrorCode: "camera_video_transport_error",
|
|
1487
|
+
transportErrorMessage: "camera video injection could not reach the run.cloud API",
|
|
1488
|
+
});
|
|
1489
|
+
const response = result && typeof result === "object" && !Array.isArray(result)
|
|
1490
|
+
? result
|
|
1491
|
+
: null;
|
|
1492
|
+
if (!response
|
|
1493
|
+
|| response.ok !== true
|
|
1494
|
+
|| response.platform !== this.platform
|
|
1495
|
+
|| response.sessionId !== id
|
|
1496
|
+
|| (response.bundleId !== null && typeof response.bundleId !== "string")
|
|
1497
|
+
|| response.filename !== filename
|
|
1498
|
+
|| response.contentType !== contentType
|
|
1499
|
+
|| response.byteSize !== video.size
|
|
1500
|
+
|| response.source !== "video"
|
|
1501
|
+
|| (response.pid !== null && typeof response.pid !== "number")
|
|
1502
|
+
|| !isAsset(response.asset)
|
|
1503
|
+
|| response.asset.id !== asset.id) {
|
|
1504
|
+
throw new RunCloudError(0, "camera video injection returned an invalid acknowledgement", {
|
|
1505
|
+
code: "camera_video_invalid_response",
|
|
1506
|
+
retryable: true,
|
|
1507
|
+
sessionId: id,
|
|
1508
|
+
platform: this.platform,
|
|
1509
|
+
action: "camera-video",
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
return result;
|
|
1513
|
+
}
|
|
1514
|
+
catch (error) {
|
|
1515
|
+
await this.client.assets.delete(asset.id).catch(() => undefined);
|
|
1516
|
+
throw error;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
/** Upload audio and deliver its decoded samples to the app microphone. */
|
|
1520
|
+
async uploadMicrophoneAudio(id, audio, options = {}) {
|
|
1521
|
+
const contentType = audio.type.toLowerCase();
|
|
1522
|
+
const supported = new Set([
|
|
1523
|
+
"audio/aac",
|
|
1524
|
+
"audio/m4a",
|
|
1525
|
+
"audio/mp4",
|
|
1526
|
+
"audio/mpeg",
|
|
1527
|
+
"audio/wav",
|
|
1528
|
+
"audio/x-m4a",
|
|
1529
|
+
"audio/x-wav",
|
|
1530
|
+
]);
|
|
1531
|
+
if (!supported.has(contentType)) {
|
|
1532
|
+
throw new Error("audio must be AAC, M4A, MP3, MP4 audio, or WAV");
|
|
1533
|
+
}
|
|
1534
|
+
const bundleId = options.bundleId?.trim();
|
|
1535
|
+
if (bundleId && bundleId.length > 256) {
|
|
1536
|
+
throw new Error("bundleId must be at most 256 characters");
|
|
1537
|
+
}
|
|
1538
|
+
const inferredName = audio.name;
|
|
1539
|
+
const extension = contentType.includes("wav")
|
|
1540
|
+
? "wav"
|
|
1541
|
+
: contentType === "audio/mpeg"
|
|
1542
|
+
? "mp3"
|
|
1543
|
+
: contentType === "audio/aac"
|
|
1544
|
+
? "aac"
|
|
1545
|
+
: "m4a";
|
|
1546
|
+
const filename = options.filename
|
|
1547
|
+
?? (typeof inferredName === "string" && inferredName ? inferredName : undefined)
|
|
1548
|
+
?? `microphone.${extension}`;
|
|
1549
|
+
const asset = await this.client.assets.upload(audio, {
|
|
1550
|
+
filename,
|
|
1551
|
+
name: options.name,
|
|
1552
|
+
});
|
|
1553
|
+
try {
|
|
1554
|
+
const result = await this.client.request("POST", `/run-cloud/${this.platform}/${encodeURIComponent(id)}/microphone-audio`, {
|
|
1555
|
+
assetId: asset.id,
|
|
1556
|
+
...(bundleId ? { bundleId } : {}),
|
|
1557
|
+
}, undefined, {
|
|
1558
|
+
errorContext: { sessionId: id, platform: this.platform, action: "microphone-audio" },
|
|
1559
|
+
transportErrorCode: "microphone_audio_transport_error",
|
|
1560
|
+
transportErrorMessage: "microphone audio injection could not reach the run.cloud API",
|
|
1561
|
+
});
|
|
1562
|
+
const response = result && typeof result === "object" && !Array.isArray(result)
|
|
1563
|
+
? result
|
|
1564
|
+
: null;
|
|
1565
|
+
const optionalFinite = (value) => value === undefined
|
|
1566
|
+
|| (typeof value === "number" && Number.isFinite(value) && value >= 0);
|
|
1567
|
+
if (!response
|
|
1568
|
+
|| response.ok !== true
|
|
1569
|
+
|| response.platform !== this.platform
|
|
1570
|
+
|| response.sessionId !== id
|
|
1571
|
+
|| typeof response.bundleId !== "string"
|
|
1572
|
+
|| response.filename !== filename
|
|
1573
|
+
|| response.contentType !== contentType
|
|
1574
|
+
|| response.byteSize !== audio.size
|
|
1575
|
+
|| (response.pid !== null && typeof response.pid !== "number")
|
|
1576
|
+
|| !optionalFinite(response.sampleRate)
|
|
1577
|
+
|| !optionalFinite(response.channels)
|
|
1578
|
+
|| !optionalFinite(response.sampleCount)
|
|
1579
|
+
|| !optionalFinite(response.durationMs)
|
|
1580
|
+
|| !isAsset(response.asset)
|
|
1581
|
+
|| response.asset.id !== asset.id) {
|
|
1582
|
+
throw new RunCloudError(0, "microphone audio injection returned an invalid acknowledgement", {
|
|
1583
|
+
code: "microphone_audio_invalid_response",
|
|
1584
|
+
retryable: true,
|
|
1585
|
+
sessionId: id,
|
|
1586
|
+
platform: this.platform,
|
|
1587
|
+
action: "microphone-audio",
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
return result;
|
|
1591
|
+
}
|
|
1592
|
+
catch (error) {
|
|
1593
|
+
await this.client.assets.delete(asset.id).catch(() => undefined);
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1408
1597
|
async keepAlive(id) {
|
|
1409
1598
|
const result = await this.client.request("POST", `/run-cloud/${this.platform}/${encodeURIComponent(id)}/activity`, {});
|
|
1410
1599
|
if (result.ok !== true
|
|
@@ -1725,40 +1914,6 @@ export class IosSimulatorClient extends SimulatorClient {
|
|
|
1725
1914
|
});
|
|
1726
1915
|
return await this.client.request("POST", `/run-cloud/ios/${encodeURIComponent(id)}/videos`, { assetId: asset.id });
|
|
1727
1916
|
}
|
|
1728
|
-
async uploadMicrophoneAudio(id, audio, options = {}) {
|
|
1729
|
-
const contentType = audio.type.toLowerCase();
|
|
1730
|
-
const supported = new Set([
|
|
1731
|
-
"audio/aac",
|
|
1732
|
-
"audio/m4a",
|
|
1733
|
-
"audio/mp4",
|
|
1734
|
-
"audio/mpeg",
|
|
1735
|
-
"audio/wav",
|
|
1736
|
-
"audio/x-m4a",
|
|
1737
|
-
"audio/x-wav",
|
|
1738
|
-
]);
|
|
1739
|
-
if (!supported.has(contentType)) {
|
|
1740
|
-
throw new Error("audio must be AAC, M4A, MP3, MP4 audio, or WAV");
|
|
1741
|
-
}
|
|
1742
|
-
const inferredName = audio.name;
|
|
1743
|
-
const extension = contentType.includes("wav")
|
|
1744
|
-
? "wav"
|
|
1745
|
-
: contentType === "audio/mpeg"
|
|
1746
|
-
? "mp3"
|
|
1747
|
-
: contentType === "audio/aac"
|
|
1748
|
-
? "aac"
|
|
1749
|
-
: "m4a";
|
|
1750
|
-
const filename = options.filename
|
|
1751
|
-
?? (typeof inferredName === "string" && inferredName ? inferredName : undefined)
|
|
1752
|
-
?? `microphone.${extension}`;
|
|
1753
|
-
const asset = await this.client.assets.upload(audio, {
|
|
1754
|
-
filename,
|
|
1755
|
-
name: options.name,
|
|
1756
|
-
});
|
|
1757
|
-
return await this.client.request("POST", `/run-cloud/ios/${encodeURIComponent(id)}/microphone-audio`, {
|
|
1758
|
-
assetId: asset.id,
|
|
1759
|
-
...(options.bundleId ? { bundleId: options.bundleId } : {}),
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
1917
|
}
|
|
1763
1918
|
export class SimulatorsClient {
|
|
1764
1919
|
client;
|
|
@@ -1770,11 +1925,15 @@ export class SimulatorsClient {
|
|
|
1770
1925
|
return await new SimulatorClient(this.client, platform).create(rest);
|
|
1771
1926
|
}
|
|
1772
1927
|
async list(options = {}) {
|
|
1773
|
-
const { platform, all } = options;
|
|
1928
|
+
const { platform, all, orgId } = options;
|
|
1774
1929
|
if (platform)
|
|
1775
|
-
return await new SimulatorClient(this.client, platform).list({ all });
|
|
1776
|
-
const query =
|
|
1777
|
-
|
|
1930
|
+
return await new SimulatorClient(this.client, platform).list({ all, orgId });
|
|
1931
|
+
const query = new URLSearchParams();
|
|
1932
|
+
if (all)
|
|
1933
|
+
query.set("all", "1");
|
|
1934
|
+
if (orgId)
|
|
1935
|
+
query.set("orgId", orgId);
|
|
1936
|
+
return await this.client.request("GET", `/run-cloud/simulators${query.size ? `?${query}` : ""}`);
|
|
1778
1937
|
}
|
|
1779
1938
|
async get(id, options = {}) {
|
|
1780
1939
|
return await new SimulatorClient(this.client, options.platform ?? "ios").get(id);
|
|
@@ -1792,6 +1951,14 @@ export class SimulatorsClient {
|
|
|
1792
1951
|
const { platform = "ios", ...installOptions } = options;
|
|
1793
1952
|
return await new SimulatorClient(this.client, platform).install(id, application, installOptions);
|
|
1794
1953
|
}
|
|
1954
|
+
async uploadCameraVideo(id, video, options = {}) {
|
|
1955
|
+
const { platform = "ios", ...mediaOptions } = options;
|
|
1956
|
+
return await new SimulatorClient(this.client, platform).uploadCameraVideo(id, video, mediaOptions);
|
|
1957
|
+
}
|
|
1958
|
+
async uploadMicrophoneAudio(id, audio, options = {}) {
|
|
1959
|
+
const { platform = "ios", ...mediaOptions } = options;
|
|
1960
|
+
return await new SimulatorClient(this.client, platform).uploadMicrophoneAudio(id, audio, mediaOptions);
|
|
1961
|
+
}
|
|
1795
1962
|
async keepAlive(id, options = {}) {
|
|
1796
1963
|
return await new SimulatorClient(this.client, options.platform ?? "ios").keepAlive(id);
|
|
1797
1964
|
}
|
|
@@ -2417,8 +2584,9 @@ export class AssetsClient {
|
|
|
2417
2584
|
constructor(client) {
|
|
2418
2585
|
this.client = client;
|
|
2419
2586
|
}
|
|
2420
|
-
async list() {
|
|
2421
|
-
|
|
2587
|
+
async list(options = {}) {
|
|
2588
|
+
const query = options.orgId ? `?orgId=${encodeURIComponent(options.orgId)}` : "";
|
|
2589
|
+
return await this.client.request("GET", `/run-cloud/assets${query}`);
|
|
2422
2590
|
}
|
|
2423
2591
|
/** Upload through the buffered multipart endpoint instead of signed storage URLs. */
|
|
2424
2592
|
async uploadDirect(file, options = {}) {
|
|
@@ -2440,6 +2608,7 @@ export class AssetsClient {
|
|
|
2440
2608
|
const uploadRunId = options.uploadRunId ?? this.defaultUploadRunId;
|
|
2441
2609
|
const prepared = await this.client.request("POST", "/run-cloud/assets/uploads", {
|
|
2442
2610
|
filename,
|
|
2611
|
+
...(options.orgId ? { orgId: options.orgId } : {}),
|
|
2443
2612
|
...(options.name ? { name: options.name } : {}),
|
|
2444
2613
|
contentType: file.type || "application/octet-stream",
|
|
2445
2614
|
byteSize: file.size,
|
|
@@ -2525,4 +2694,50 @@ export class AssetsClient {
|
|
|
2525
2694
|
return await this.client.request("DELETE", `/run-cloud/assets/${encodeURIComponent(id)}`);
|
|
2526
2695
|
}
|
|
2527
2696
|
}
|
|
2697
|
+
export class CapabilitiesClient {
|
|
2698
|
+
client;
|
|
2699
|
+
constructor(client) {
|
|
2700
|
+
this.client = client;
|
|
2701
|
+
}
|
|
2702
|
+
/** Mint a short-lived bearer token. The token is returned only by this call. */
|
|
2703
|
+
async create(options) {
|
|
2704
|
+
return await this.client.request("POST", "/run-cloud/capabilities", options);
|
|
2705
|
+
}
|
|
2706
|
+
async list(options = {}) {
|
|
2707
|
+
const query = new URLSearchParams();
|
|
2708
|
+
if (options.orgId)
|
|
2709
|
+
query.set("orgId", options.orgId);
|
|
2710
|
+
if (options.active === false)
|
|
2711
|
+
query.set("active", "0");
|
|
2712
|
+
const result = await this.client.request("GET", `/run-cloud/capabilities${query.size ? `?${query}` : ""}`);
|
|
2713
|
+
return result.items;
|
|
2714
|
+
}
|
|
2715
|
+
async revoke(id) {
|
|
2716
|
+
return await this.client.request("DELETE", `/run-cloud/capabilities/${encodeURIComponent(id)}`);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
export class MobileClient {
|
|
2720
|
+
client;
|
|
2721
|
+
constructor(client) {
|
|
2722
|
+
this.client = client;
|
|
2723
|
+
}
|
|
2724
|
+
/** Idempotently create a mobile session and install one existing Asset or Xcode build output. */
|
|
2725
|
+
async launch(source, options) {
|
|
2726
|
+
if (!options.idempotencyKey?.trim()) {
|
|
2727
|
+
throw new Error("mobile.launch requires a stable idempotencyKey");
|
|
2728
|
+
}
|
|
2729
|
+
if (Boolean(source.assetId) === Boolean(source.buildId)) {
|
|
2730
|
+
throw new Error("mobile.launch requires exactly one assetId or buildId");
|
|
2731
|
+
}
|
|
2732
|
+
return await this.client.request("POST", "/run-cloud/mobile/launch", {
|
|
2733
|
+
...(source.assetId ? { assetId: source.assetId } : { buildId: source.buildId }),
|
|
2734
|
+
...(options.orgId ? { orgId: options.orgId } : {}),
|
|
2735
|
+
...(options.platform ? { platform: options.platform } : {}),
|
|
2736
|
+
...(options.session ? { session: options.session } : {}),
|
|
2737
|
+
}, { "Idempotency-Key": options.idempotencyKey });
|
|
2738
|
+
}
|
|
2739
|
+
async getLaunch(id) {
|
|
2740
|
+
return await this.client.request("GET", `/run-cloud/mobile/launches/${encodeURIComponent(id)}`);
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2528
2743
|
//# sourceMappingURL=index.js.map
|