@promigence/sdk 0.1.0-beta.12 → 0.1.0-beta.13
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/caller.js +10 -0
- package/dist/cjs/caller.js +10 -0
- package/dist/cjs/client.d.ts +16 -6
- package/dist/cjs/client.js +1180 -1129
- package/dist/cjs/core.d.ts +1 -1
- package/dist/cjs/core.js +1 -1
- package/dist/cjs/dispatcher.d.ts +2 -0
- package/dist/cjs/dispatcher.js +5 -0
- package/dist/cjs/http2.d.ts +10 -0
- package/dist/cjs/http2.js +143 -51
- package/dist/cjs/keyed.d.ts +6 -0
- package/dist/cjs/keyed.js +59 -0
- package/dist/cjs/proxy.js +11 -0
- package/dist/cjs/sandbox.d.ts +3 -1
- package/dist/cjs/sandbox.js +33 -20
- package/dist/cjs/transport-worker.js +11 -2
- package/dist/client.d.ts +16 -6
- package/dist/client.js +1179 -1127
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/dispatcher.d.ts +2 -0
- package/dist/dispatcher.js +5 -0
- package/dist/http2.d.ts +10 -0
- package/dist/http2.js +142 -51
- package/dist/keyed.d.ts +6 -0
- package/dist/keyed.js +55 -0
- package/dist/proxy.js +11 -0
- package/dist/sandbox.d.ts +3 -1
- package/dist/sandbox.js +33 -20
- package/dist/transport-worker.js +11 -2
- package/package.json +1 -1
package/dist/cjs/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PromigenceClient = exports.LONG_POLL_MS = exports.REPO_CREDENTIALS_FIX = exports.CONTEXT_GIT_RE = exports.IMAGE_DIGEST_RE = exports.MAX_SOURCES = exports.NOTICE_HEADER = exports.isWaiting = exports.FILE_TIMEOUT_MS = void 0;
|
|
3
|
+
exports.PromigenceClient = exports.LONG_POLL_MS = exports.isKeyedCreate = exports.REPO_CREDENTIALS_FIX = exports.CONTEXT_GIT_RE = exports.IMAGE_DIGEST_RE = exports.MAX_SOURCES = exports.NOTICE_HEADER = exports.isWaiting = exports.FILE_TIMEOUT_MS = void 0;
|
|
4
4
|
exports.execOutputBytes = execOutputBytes;
|
|
5
5
|
exports.configDir = configDir;
|
|
6
6
|
exports.credentialsPath = credentialsPath;
|
|
@@ -24,7 +24,6 @@ exports.checkRepoUrl = checkRepoUrl;
|
|
|
24
24
|
exports.checkDockerfileContext = checkDockerfileContext;
|
|
25
25
|
exports.snapshotCreateBody = snapshotCreateBody;
|
|
26
26
|
exports.errorFor = errorFor;
|
|
27
|
-
exports.isKeyedCreate = isKeyedCreate;
|
|
28
27
|
exports.checkSandboxId = checkSandboxId;
|
|
29
28
|
exports.checkSpendCap = checkSpendCap;
|
|
30
29
|
exports.newIdempotencyKey = newIdempotencyKey;
|
|
@@ -44,7 +43,8 @@ const http2_js_1 = require("./http2.js");
|
|
|
44
43
|
const context_js_1 = require("./context.js");
|
|
45
44
|
const proxy_js_1 = require("./proxy.js");
|
|
46
45
|
const trace_js_1 = require("./trace.js");
|
|
47
|
-
const
|
|
46
|
+
const keyed_js_1 = require("./keyed.js");
|
|
47
|
+
Object.defineProperty(exports, "isKeyedCreate", { enumerable: true, get: function () { return keyed_js_1.isKeyedCreate; } });
|
|
48
48
|
const dispatcher_js_1 = require("./dispatcher.js");
|
|
49
49
|
const ca_guard_js_1 = require("./ca-guard.js");
|
|
50
50
|
const hint_js_1 = require("./hint.js");
|
|
@@ -295,15 +295,34 @@ function keyOrigin(env = process.env) {
|
|
|
295
295
|
return stored.store === "macos-keychain" ? "macOS Keychain" : stored.store === "secret-service" ? "Secret Service keyring" : "credentials file";
|
|
296
296
|
return undefined;
|
|
297
297
|
}
|
|
298
|
+
function nonEmpty(v) {
|
|
299
|
+
return v ? v : undefined;
|
|
300
|
+
}
|
|
301
|
+
function withV1(u) {
|
|
302
|
+
const t = u.replace(/\/+$/, "");
|
|
303
|
+
return /^https?:\/\/[^/?#]+$/i.test(t) ? `${t}/v1` : t;
|
|
304
|
+
}
|
|
305
|
+
let credsLastInputs;
|
|
306
|
+
let credsLast;
|
|
298
307
|
function resolveCredentials(opts, env = process.env) {
|
|
299
|
-
const set =
|
|
300
|
-
const trim =
|
|
301
|
-
const t = u.replace(/\/+$/, "");
|
|
302
|
-
return /^https?:\/\/[^/?#]+$/i.test(t) ? `${t}/v1` : t;
|
|
303
|
-
};
|
|
308
|
+
const set = nonEmpty;
|
|
309
|
+
const trim = withV1;
|
|
304
310
|
const optKey = opts.apiKey?.trim();
|
|
305
311
|
const envKey = optKey === undefined ? set(env["PROMIGENCE_API_KEY"]?.trim()) : undefined;
|
|
306
312
|
const envUrl = optKey !== undefined && opts.apiUrl !== undefined ? undefined : set(env["PROMIGENCE_API_URL"]);
|
|
313
|
+
const inputs = optKey === undefined && envKey === undefined
|
|
314
|
+
? undefined
|
|
315
|
+
: `${optKey ?? "\0"}\n${envKey ?? "\0"}\n${envUrl ?? "\0"}\n${opts.apiUrl ?? "\0"}`;
|
|
316
|
+
if (inputs !== undefined && inputs === credsLastInputs && credsLast !== undefined)
|
|
317
|
+
return { apiKey: credsLast.apiKey, apiUrl: credsLast.apiUrl };
|
|
318
|
+
const worked = resolveCredentialsFrom(opts, env, set, trim, optKey, envKey, envUrl);
|
|
319
|
+
if (inputs !== undefined && worked.refusal === undefined) {
|
|
320
|
+
credsLastInputs = inputs;
|
|
321
|
+
credsLast = { apiKey: worked.apiKey, apiUrl: worked.apiUrl };
|
|
322
|
+
}
|
|
323
|
+
return worked;
|
|
324
|
+
}
|
|
325
|
+
function resolveCredentialsFrom(opts, env, set, trim, optKey, envKey, envUrl) {
|
|
307
326
|
const given = optKey !== undefined || envKey !== undefined;
|
|
308
327
|
const { creds, store } = given
|
|
309
328
|
? { creds: {}, store: undefined }
|
|
@@ -704,28 +723,6 @@ function errorFor(code, message, fix, extra = {}) {
|
|
|
704
723
|
}));
|
|
705
724
|
}
|
|
706
725
|
}
|
|
707
|
-
const KEYED_PATTERNS = keyed_gen_js_1.KEYED_CREATES.map(([m, p]) => [m, p.split("/").filter(Boolean)]);
|
|
708
|
-
const KEYED_METHODS = new Set(keyed_gen_js_1.KEYED_CREATES.map(([m]) => m));
|
|
709
|
-
function isKeyedCreate(method, path) {
|
|
710
|
-
if (!KEYED_METHODS.has(method))
|
|
711
|
-
return false;
|
|
712
|
-
const segs = (path.split("?")[0] ?? "").split("/").filter(Boolean);
|
|
713
|
-
for (const [m, pat] of KEYED_PATTERNS) {
|
|
714
|
-
if (m !== method || pat.length !== segs.length)
|
|
715
|
-
continue;
|
|
716
|
-
let all = true;
|
|
717
|
-
for (let i = 0; i < pat.length; i++) {
|
|
718
|
-
const s = pat[i];
|
|
719
|
-
if (s.startsWith("{") ? (segs[i] ?? "").length === 0 : s !== segs[i]) {
|
|
720
|
-
all = false;
|
|
721
|
-
break;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
if (all)
|
|
725
|
-
return true;
|
|
726
|
-
}
|
|
727
|
-
return false;
|
|
728
|
-
}
|
|
729
726
|
function commandTimeoutMs(v) {
|
|
730
727
|
if (v === undefined)
|
|
731
728
|
return undefined;
|
|
@@ -806,6 +803,12 @@ function serverAppMs(header) {
|
|
|
806
803
|
function headerOf(res, name) {
|
|
807
804
|
return res instanceof http2_js_1.H2Response ? res.header(name) : res.headers.get(name);
|
|
808
805
|
}
|
|
806
|
+
function ownFetchDispatcherFor(timeoutMs) {
|
|
807
|
+
const guarded = (0, dispatcher_js_1.customDispatcher)() ? undefined : (0, ca_guard_js_1.guardedFetchDispatcher)();
|
|
808
|
+
if (guarded !== undefined)
|
|
809
|
+
return (0, dispatcher_js_1.longRequestDispatcher)(timeoutMs, guarded) ?? guarded;
|
|
810
|
+
return (0, dispatcher_js_1.longRequestDispatcher)(timeoutMs);
|
|
811
|
+
}
|
|
809
812
|
class PromigenceClient {
|
|
810
813
|
apiUrl;
|
|
811
814
|
#apiKey;
|
|
@@ -837,7 +840,7 @@ class PromigenceClient {
|
|
|
837
840
|
this.fetchImpl = opts.fetch ?? (proxy ? (0, proxy_js_1.proxyFetch)(proxy) : globalThis.fetch);
|
|
838
841
|
this.ownFetch = opts.fetch === undefined && proxy === undefined;
|
|
839
842
|
this.h2 = env !== undefined && this.apiUrl.startsWith("https:") && (0, http2_js_1.http2Allowed)(opts.http2, env)
|
|
840
|
-
?
|
|
843
|
+
? (0, http2_js_1.sharedTransport)(this.fetchImpl)
|
|
841
844
|
: undefined;
|
|
842
845
|
this.requestTimeoutMs = opts.requestTimeoutMs ?? 30_000;
|
|
843
846
|
this.maxRetries = opts.maxRetries ?? 3;
|
|
@@ -936,11 +939,18 @@ class PromigenceClient {
|
|
|
936
939
|
return (0, ca_guard_js_1.guardedFetchDispatcher)();
|
|
937
940
|
}
|
|
938
941
|
dispatcherFor(timeoutMs) {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
return (0, dispatcher_js_1.longRequestDispatcher)(timeoutMs, guarded) ?? guarded;
|
|
942
|
+
if (this.ownFetch)
|
|
943
|
+
return ownFetchDispatcherFor(timeoutMs);
|
|
942
944
|
return (0, dispatcher_js_1.longRequestDispatcher)(timeoutMs);
|
|
943
945
|
}
|
|
946
|
+
async againKeyed(attempt, signal) {
|
|
947
|
+
if (attempt >= this.maxRetries || signal?.aborted)
|
|
948
|
+
return false;
|
|
949
|
+
this.stats.retries++;
|
|
950
|
+
const backoff = Math.min(MAX_RETRY_SLEEP_MS, this.retryBaseMs * 2 ** attempt);
|
|
951
|
+
await sleep(backoff * (0.5 + Math.random() * 0.5));
|
|
952
|
+
return true;
|
|
953
|
+
}
|
|
944
954
|
async request(opts) {
|
|
945
955
|
const tr = (0, trace_js_1.traceBegin)(opts.method, opts.path);
|
|
946
956
|
const url = `${this.apiUrl}${opts.path}`;
|
|
@@ -962,21 +972,13 @@ class PromigenceClient {
|
|
|
962
972
|
headers["authorization"] = this.bearer();
|
|
963
973
|
if (opts.body !== undefined)
|
|
964
974
|
headers["content-type"] = "application/json";
|
|
965
|
-
const keyedCreate = isKeyedCreate(opts.method, opts.path);
|
|
975
|
+
const keyedCreate = (0, keyed_js_1.isKeyedCreate)(opts.method, opts.path);
|
|
966
976
|
const idempotencyKey = opts.idempotencyKey ?? (keyedCreate ? newIdempotencyKey() : undefined);
|
|
967
977
|
if (idempotencyKey)
|
|
968
978
|
headers["idempotency-key"] = idempotencyKey;
|
|
969
979
|
const replayable = opts.method === "GET" || opts.method === "DELETE" || (keyedCreate && Boolean(idempotencyKey));
|
|
970
|
-
const again = async (attempt) => {
|
|
971
|
-
if (!keyedCreate || !idempotencyKey || attempt >= this.maxRetries || opts.signal?.aborted)
|
|
972
|
-
return false;
|
|
973
|
-
this.stats.retries++;
|
|
974
|
-
const backoff = Math.min(MAX_RETRY_SLEEP_MS, this.retryBaseMs * 2 ** attempt);
|
|
975
|
-
await sleep(backoff * (0.5 + Math.random() * 0.5));
|
|
976
|
-
return true;
|
|
977
|
-
};
|
|
978
980
|
const timeoutMs = opts.timeoutMs ?? this.requestTimeoutMs;
|
|
979
|
-
|
|
981
|
+
let dispatcher;
|
|
980
982
|
let resentDelete = false;
|
|
981
983
|
for (let attempt = 0;; attempt++) {
|
|
982
984
|
let res;
|
|
@@ -989,8 +991,7 @@ class PromigenceClient {
|
|
|
989
991
|
init.signal = opts.signal;
|
|
990
992
|
if (opts.body !== undefined)
|
|
991
993
|
init.body = JSON.stringify(opts.body);
|
|
992
|
-
|
|
993
|
-
init.dispatcher = dispatcher;
|
|
994
|
+
init.dispatcherFor = ownFetchDispatcherFor;
|
|
994
995
|
if (tr !== undefined) {
|
|
995
996
|
init.trace = tr;
|
|
996
997
|
(0, trace_js_1.mark)(tr, "init");
|
|
@@ -1006,7 +1007,7 @@ class PromigenceClient {
|
|
|
1006
1007
|
headers,
|
|
1007
1008
|
signal: signals.length === 1 ? signals[0] : AbortSignal.any(signals),
|
|
1008
1009
|
...(opts.body === undefined ? {} : { body: JSON.stringify(opts.body) }),
|
|
1009
|
-
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
1010
|
+
...((dispatcher ??= this.dispatcherFor(timeoutMs)) === undefined ? {} : { dispatcher }),
|
|
1010
1011
|
};
|
|
1011
1012
|
if (tr !== undefined)
|
|
1012
1013
|
(0, trace_js_1.mark)(tr, "init");
|
|
@@ -1024,7 +1025,7 @@ class PromigenceClient {
|
|
|
1024
1025
|
}
|
|
1025
1026
|
catch (err) {
|
|
1026
1027
|
const e = err;
|
|
1027
|
-
if (await
|
|
1028
|
+
if (keyedCreate && idempotencyKey && await this.againKeyed(attempt, opts.signal))
|
|
1028
1029
|
continue;
|
|
1029
1030
|
const cutOff = e?.cause?.code === "UND_ERR_HEADERS_TIMEOUT" || e?.cause?.code === "UND_ERR_BODY_TIMEOUT";
|
|
1030
1031
|
const timedOut = e?.name === "TimeoutError" || e?.name === "AbortError" || cutOff;
|
|
@@ -1335,316 +1336,332 @@ class PromigenceClient {
|
|
|
1335
1336
|
...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
1336
1337
|
});
|
|
1337
1338
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1339
|
+
group_templates() {
|
|
1340
|
+
return {
|
|
1341
|
+
list: async (opts = {}) => {
|
|
1342
|
+
const r = await this.request({
|
|
1343
|
+
method: "GET",
|
|
1344
|
+
path: "/templates",
|
|
1345
|
+
anonymous: true,
|
|
1346
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
1347
|
+
});
|
|
1348
|
+
return r?.templates ?? [];
|
|
1349
|
+
},
|
|
1350
|
+
get: (name, opts = {}) => this.request({
|
|
1341
1351
|
method: "GET",
|
|
1342
|
-
path:
|
|
1352
|
+
path: `/templates/${seg(name)}`,
|
|
1343
1353
|
anonymous: true,
|
|
1344
1354
|
...(opts.signal ? { signal: opts.signal } : {}),
|
|
1345
|
-
})
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
get: (name, opts = {}) => this.request({
|
|
1349
|
-
method: "GET",
|
|
1350
|
-
path: `/templates/${seg(name)}`,
|
|
1351
|
-
anonymous: true,
|
|
1352
|
-
...(opts.signal ? { signal: opts.signal } : {}),
|
|
1353
|
-
}),
|
|
1354
|
-
};
|
|
1355
|
+
}),
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1355
1358
|
me(opts = {}) {
|
|
1356
1359
|
return this.request({ method: "GET", path: "/me", ...(opts.signal ? { signal: opts.signal } : {}) });
|
|
1357
1360
|
}
|
|
1358
1361
|
outcomes(opts = {}) {
|
|
1359
1362
|
return this.request({ method: "GET", path: "/me/outcomes?period=month", ...(opts.signal ? { signal: opts.signal } : {}) });
|
|
1360
1363
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
invites: async (org) => {
|
|
1418
|
-
const r = await this.request({ method: "GET", path: `/orgs/${orgSeg(org)}/invites` });
|
|
1419
|
-
return r?.invites ?? [];
|
|
1420
|
-
},
|
|
1421
|
-
leave: (org) => this.request({ method: "DELETE", path: `/orgs/${orgSeg(org)}/members/me` }),
|
|
1422
|
-
projects: {
|
|
1423
|
-
list: async (org) => {
|
|
1424
|
-
const r = await this.request({ method: "GET", path: `/orgs/${orgSeg(org)}/projects` });
|
|
1425
|
-
return r?.projects ?? [];
|
|
1364
|
+
group_billing() {
|
|
1365
|
+
return {
|
|
1366
|
+
usage: () => this.request({ method: "GET", path: "/billing" }),
|
|
1367
|
+
ledger: async (limit = 100) => (await this.billing.ledgerPage({ limit })).items,
|
|
1368
|
+
ledgerPage: async (opts = {}) => {
|
|
1369
|
+
const limit = opts.limit ?? 100;
|
|
1370
|
+
const r = await this.request({ method: "GET", path: `/me/ledger?${pageQuery(limit, 1000, opts.cursor)}` });
|
|
1371
|
+
return pageOf(r, "entries", limit);
|
|
1372
|
+
},
|
|
1373
|
+
historyPage: async (opts = {}) => {
|
|
1374
|
+
const limit = opts.limit ?? 50;
|
|
1375
|
+
const q = new URLSearchParams(pageQuery(limit, 100, opts.cursor));
|
|
1376
|
+
if (opts.kind !== undefined && opts.kind !== "all")
|
|
1377
|
+
q.set("kind", opts.kind);
|
|
1378
|
+
const r = await this.request({ method: "GET", path: `/billing/history?${q.toString()}` });
|
|
1379
|
+
return pageOf(r, "entries", limit);
|
|
1380
|
+
},
|
|
1381
|
+
checkout: () => this.request({ method: "POST", path: "/billing/checkout", body: {} }),
|
|
1382
|
+
portal: () => this.request({ method: "POST", path: "/billing/portal", body: {} }),
|
|
1383
|
+
referrals: () => this.request({ method: "GET", path: "/referrals" }),
|
|
1384
|
+
applyReferral: (code) => this.request({ method: "POST", path: "/referrals/apply", body: { code } }),
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
group_keys() {
|
|
1388
|
+
return {
|
|
1389
|
+
list: async (opts = {}) => {
|
|
1390
|
+
const r = await this.request({ method: "GET", path: `/keys${opts.all ? "?all=1" : ""}` });
|
|
1391
|
+
return r?.keys ?? [];
|
|
1392
|
+
},
|
|
1393
|
+
create: (opts = {}) => {
|
|
1394
|
+
const body = { ...expiryFields(opts) };
|
|
1395
|
+
if (opts.name !== undefined)
|
|
1396
|
+
body["name"] = opts.name.slice(0, 64);
|
|
1397
|
+
return this.request({ method: "POST", path: "/keys", body });
|
|
1398
|
+
},
|
|
1399
|
+
revoke: (keyId) => {
|
|
1400
|
+
if (!keyId?.trim())
|
|
1401
|
+
throw new core_js_1.InvalidArgumentError("keys.revoke needs a key id", "Pass a key_id from keys.list().");
|
|
1402
|
+
return this.request({ method: "DELETE", path: `/keys/${encodeURIComponent(keyId)}` });
|
|
1403
|
+
},
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
group_orgs() {
|
|
1407
|
+
return {
|
|
1408
|
+
list: async () => {
|
|
1409
|
+
const r = await this.request({ method: "GET", path: "/orgs" });
|
|
1410
|
+
return r?.orgs ?? [];
|
|
1411
|
+
},
|
|
1412
|
+
setDefault: (org) => this.request({ method: "POST", path: `/orgs/${orgSeg(org)}/default`, body: {} }),
|
|
1413
|
+
createKey: (org, opts = {}) => {
|
|
1414
|
+
const body = { ...expiryFields(opts) };
|
|
1415
|
+
if (opts.name !== undefined)
|
|
1416
|
+
body["name"] = opts.name.slice(0, 64);
|
|
1417
|
+
if (opts.project !== undefined)
|
|
1418
|
+
body["project"] = opts.project;
|
|
1419
|
+
return this.request({ method: "POST", path: `/orgs/${orgSeg(org)}/keys`, body });
|
|
1426
1420
|
},
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1421
|
+
members: async (org) => {
|
|
1422
|
+
const r = await this.request({ method: "GET", path: `/orgs/${orgSeg(org)}/members` });
|
|
1423
|
+
return r?.members ?? [];
|
|
1424
|
+
},
|
|
1425
|
+
invites: async (org) => {
|
|
1426
|
+
const r = await this.request({ method: "GET", path: `/orgs/${orgSeg(org)}/invites` });
|
|
1427
|
+
return r?.invites ?? [];
|
|
1428
|
+
},
|
|
1429
|
+
leave: (org) => this.request({ method: "DELETE", path: `/orgs/${orgSeg(org)}/members/me` }),
|
|
1430
|
+
projects: {
|
|
1431
|
+
list: async (org) => {
|
|
1432
|
+
const r = await this.request({ method: "GET", path: `/orgs/${orgSeg(org)}/projects` });
|
|
1433
|
+
return r?.projects ?? [];
|
|
1434
|
+
},
|
|
1435
|
+
create: (org, name) => this.request({ method: "POST", path: `/orgs/${orgSeg(org)}/projects`, body: { name } }),
|
|
1436
|
+
rename: (org, project, name) => this.request({
|
|
1437
|
+
method: "PATCH", path: `/orgs/${orgSeg(org)}/projects/${encodeURIComponent(project)}`, body: { name },
|
|
1438
|
+
}),
|
|
1439
|
+
},
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
group_credits() {
|
|
1443
|
+
return {
|
|
1444
|
+
offer: () => this.request({ method: "GET", path: "/credits/offer" }),
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
group_github() {
|
|
1448
|
+
return {
|
|
1449
|
+
status: () => this.request({ method: "GET", path: "/integrations/github" }),
|
|
1450
|
+
connect: () => this.request({ method: "POST", path: "/integrations/github/connect", body: {} }),
|
|
1451
|
+
ticket: () => this.request({ method: "POST", path: "/integrations/github/requests", body: {} }),
|
|
1452
|
+
request: (requestId) => this.request({
|
|
1453
|
+
method: "GET",
|
|
1454
|
+
path: `/integrations/github/requests/${encodeURIComponent(requestId)}`,
|
|
1430
1455
|
}),
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
if (waitMs > 0)
|
|
1542
|
-
req.timeoutMs = waitMs + this.requestTimeoutMs;
|
|
1543
|
-
if (opts.idempotencyKey)
|
|
1544
|
-
req.idempotencyKey = opts.idempotencyKey;
|
|
1545
|
-
if (opts.signal)
|
|
1546
|
-
req.signal = opts.signal;
|
|
1547
|
-
return this.request(req);
|
|
1548
|
-
},
|
|
1549
|
-
get: (runId, opts = {}) => this.request({
|
|
1550
|
-
method: "GET",
|
|
1551
|
-
path: `/runs/${encodeURIComponent(runId)}${waitQuery(opts.waitMs)}`,
|
|
1552
|
-
...this.readExtras(opts),
|
|
1553
|
-
}),
|
|
1554
|
-
list: async (limit = 20) => (await this.runs.listPage({ limit })).items,
|
|
1555
|
-
listPage: async (opts = {}) => {
|
|
1556
|
-
const limit = opts.limit ?? 20;
|
|
1557
|
-
const r = await this.request({ method: "GET", path: `/runs?${pageQuery(limit, 500, opts.cursor)}` });
|
|
1558
|
-
return pageOf(r, "runs", limit);
|
|
1559
|
-
},
|
|
1560
|
-
status: (runId, opts = {}) => this.runs.get(runId, opts),
|
|
1561
|
-
sandboxes: async (runId, opts = {}) => {
|
|
1562
|
-
const r = await this.runs.sandboxesWithCapacity(runId, opts);
|
|
1563
|
-
return r.sandboxes;
|
|
1564
|
-
},
|
|
1565
|
-
sandboxesWithCapacity: async (runId, opts = {}) => {
|
|
1566
|
-
const r = await this.request({
|
|
1456
|
+
disconnect: (installationId) => this.request({
|
|
1457
|
+
method: "DELETE",
|
|
1458
|
+
path: `/integrations/github/installations/${encodeURIComponent(String(installationId))}`,
|
|
1459
|
+
}),
|
|
1460
|
+
repos: (opts = {}) => {
|
|
1461
|
+
const q = new URLSearchParams();
|
|
1462
|
+
if (opts.page !== undefined)
|
|
1463
|
+
q.set("page", String(Math.max(1, Math.floor(opts.page))));
|
|
1464
|
+
if (opts.perPage !== undefined)
|
|
1465
|
+
q.set("per_page", String(Math.min(100, Math.max(1, Math.floor(opts.perPage)))));
|
|
1466
|
+
const qs = q.toString();
|
|
1467
|
+
return this.request({ method: "GET", path: `/integrations/github/repos${qs ? `?${qs}` : ""}` });
|
|
1468
|
+
},
|
|
1469
|
+
untrack: (trackId) => this.request({ method: "DELETE", path: `/integrations/github/tracks/${encodeURIComponent(trackId)}` }),
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
group_gitlab() {
|
|
1473
|
+
return {
|
|
1474
|
+
status: () => this.request({ method: "GET", path: "/integrations/gitlab" }),
|
|
1475
|
+
connect: () => this.request({ method: "POST", path: "/integrations/gitlab/connect", body: {} }),
|
|
1476
|
+
ticket: () => this.request({ method: "POST", path: "/integrations/gitlab/requests", body: {} }),
|
|
1477
|
+
request: (requestId) => this.request({
|
|
1478
|
+
method: "GET",
|
|
1479
|
+
path: `/integrations/gitlab/requests/${encodeURIComponent(requestId)}`,
|
|
1480
|
+
}),
|
|
1481
|
+
disconnect: (connectionId) => this.request({
|
|
1482
|
+
method: "DELETE",
|
|
1483
|
+
path: `/integrations/gitlab/connections/${encodeURIComponent(connectionId)}`,
|
|
1484
|
+
}),
|
|
1485
|
+
repos: (opts = {}) => {
|
|
1486
|
+
const q = new URLSearchParams();
|
|
1487
|
+
if (opts.page !== undefined)
|
|
1488
|
+
q.set("page", String(Math.max(1, Math.floor(opts.page))));
|
|
1489
|
+
if (opts.perPage !== undefined)
|
|
1490
|
+
q.set("per_page", String(Math.min(100, Math.max(1, Math.floor(opts.perPage)))));
|
|
1491
|
+
if (opts.connectionId !== undefined)
|
|
1492
|
+
q.set("connection_id", opts.connectionId);
|
|
1493
|
+
const qs = q.toString();
|
|
1494
|
+
return this.request({ method: "GET", path: `/integrations/gitlab/repos${qs ? `?${qs}` : ""}` });
|
|
1495
|
+
},
|
|
1496
|
+
untrack: (trackId) => this.request({ method: "DELETE", path: `/integrations/gitlab/tracks/${encodeURIComponent(trackId)}` }),
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
group_runs() {
|
|
1500
|
+
return {
|
|
1501
|
+
start: (opts) => {
|
|
1502
|
+
if (!opts.snapshot)
|
|
1503
|
+
throw new core_js_1.InvalidArgumentError("runs.start needs a snapshot", "Pass { snapshot: 'snap_… | alias | alias@commit7' }.");
|
|
1504
|
+
if (!Number.isInteger(opts.count) || opts.count < 1)
|
|
1505
|
+
throw new core_js_1.InvalidArgumentError("runs.start needs count >= 1", "Pass { count: 1 } or more.");
|
|
1506
|
+
const body = { snapshot: opts.snapshot, count: opts.count };
|
|
1507
|
+
if (opts.timeoutMs !== undefined)
|
|
1508
|
+
body["timeout_ms"] = opts.timeoutMs;
|
|
1509
|
+
if (opts.spendCap !== undefined)
|
|
1510
|
+
body["spend_cap"] = checkSpendCap(opts.spendCap);
|
|
1511
|
+
if (opts.allowPartial !== undefined)
|
|
1512
|
+
body["allow_partial"] = opts.allowPartial;
|
|
1513
|
+
if (opts.verifyEach !== undefined)
|
|
1514
|
+
body["verify_each"] = opts.verifyEach;
|
|
1515
|
+
if (opts.network !== undefined)
|
|
1516
|
+
body["network"] = opts.network;
|
|
1517
|
+
if (opts.optimizeFor !== undefined)
|
|
1518
|
+
body["optimize_for"] = opts.optimizeFor;
|
|
1519
|
+
if (opts.secrets?.length)
|
|
1520
|
+
body["secrets"] = opts.secrets;
|
|
1521
|
+
if (opts.labels && Object.keys(opts.labels).length)
|
|
1522
|
+
body["labels"] = opts.labels;
|
|
1523
|
+
if (opts.exec) {
|
|
1524
|
+
const e = {};
|
|
1525
|
+
if (opts.exec.argv)
|
|
1526
|
+
e["argv"] = opts.exec.argv;
|
|
1527
|
+
if (opts.exec.sh)
|
|
1528
|
+
e["sh"] = opts.exec.sh;
|
|
1529
|
+
if (opts.exec.cwd)
|
|
1530
|
+
e["cwd"] = opts.exec.cwd;
|
|
1531
|
+
if (opts.exec.env)
|
|
1532
|
+
e["env"] = opts.exec.env;
|
|
1533
|
+
const t = commandTimeoutMs(opts.exec.timeoutMs);
|
|
1534
|
+
if (t !== undefined)
|
|
1535
|
+
e["timeout_ms"] = t;
|
|
1536
|
+
if (opts.exec.maxOutputBytes !== undefined)
|
|
1537
|
+
e["max_output_bytes"] = opts.exec.maxOutputBytes;
|
|
1538
|
+
e["kill_on_exit"] = opts.exec.killOnExit ?? true;
|
|
1539
|
+
body["exec"] = e;
|
|
1540
|
+
}
|
|
1541
|
+
if (opts.grade) {
|
|
1542
|
+
if (!opts.grade.cmd)
|
|
1543
|
+
throw new core_js_1.InvalidArgumentError("runs.start grade needs a cmd", "Pass { grade: { cmd: 'pytest -q tests/' } }.");
|
|
1544
|
+
const g = { cmd: opts.grade.cmd };
|
|
1545
|
+
if (opts.grade.holdoutCmd)
|
|
1546
|
+
g["holdout_cmd"] = opts.grade.holdoutCmd;
|
|
1547
|
+
if (opts.grade.timeoutMs !== undefined)
|
|
1548
|
+
g["timeout_ms"] = opts.grade.timeoutMs;
|
|
1549
|
+
if (opts.grade.score !== undefined)
|
|
1550
|
+
g["score"] = opts.grade.score;
|
|
1551
|
+
body["grade"] = g;
|
|
1552
|
+
}
|
|
1553
|
+
if (opts.gradeOutput !== undefined)
|
|
1554
|
+
body["grade_output"] = opts.gradeOutput;
|
|
1555
|
+
const waitMs = opts.waitMs !== undefined && opts.waitMs > 0 ? Math.min(opts.waitMs, WAIT_MS_CAP) : 0;
|
|
1556
|
+
const req = { method: "POST", path: `/runs${waitQuery(waitMs)}`, body };
|
|
1557
|
+
if (waitMs > 0)
|
|
1558
|
+
req.timeoutMs = waitMs + this.requestTimeoutMs;
|
|
1559
|
+
if (opts.idempotencyKey)
|
|
1560
|
+
req.idempotencyKey = opts.idempotencyKey;
|
|
1561
|
+
if (opts.signal)
|
|
1562
|
+
req.signal = opts.signal;
|
|
1563
|
+
return this.request(req);
|
|
1564
|
+
},
|
|
1565
|
+
get: (runId, opts = {}) => this.request({
|
|
1567
1566
|
method: "GET",
|
|
1568
|
-
path: `/runs/${encodeURIComponent(runId)}
|
|
1567
|
+
path: `/runs/${encodeURIComponent(runId)}${waitQuery(opts.waitMs)}`,
|
|
1569
1568
|
...this.readExtras(opts),
|
|
1570
|
-
})
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
report: (runId) => this.request({ method: "GET", path: `/runs/${encodeURIComponent(runId)}/report` }),
|
|
1577
|
-
cancel: (runId) => this.request({ method: "POST", path: `/runs/${encodeURIComponent(runId)}/cancel`, body: {} }),
|
|
1578
|
-
kill: (runId) => this.request({ method: "DELETE", path: `/runs/${encodeURIComponent(runId)}` }),
|
|
1579
|
-
quote: (opts) => {
|
|
1580
|
-
if (!opts.snapshot)
|
|
1581
|
-
throw new core_js_1.InvalidArgumentError("runs.quote needs a snapshot", "Pass { snapshot: 'snap_… | alias | alias@commit7' }.");
|
|
1582
|
-
if (!Number.isInteger(opts.count) || opts.count < 1)
|
|
1583
|
-
throw new core_js_1.InvalidArgumentError("runs.quote needs count >= 1", "Pass { count: 1 } or more.");
|
|
1584
|
-
const body = { snapshot: opts.snapshot, count: opts.count };
|
|
1585
|
-
if (opts.timeoutMs !== undefined)
|
|
1586
|
-
body["timeout_ms"] = opts.timeoutMs;
|
|
1587
|
-
if (opts.tier !== undefined)
|
|
1588
|
-
body["tier"] = opts.tier;
|
|
1589
|
-
if (opts.spendCap !== undefined)
|
|
1590
|
-
body["spend_cap"] = checkSpendCap(opts.spendCap);
|
|
1591
|
-
return this.request({ method: "POST", path: "/runs/quote", body });
|
|
1592
|
-
},
|
|
1593
|
-
replay: (runId, sandboxIds, opts = {}) => this.request({
|
|
1594
|
-
method: "POST",
|
|
1595
|
-
path: `/runs/${encodeURIComponent(runId)}/replay`,
|
|
1596
|
-
body: {
|
|
1597
|
-
...(sandboxIds?.length ? { sandbox_ids: sandboxIds } : {}),
|
|
1598
|
-
...(opts.env && Object.keys(opts.env).length ? { env: opts.env } : {}),
|
|
1569
|
+
}),
|
|
1570
|
+
list: async (limit = 20) => (await this.runs.listPage({ limit })).items,
|
|
1571
|
+
listPage: async (opts = {}) => {
|
|
1572
|
+
const limit = opts.limit ?? 20;
|
|
1573
|
+
const r = await this.request({ method: "GET", path: `/runs?${pageQuery(limit, 500, opts.cursor)}` });
|
|
1574
|
+
return pageOf(r, "runs", limit);
|
|
1599
1575
|
},
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1576
|
+
status: (runId, opts = {}) => this.runs.get(runId, opts),
|
|
1577
|
+
sandboxes: async (runId, opts = {}) => {
|
|
1578
|
+
const r = await this.runs.sandboxesWithCapacity(runId, opts);
|
|
1579
|
+
return r.sandboxes;
|
|
1580
|
+
},
|
|
1581
|
+
sandboxesWithCapacity: async (runId, opts = {}) => {
|
|
1582
|
+
const r = await this.request({
|
|
1583
|
+
method: "GET",
|
|
1584
|
+
path: `/runs/${encodeURIComponent(runId)}/sandboxes${waitQuery(opts.waitMs)}`,
|
|
1585
|
+
...this.readExtras(opts),
|
|
1586
|
+
});
|
|
1587
|
+
if (Array.isArray(r))
|
|
1588
|
+
return { sandboxes: r };
|
|
1589
|
+
const sandboxes = r?.sandboxes ?? [];
|
|
1590
|
+
return r?.capacity ? { sandboxes, capacity: r.capacity } : { sandboxes };
|
|
1591
|
+
},
|
|
1592
|
+
report: (runId) => this.request({ method: "GET", path: `/runs/${encodeURIComponent(runId)}/report` }),
|
|
1593
|
+
cancel: (runId) => this.request({ method: "POST", path: `/runs/${encodeURIComponent(runId)}/cancel`, body: {} }),
|
|
1594
|
+
kill: (runId) => this.request({ method: "DELETE", path: `/runs/${encodeURIComponent(runId)}` }),
|
|
1595
|
+
quote: (opts) => {
|
|
1596
|
+
if (!opts.snapshot)
|
|
1597
|
+
throw new core_js_1.InvalidArgumentError("runs.quote needs a snapshot", "Pass { snapshot: 'snap_… | alias | alias@commit7' }.");
|
|
1598
|
+
if (!Number.isInteger(opts.count) || opts.count < 1)
|
|
1599
|
+
throw new core_js_1.InvalidArgumentError("runs.quote needs count >= 1", "Pass { count: 1 } or more.");
|
|
1600
|
+
const body = { snapshot: opts.snapshot, count: opts.count };
|
|
1601
|
+
if (opts.timeoutMs !== undefined)
|
|
1602
|
+
body["timeout_ms"] = opts.timeoutMs;
|
|
1603
|
+
if (opts.tier !== undefined)
|
|
1604
|
+
body["tier"] = opts.tier;
|
|
1605
|
+
if (opts.spendCap !== undefined)
|
|
1606
|
+
body["spend_cap"] = checkSpendCap(opts.spendCap);
|
|
1607
|
+
return this.request({ method: "POST", path: "/runs/quote", body });
|
|
1608
|
+
},
|
|
1609
|
+
replay: (runId, sandboxIds, opts = {}) => this.request({
|
|
1614
1610
|
method: "POST",
|
|
1615
|
-
path: `/runs/${encodeURIComponent(runId)}/
|
|
1616
|
-
body
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1611
|
+
path: `/runs/${encodeURIComponent(runId)}/replay`,
|
|
1612
|
+
body: {
|
|
1613
|
+
...(sandboxIds?.length ? { sandbox_ids: sandboxIds } : {}),
|
|
1614
|
+
...(opts.env && Object.keys(opts.env).length ? { env: opts.env } : {}),
|
|
1615
|
+
},
|
|
1616
|
+
}),
|
|
1617
|
+
captures: (runId) => this.request({
|
|
1618
|
+
method: "GET",
|
|
1619
|
+
path: `/runs/${encodeURIComponent(runId)}/captures`,
|
|
1620
|
+
}),
|
|
1621
|
+
open: (runId, opts = {}) => {
|
|
1622
|
+
const body = {};
|
|
1623
|
+
if (opts.sandboxId !== undefined)
|
|
1624
|
+
body["sandbox_id"] = opts.sandboxId;
|
|
1625
|
+
if (opts.spendCap !== undefined)
|
|
1626
|
+
body["spend_cap"] = opts.spendCap;
|
|
1627
|
+
if (opts.timeoutMs !== undefined)
|
|
1628
|
+
body["timeout_ms"] = opts.timeoutMs;
|
|
1629
|
+
return this.request({
|
|
1630
|
+
method: "POST",
|
|
1631
|
+
path: `/runs/${encodeURIComponent(runId)}/open`,
|
|
1632
|
+
body,
|
|
1633
|
+
});
|
|
1634
|
+
},
|
|
1635
|
+
waitReady: async (runId, opts = {}) => {
|
|
1636
|
+
const seen = new Set();
|
|
1637
|
+
const progress = opts.onReady !== undefined || opts.onCapacity !== undefined;
|
|
1638
|
+
const r = await this.waitLoop(opts, progress ? PROGRESS_POLL_MS : exports.LONG_POLL_MS, [], async (read) => {
|
|
1639
|
+
const { sandboxes, capacity } = await this.runs.sandboxesWithCapacity(runId, read);
|
|
1640
|
+
if (capacity)
|
|
1641
|
+
opts.onCapacity?.(capacity);
|
|
1642
|
+
return sandboxes;
|
|
1643
|
+
}, (sandboxes) => {
|
|
1644
|
+
for (const s of sandboxes) {
|
|
1645
|
+
if (!(0, exports.isWaiting)(s.state) && !seen.has(s.sandbox_id)) {
|
|
1646
|
+
seen.add(s.sandbox_id);
|
|
1647
|
+
opts.onReady?.(s);
|
|
1648
|
+
}
|
|
1632
1649
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1650
|
+
return sandboxes.length > 0 && sandboxes.every((s) => !(0, exports.isWaiting)(s.state));
|
|
1651
|
+
});
|
|
1652
|
+
return { sandboxes: r.value, timedOut: r.timedOut, waitedMs: r.waitedMs };
|
|
1653
|
+
},
|
|
1654
|
+
waitDone: async (runId, opts = {}) => {
|
|
1655
|
+
const r = await this.waitLoop(opts, exports.LONG_POLL_MS, { run_id: runId, state: "running" }, (read) => this.runs.get(runId, read), (run) => {
|
|
1656
|
+
const terminal = run.state === "completed" || run.state === "cap_killed" || run.state === "aborted";
|
|
1657
|
+
const allDone = Array.isArray(run.sandboxes) && run.sandboxes.length > 0 &&
|
|
1658
|
+
run.sandboxes.every((s) => s.state === "ended" || s.command_outcome != null);
|
|
1659
|
+
return terminal || allDone;
|
|
1660
|
+
});
|
|
1661
|
+
return { run: r.value, timedOut: r.timedOut, waitedMs: r.waitedMs };
|
|
1662
|
+
},
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1648
1665
|
async waitLoop(opts, maxWaitMs, empty, read, done) {
|
|
1649
1666
|
const bound = opts.waitTimeoutMs ?? core_js_1.WAIT_TIMEOUT_MS;
|
|
1650
1667
|
const started = Date.now();
|
|
@@ -1698,720 +1715,722 @@ class PromigenceClient {
|
|
|
1698
1715
|
await sleep(Math.min(opts.pollMs ?? pollInterval(i), Math.max(1, bound - elapsed)));
|
|
1699
1716
|
}
|
|
1700
1717
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
const body = {};
|
|
1707
|
-
if (opts.argv?.length)
|
|
1708
|
-
body["argv"] = opts.argv;
|
|
1709
|
-
if (opts.sh)
|
|
1710
|
-
body["sh"] = opts.sh;
|
|
1711
|
-
if (opts.cwd)
|
|
1712
|
-
body["cwd"] = opts.cwd;
|
|
1713
|
-
if (opts.env)
|
|
1714
|
-
body["env"] = opts.env;
|
|
1715
|
-
if (opts.user)
|
|
1716
|
-
body["user"] = opts.user;
|
|
1717
|
-
if (opts.secrets?.length)
|
|
1718
|
-
body["secrets"] = opts.secrets;
|
|
1719
|
-
if (opts.terminal)
|
|
1720
|
-
body["terminal"] = true;
|
|
1721
|
-
if (opts.killOnExit !== undefined)
|
|
1722
|
-
body["kill_on_exit"] = opts.killOnExit;
|
|
1723
|
-
const timeoutMs = commandTimeoutMs(opts.timeoutMs) ?? 15 * 60_000;
|
|
1724
|
-
body["timeout_ms"] = timeoutMs;
|
|
1725
|
-
body["max_output_bytes"] = opts.maxOutputBytes ?? core_js_1.MAX_OUTPUT_BYTES_DEFAULT;
|
|
1726
|
-
if (opts.encoding === "base64")
|
|
1727
|
-
body["encoding"] = "base64";
|
|
1728
|
-
const req = {
|
|
1729
|
-
method: "POST",
|
|
1730
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs`,
|
|
1731
|
-
body,
|
|
1732
|
-
timeoutMs: timeoutMs + 30_000,
|
|
1733
|
-
};
|
|
1734
|
-
if (opts.idempotencyKey)
|
|
1735
|
-
req.idempotencyKey = opts.idempotencyKey;
|
|
1736
|
-
if (opts.signal)
|
|
1737
|
-
req.signal = opts.signal;
|
|
1738
|
-
return this.request(req);
|
|
1739
|
-
},
|
|
1740
|
-
execStream: async (sandboxId, opts, on = {}) => {
|
|
1741
|
-
if (!opts.argv?.length && !opts.sh) {
|
|
1742
|
-
throw new core_js_1.InvalidArgumentError("exec needs argv or sh", "Pass { argv: ['bun','typecheck'] } or { sh: 'bun typecheck' }.");
|
|
1743
|
-
}
|
|
1744
|
-
if (opts.idempotencyKey) {
|
|
1745
|
-
throw new core_js_1.InvalidArgumentError("a streamed exec cannot take an idempotencyKey (its output cannot be replayed)", "Use sandboxes.exec (buffered) or sandboxes.execBackground with the key, or stream without one.");
|
|
1746
|
-
}
|
|
1747
|
-
const body = {};
|
|
1748
|
-
if (opts.argv?.length)
|
|
1749
|
-
body["argv"] = opts.argv;
|
|
1750
|
-
if (opts.sh)
|
|
1751
|
-
body["sh"] = opts.sh;
|
|
1752
|
-
if (opts.cwd)
|
|
1753
|
-
body["cwd"] = opts.cwd;
|
|
1754
|
-
if (opts.env)
|
|
1755
|
-
body["env"] = opts.env;
|
|
1756
|
-
if (opts.user)
|
|
1757
|
-
body["user"] = opts.user;
|
|
1758
|
-
if (opts.secrets?.length)
|
|
1759
|
-
body["secrets"] = opts.secrets;
|
|
1760
|
-
if (opts.terminal)
|
|
1761
|
-
body["terminal"] = true;
|
|
1762
|
-
if (opts.killOnExit !== undefined)
|
|
1763
|
-
body["kill_on_exit"] = opts.killOnExit;
|
|
1764
|
-
body["timeout_ms"] = commandTimeoutMs(opts.timeoutMs) ?? 15 * 60_000;
|
|
1765
|
-
body["max_output_bytes"] = opts.maxOutputBytes ?? core_js_1.MAX_OUTPUT_BYTES_DEFAULT;
|
|
1766
|
-
const url = `${this.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/execs`;
|
|
1767
|
-
const idle = new AbortController();
|
|
1768
|
-
let silent = false;
|
|
1769
|
-
let reader;
|
|
1770
|
-
let timer;
|
|
1771
|
-
const arm = () => {
|
|
1772
|
-
clearTimeout(timer);
|
|
1773
|
-
timer = setTimeout(() => {
|
|
1774
|
-
silent = true;
|
|
1775
|
-
idle.abort();
|
|
1776
|
-
reader?.cancel().catch(() => { });
|
|
1777
|
-
}, STREAM_IDLE_MS);
|
|
1778
|
-
};
|
|
1779
|
-
let stream;
|
|
1780
|
-
const bearer = `Bearer ${this.requireKey()}`;
|
|
1781
|
-
const dispatcher = this.guardDispatcher();
|
|
1782
|
-
for (let attempt = 0; stream === undefined; attempt++) {
|
|
1783
|
-
let r;
|
|
1784
|
-
this.stats.requests++;
|
|
1785
|
-
arm();
|
|
1786
|
-
try {
|
|
1787
|
-
r = await this.fetchImpl(url, {
|
|
1788
|
-
method: "POST",
|
|
1789
|
-
headers: {
|
|
1790
|
-
accept: "text/event-stream",
|
|
1791
|
-
"content-type": "application/json",
|
|
1792
|
-
"user-agent": this.userAgent,
|
|
1793
|
-
authorization: bearer,
|
|
1794
|
-
...(this.caller ? { [caller_js_1.CALLER_HEADER]: this.caller } : {}),
|
|
1795
|
-
},
|
|
1796
|
-
body: JSON.stringify(body),
|
|
1797
|
-
signal: opts.signal ? AbortSignal.any([opts.signal, idle.signal]) : idle.signal,
|
|
1798
|
-
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
1799
|
-
});
|
|
1800
|
-
}
|
|
1801
|
-
catch (err) {
|
|
1802
|
-
clearTimeout(timer);
|
|
1803
|
-
const e = err;
|
|
1804
|
-
const why = silent ? `no answer in ${STREAM_IDLE_MS / 1000} s` : transportDetail(e);
|
|
1805
|
-
throw new core_js_1.UnreachableError(`cannot reach ${url}: ${why}`);
|
|
1806
|
-
}
|
|
1807
|
-
if (r.ok && r.body) {
|
|
1808
|
-
stream = r.body;
|
|
1809
|
-
break;
|
|
1718
|
+
group_sandboxes() {
|
|
1719
|
+
return {
|
|
1720
|
+
exec: (sandboxId, opts) => {
|
|
1721
|
+
if (!opts.argv?.length && !opts.sh) {
|
|
1722
|
+
throw new core_js_1.InvalidArgumentError("exec needs argv or sh", "Pass { argv: ['bun','typecheck'] } or { sh: 'bun typecheck' }.");
|
|
1810
1723
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1724
|
+
const body = {};
|
|
1725
|
+
if (opts.argv?.length)
|
|
1726
|
+
body["argv"] = opts.argv;
|
|
1727
|
+
if (opts.sh)
|
|
1728
|
+
body["sh"] = opts.sh;
|
|
1729
|
+
if (opts.cwd)
|
|
1730
|
+
body["cwd"] = opts.cwd;
|
|
1731
|
+
if (opts.env)
|
|
1732
|
+
body["env"] = opts.env;
|
|
1733
|
+
if (opts.user)
|
|
1734
|
+
body["user"] = opts.user;
|
|
1735
|
+
if (opts.secrets?.length)
|
|
1736
|
+
body["secrets"] = opts.secrets;
|
|
1737
|
+
if (opts.terminal)
|
|
1738
|
+
body["terminal"] = true;
|
|
1739
|
+
if (opts.killOnExit !== undefined)
|
|
1740
|
+
body["kill_on_exit"] = opts.killOnExit;
|
|
1741
|
+
const timeoutMs = commandTimeoutMs(opts.timeoutMs) ?? 15 * 60_000;
|
|
1742
|
+
body["timeout_ms"] = timeoutMs;
|
|
1743
|
+
body["max_output_bytes"] = opts.maxOutputBytes ?? core_js_1.MAX_OUTPUT_BYTES_DEFAULT;
|
|
1744
|
+
if (opts.encoding === "base64")
|
|
1745
|
+
body["encoding"] = "base64";
|
|
1746
|
+
const req = {
|
|
1747
|
+
method: "POST",
|
|
1748
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs`,
|
|
1749
|
+
body,
|
|
1750
|
+
timeoutMs: timeoutMs + 30_000,
|
|
1751
|
+
};
|
|
1752
|
+
if (opts.idempotencyKey)
|
|
1753
|
+
req.idempotencyKey = opts.idempotencyKey;
|
|
1754
|
+
if (opts.signal)
|
|
1755
|
+
req.signal = opts.signal;
|
|
1756
|
+
return this.request(req);
|
|
1757
|
+
},
|
|
1758
|
+
execStream: async (sandboxId, opts, on = {}) => {
|
|
1759
|
+
if (!opts.argv?.length && !opts.sh) {
|
|
1760
|
+
throw new core_js_1.InvalidArgumentError("exec needs argv or sh", "Pass { argv: ['bun','typecheck'] } or { sh: 'bun typecheck' }.");
|
|
1816
1761
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
const wait = this.refusalBackoff(r.status, retryAfterMs, payload.error?.code, attempt, false, opts.signal);
|
|
1820
|
-
if (wait !== undefined) {
|
|
1821
|
-
this.stats.retries++;
|
|
1822
|
-
await sleep(wait);
|
|
1823
|
-
continue;
|
|
1762
|
+
if (opts.idempotencyKey) {
|
|
1763
|
+
throw new core_js_1.InvalidArgumentError("a streamed exec cannot take an idempotencyKey (its output cannot be replayed)", "Use sandboxes.exec (buffered) or sandboxes.execBackground with the key, or stream without one.");
|
|
1824
1764
|
}
|
|
1825
|
-
const
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1765
|
+
const body = {};
|
|
1766
|
+
if (opts.argv?.length)
|
|
1767
|
+
body["argv"] = opts.argv;
|
|
1768
|
+
if (opts.sh)
|
|
1769
|
+
body["sh"] = opts.sh;
|
|
1770
|
+
if (opts.cwd)
|
|
1771
|
+
body["cwd"] = opts.cwd;
|
|
1772
|
+
if (opts.env)
|
|
1773
|
+
body["env"] = opts.env;
|
|
1774
|
+
if (opts.user)
|
|
1775
|
+
body["user"] = opts.user;
|
|
1776
|
+
if (opts.secrets?.length)
|
|
1777
|
+
body["secrets"] = opts.secrets;
|
|
1778
|
+
if (opts.terminal)
|
|
1779
|
+
body["terminal"] = true;
|
|
1780
|
+
if (opts.killOnExit !== undefined)
|
|
1781
|
+
body["kill_on_exit"] = opts.killOnExit;
|
|
1782
|
+
body["timeout_ms"] = commandTimeoutMs(opts.timeoutMs) ?? 15 * 60_000;
|
|
1783
|
+
body["max_output_bytes"] = opts.maxOutputBytes ?? core_js_1.MAX_OUTPUT_BYTES_DEFAULT;
|
|
1784
|
+
const url = `${this.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/execs`;
|
|
1785
|
+
const idle = new AbortController();
|
|
1786
|
+
let silent = false;
|
|
1787
|
+
let reader;
|
|
1788
|
+
let timer;
|
|
1789
|
+
const arm = () => {
|
|
1790
|
+
clearTimeout(timer);
|
|
1791
|
+
timer = setTimeout(() => {
|
|
1792
|
+
silent = true;
|
|
1793
|
+
idle.abort();
|
|
1794
|
+
reader?.cancel().catch(() => { });
|
|
1795
|
+
}, STREAM_IDLE_MS);
|
|
1796
|
+
};
|
|
1797
|
+
let stream;
|
|
1798
|
+
const bearer = `Bearer ${this.requireKey()}`;
|
|
1799
|
+
const dispatcher = this.guardDispatcher();
|
|
1800
|
+
for (let attempt = 0; stream === undefined; attempt++) {
|
|
1801
|
+
let r;
|
|
1802
|
+
this.stats.requests++;
|
|
1843
1803
|
arm();
|
|
1844
|
-
let chunk;
|
|
1845
1804
|
try {
|
|
1846
|
-
|
|
1805
|
+
r = await this.fetchImpl(url, {
|
|
1806
|
+
method: "POST",
|
|
1807
|
+
headers: {
|
|
1808
|
+
accept: "text/event-stream",
|
|
1809
|
+
"content-type": "application/json",
|
|
1810
|
+
"user-agent": this.userAgent,
|
|
1811
|
+
authorization: bearer,
|
|
1812
|
+
...(this.caller ? { [caller_js_1.CALLER_HEADER]: this.caller } : {}),
|
|
1813
|
+
},
|
|
1814
|
+
body: JSON.stringify(body),
|
|
1815
|
+
signal: opts.signal ? AbortSignal.any([opts.signal, idle.signal]) : idle.signal,
|
|
1816
|
+
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
1817
|
+
});
|
|
1847
1818
|
}
|
|
1848
1819
|
catch (err) {
|
|
1849
|
-
|
|
1850
|
-
throw err;
|
|
1851
|
-
if (silent)
|
|
1852
|
-
throw lost(`was silent for ${STREAM_IDLE_MS / 1000} s`);
|
|
1820
|
+
clearTimeout(timer);
|
|
1853
1821
|
const e = err;
|
|
1854
|
-
|
|
1822
|
+
const why = silent ? `no answer in ${STREAM_IDLE_MS / 1000} s` : transportDetail(e);
|
|
1823
|
+
throw new core_js_1.UnreachableError(`cannot reach ${url}: ${why}`);
|
|
1855
1824
|
}
|
|
1856
|
-
if (
|
|
1825
|
+
if (r.ok && r.body) {
|
|
1826
|
+
stream = r.body;
|
|
1857
1827
|
break;
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1828
|
+
}
|
|
1829
|
+
clearTimeout(timer);
|
|
1830
|
+
const text = await r.text().catch(() => "");
|
|
1831
|
+
let payload = {};
|
|
1832
|
+
try {
|
|
1833
|
+
payload = text ? JSON.parse(text) : {};
|
|
1834
|
+
}
|
|
1835
|
+
catch { }
|
|
1836
|
+
const retryAfterMs = parseRetryAfter(r.headers.get("retry-after"));
|
|
1837
|
+
const wait = this.refusalBackoff(r.status, retryAfterMs, payload.error?.code, attempt, false, opts.signal);
|
|
1838
|
+
if (wait !== undefined) {
|
|
1839
|
+
this.stats.retries++;
|
|
1840
|
+
await sleep(wait);
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
const code = payload.error?.code ?? codeForStatus(r.status);
|
|
1844
|
+
throw errorFor(code, payload.error?.message ?? `HTTP ${r.status} from POST /sandboxes/${sandboxId}/execs`, payload.error?.fix ?? "See the API response for details.", {
|
|
1845
|
+
status: r.status,
|
|
1846
|
+
...bodyExtra(payload),
|
|
1847
|
+
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
const out = [];
|
|
1851
|
+
const errOut = [];
|
|
1852
|
+
let exit;
|
|
1853
|
+
let failure;
|
|
1854
|
+
const dec = new TextDecoder();
|
|
1855
|
+
let buf = "";
|
|
1856
|
+
const lost = (how) => new core_js_1.UnreachableError(`the exec stream for sandbox ${sandboxId} ${how}`, "The connection dropped before the command finished; its result is unknown. " +
|
|
1857
|
+
`Read it by id (\`promigence exec output <exec_id> --sandbox ${(0, hint_js_1.hintArg)(sandboxId)}\`) or re-run.`);
|
|
1858
|
+
reader = stream.getReader();
|
|
1859
|
+
try {
|
|
1860
|
+
for (;;) {
|
|
1861
|
+
arm();
|
|
1862
|
+
let chunk;
|
|
1883
1863
|
try {
|
|
1884
|
-
|
|
1864
|
+
chunk = await reader.read();
|
|
1885
1865
|
}
|
|
1886
|
-
catch {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
if (
|
|
1890
|
-
|
|
1891
|
-
|
|
1866
|
+
catch (err) {
|
|
1867
|
+
if (opts.signal?.aborted)
|
|
1868
|
+
throw err;
|
|
1869
|
+
if (silent)
|
|
1870
|
+
throw lost(`was silent for ${STREAM_IDLE_MS / 1000} s`);
|
|
1871
|
+
const e = err;
|
|
1872
|
+
throw lost(`was cut off (${e?.cause?.code ?? e?.message ?? "connection lost"})`);
|
|
1873
|
+
}
|
|
1874
|
+
if (chunk.done)
|
|
1875
|
+
break;
|
|
1876
|
+
const value = chunk.value;
|
|
1877
|
+
buf += dec.decode(value, { stream: true });
|
|
1878
|
+
let sep;
|
|
1879
|
+
while ((sep = buf.indexOf("\n\n")) >= 0) {
|
|
1880
|
+
const block = buf.slice(0, sep);
|
|
1881
|
+
buf = buf.slice(sep + 2);
|
|
1882
|
+
let event = "";
|
|
1883
|
+
let data = "";
|
|
1884
|
+
for (const line of block.split("\n")) {
|
|
1885
|
+
if (line.startsWith("event:"))
|
|
1886
|
+
event = line.slice(6).trim();
|
|
1887
|
+
else if (line.startsWith("data:"))
|
|
1888
|
+
data = line.slice(5).replace(/^ /, "");
|
|
1892
1889
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1890
|
+
if (!event)
|
|
1891
|
+
continue;
|
|
1892
|
+
if (event === "heartbeat") {
|
|
1893
|
+
on.heartbeat?.();
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
if (event === "reconnect") {
|
|
1897
|
+
reader.cancel().catch(() => { });
|
|
1898
|
+
throw new core_js_1.ServiceRestartError(`a service restart stopped the command on sandbox ${sandboxId} while its output was streaming`, "Not billed: the time the command ran is credited back. The sandbox kept running; the command may have done part of its work before it stopped: run it again if that is safe.");
|
|
1899
|
+
}
|
|
1900
|
+
let parsed = {};
|
|
1901
|
+
try {
|
|
1902
|
+
parsed = JSON.parse(data);
|
|
1903
|
+
}
|
|
1904
|
+
catch { }
|
|
1905
|
+
if (event === "stdout" || event === "stderr") {
|
|
1906
|
+
const bytes = b64ToBytes(String(parsed["data"] ?? ""));
|
|
1907
|
+
if (event === "stdout") {
|
|
1908
|
+
out.push(bytes);
|
|
1909
|
+
on.stdout?.(bytes);
|
|
1910
|
+
}
|
|
1911
|
+
else {
|
|
1912
|
+
errOut.push(bytes);
|
|
1913
|
+
on.stderr?.(bytes);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
else if (event === "exit") {
|
|
1917
|
+
exit = { ...(exit ?? {}), ...parsed };
|
|
1918
|
+
}
|
|
1919
|
+
else if (event === "error") {
|
|
1920
|
+
failure = parsed;
|
|
1896
1921
|
}
|
|
1897
|
-
}
|
|
1898
|
-
else if (event === "exit") {
|
|
1899
|
-
exit = { ...(exit ?? {}), ...parsed };
|
|
1900
|
-
}
|
|
1901
|
-
else if (event === "error") {
|
|
1902
|
-
failure = parsed;
|
|
1903
1922
|
}
|
|
1904
1923
|
}
|
|
1905
1924
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
clearTimeout(timer);
|
|
1909
|
-
}
|
|
1910
|
-
if (failure) {
|
|
1911
|
-
throw errorFor(failure.code ?? "internal", failure.message ?? "the exec stream failed", failure.fix ?? "Re-run; if it repeats, include the exec_id.", { status: 200, ...bodyExtra({ error: failure }) });
|
|
1912
|
-
}
|
|
1913
|
-
if (!exit && silent)
|
|
1914
|
-
throw lost(`was silent for ${STREAM_IDLE_MS / 1000} s`);
|
|
1915
|
-
if (!exit) {
|
|
1916
|
-
throw lost("ended without an exit event");
|
|
1917
|
-
}
|
|
1918
|
-
const rawCode = exit["exit_code"];
|
|
1919
|
-
const signal = exit["signal"];
|
|
1920
|
-
const exitCode = typeof rawCode === "number" && Number.isFinite(rawCode)
|
|
1921
|
-
? rawCode
|
|
1922
|
-
: typeof signal === "number" && signal > 0 ? 128 + signal : null;
|
|
1923
|
-
const join = (parts) => {
|
|
1924
|
-
const n = parts.reduce((a, p) => a + p.length, 0);
|
|
1925
|
-
const all = new Uint8Array(n);
|
|
1926
|
-
let at = 0;
|
|
1927
|
-
for (const p of parts) {
|
|
1928
|
-
all.set(p, at);
|
|
1929
|
-
at += p.length;
|
|
1925
|
+
finally {
|
|
1926
|
+
clearTimeout(timer);
|
|
1930
1927
|
}
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
return {
|
|
1934
|
-
exec_id: String(exit["exec_id"] ?? ""),
|
|
1935
|
-
sandbox_id: sandboxId,
|
|
1936
|
-
exit_code: exitCode,
|
|
1937
|
-
stdout: join(out),
|
|
1938
|
-
stderr: join(errOut),
|
|
1939
|
-
truncated: Boolean(exit["truncated"] ?? false),
|
|
1940
|
-
...(exit["timed_out"] === true ? { timed_out: true } : {}),
|
|
1941
|
-
...(exit["oom_killed"] === true ? { oom_killed: true } : {}),
|
|
1942
|
-
...(typeof exit["reason"] === "string" ? { reason: exit["reason"] } : {}),
|
|
1943
|
-
...(typeof signal === "number" ? { signal } : {}),
|
|
1944
|
-
};
|
|
1945
|
-
},
|
|
1946
|
-
execBackground: (sandboxId, opts) => {
|
|
1947
|
-
if (!opts.argv?.length && !opts.sh) {
|
|
1948
|
-
throw new core_js_1.InvalidArgumentError("exec needs argv or sh", "Pass { argv: ['npm','run','dev'] } or { sh: 'npm run dev' }.");
|
|
1949
|
-
}
|
|
1950
|
-
const body = { background: true };
|
|
1951
|
-
if (opts.argv?.length)
|
|
1952
|
-
body["argv"] = opts.argv;
|
|
1953
|
-
if (opts.sh)
|
|
1954
|
-
body["sh"] = opts.sh;
|
|
1955
|
-
if (opts.cwd)
|
|
1956
|
-
body["cwd"] = opts.cwd;
|
|
1957
|
-
if (opts.env)
|
|
1958
|
-
body["env"] = opts.env;
|
|
1959
|
-
if (opts.user)
|
|
1960
|
-
body["user"] = opts.user;
|
|
1961
|
-
if (opts.secrets?.length)
|
|
1962
|
-
body["secrets"] = opts.secrets;
|
|
1963
|
-
if (opts.stdin)
|
|
1964
|
-
body["stdin"] = true;
|
|
1965
|
-
if (opts.follow)
|
|
1966
|
-
body["follow"] = true;
|
|
1967
|
-
const timeoutMs = commandTimeoutMs(opts.timeoutMs);
|
|
1968
|
-
if (timeoutMs !== undefined)
|
|
1969
|
-
body["timeout_ms"] = timeoutMs;
|
|
1970
|
-
if (opts.maxOutputBytes !== undefined)
|
|
1971
|
-
body["max_output_bytes"] = opts.maxOutputBytes;
|
|
1972
|
-
return this.request({
|
|
1973
|
-
method: "POST",
|
|
1974
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs`,
|
|
1975
|
-
body,
|
|
1976
|
-
...(opts.idempotencyKey ? { idempotencyKey: opts.idempotencyKey } : {}),
|
|
1977
|
-
});
|
|
1978
|
-
},
|
|
1979
|
-
execs: {
|
|
1980
|
-
list: async (sandboxId) => {
|
|
1981
|
-
const r = await this.request({
|
|
1982
|
-
method: "GET", path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs`,
|
|
1983
|
-
});
|
|
1984
|
-
return r.execs ?? [];
|
|
1985
|
-
},
|
|
1986
|
-
get: (sandboxId, execId, opts = {}) => {
|
|
1987
|
-
const wait = opts.waitMs !== undefined && opts.waitMs > 0 ? Math.min(Math.floor(opts.waitMs), WAIT_MS_CAP) : 0;
|
|
1988
|
-
return this.request({
|
|
1989
|
-
method: "GET",
|
|
1990
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}${wait ? `?wait_ms=${wait}` : ""}`,
|
|
1991
|
-
...(wait ? { timeoutMs: wait + this.requestTimeoutMs } : {}),
|
|
1992
|
-
...(opts.signal ? { signal: opts.signal } : {}),
|
|
1993
|
-
});
|
|
1994
|
-
},
|
|
1995
|
-
read: async (sandboxId, execId, stream, opts = {}) => {
|
|
1996
|
-
const q = [`stream=${stream}`, `offset=${Math.max(0, Math.floor(opts.offset ?? 0))}`];
|
|
1997
|
-
if (opts.len !== undefined)
|
|
1998
|
-
q.push(`len=${Math.max(0, Math.floor(opts.len))}`);
|
|
1999
|
-
const wait = opts.waitMs !== undefined && opts.waitMs > 0 ? Math.min(Math.floor(opts.waitMs), WAIT_MS_CAP) : 0;
|
|
2000
|
-
if (wait)
|
|
2001
|
-
q.push(`wait_ms=${wait}`);
|
|
2002
|
-
if (wait && opts.untilOutput)
|
|
2003
|
-
q.push("until=output");
|
|
2004
|
-
const r = await this.request({
|
|
2005
|
-
method: "GET",
|
|
2006
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}?${q.join("&")}`,
|
|
2007
|
-
...(wait ? { timeoutMs: wait + this.requestTimeoutMs } : {}),
|
|
2008
|
-
...(opts.signal ? { signal: opts.signal } : {}),
|
|
2009
|
-
});
|
|
2010
|
-
return { data: b64ToBytes(r.data ?? ""), offset: r.offset ?? opts.offset ?? 0, eof: Boolean(r.eof), skipped: r.skipped ?? 0, record: r };
|
|
2011
|
-
},
|
|
2012
|
-
stdin: (sandboxId, execId, opts = {}) => {
|
|
2013
|
-
if (opts.data === undefined && !opts.eof) {
|
|
2014
|
-
throw new core_js_1.InvalidArgumentError("execs.stdin needs data, eof, or both", "Pass { data: 'y\\n' } to write, { eof: true } to close, or both to do each in one call.");
|
|
1928
|
+
if (failure) {
|
|
1929
|
+
throw errorFor(failure.code ?? "internal", failure.message ?? "the exec stream failed", failure.fix ?? "Re-run; if it repeats, include the exec_id.", { status: 200, ...bodyExtra({ error: failure }) });
|
|
2015
1930
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
body: {
|
|
2021
|
-
...(bytes === undefined ? {} : { data: bytesToB64(bytes) }),
|
|
2022
|
-
...(opts.eof ? { eof: true } : {}),
|
|
2023
|
-
},
|
|
2024
|
-
});
|
|
2025
|
-
},
|
|
2026
|
-
signal: (sandboxId, execId, signal = "SIGTERM") => this.request({
|
|
2027
|
-
method: "POST",
|
|
2028
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}/signal`,
|
|
2029
|
-
body: { signal },
|
|
2030
|
-
}),
|
|
2031
|
-
forget: (sandboxId, execId) => this.request({
|
|
2032
|
-
method: "DELETE",
|
|
2033
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}`,
|
|
2034
|
-
}),
|
|
2035
|
-
},
|
|
2036
|
-
ports: {
|
|
2037
|
-
expose: (sandboxId, port) => {
|
|
2038
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
2039
|
-
throw new core_js_1.InvalidArgumentError(`port must be an integer 1..65535, got ${port}`, "Pass the port your process listens on inside the sandbox, e.g. 3000.");
|
|
1931
|
+
if (!exit && silent)
|
|
1932
|
+
throw lost(`was silent for ${STREAM_IDLE_MS / 1000} s`);
|
|
1933
|
+
if (!exit) {
|
|
1934
|
+
throw lost("ended without an exit event");
|
|
2040
1935
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
1936
|
+
const rawCode = exit["exit_code"];
|
|
1937
|
+
const signal = exit["signal"];
|
|
1938
|
+
const exitCode = typeof rawCode === "number" && Number.isFinite(rawCode)
|
|
1939
|
+
? rawCode
|
|
1940
|
+
: typeof signal === "number" && signal > 0 ? 128 + signal : null;
|
|
1941
|
+
const join = (parts) => {
|
|
1942
|
+
const n = parts.reduce((a, p) => a + p.length, 0);
|
|
1943
|
+
const all = new Uint8Array(n);
|
|
1944
|
+
let at = 0;
|
|
1945
|
+
for (const p of parts) {
|
|
1946
|
+
all.set(p, at);
|
|
1947
|
+
at += p.length;
|
|
1948
|
+
}
|
|
1949
|
+
return new TextDecoder().decode(all);
|
|
1950
|
+
};
|
|
1951
|
+
return {
|
|
1952
|
+
exec_id: String(exit["exec_id"] ?? ""),
|
|
1953
|
+
sandbox_id: sandboxId,
|
|
1954
|
+
exit_code: exitCode,
|
|
1955
|
+
stdout: join(out),
|
|
1956
|
+
stderr: join(errOut),
|
|
1957
|
+
truncated: Boolean(exit["truncated"] ?? false),
|
|
1958
|
+
...(exit["timed_out"] === true ? { timed_out: true } : {}),
|
|
1959
|
+
...(exit["oom_killed"] === true ? { oom_killed: true } : {}),
|
|
1960
|
+
...(typeof exit["reason"] === "string" ? { reason: exit["reason"] } : {}),
|
|
1961
|
+
...(typeof signal === "number" ? { signal } : {}),
|
|
1962
|
+
};
|
|
2050
1963
|
},
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
}),
|
|
2055
|
-
},
|
|
2056
|
-
ssh: {
|
|
2057
|
-
open: (sandboxId, opts) => {
|
|
2058
|
-
if (!opts.publicKey?.trim()) {
|
|
2059
|
-
throw new core_js_1.InvalidArgumentError("publicKey is required", "Pass the contents of an ssh-ed25519 .pub file, or use `promigence sandbox ssh <id>`, which makes one.");
|
|
1964
|
+
execBackground: (sandboxId, opts) => {
|
|
1965
|
+
if (!opts.argv?.length && !opts.sh) {
|
|
1966
|
+
throw new core_js_1.InvalidArgumentError("exec needs argv or sh", "Pass { argv: ['npm','run','dev'] } or { sh: 'npm run dev' }.");
|
|
2060
1967
|
}
|
|
1968
|
+
const body = { background: true };
|
|
1969
|
+
if (opts.argv?.length)
|
|
1970
|
+
body["argv"] = opts.argv;
|
|
1971
|
+
if (opts.sh)
|
|
1972
|
+
body["sh"] = opts.sh;
|
|
1973
|
+
if (opts.cwd)
|
|
1974
|
+
body["cwd"] = opts.cwd;
|
|
1975
|
+
if (opts.env)
|
|
1976
|
+
body["env"] = opts.env;
|
|
1977
|
+
if (opts.user)
|
|
1978
|
+
body["user"] = opts.user;
|
|
1979
|
+
if (opts.secrets?.length)
|
|
1980
|
+
body["secrets"] = opts.secrets;
|
|
1981
|
+
if (opts.stdin)
|
|
1982
|
+
body["stdin"] = true;
|
|
1983
|
+
if (opts.follow)
|
|
1984
|
+
body["follow"] = true;
|
|
1985
|
+
const timeoutMs = commandTimeoutMs(opts.timeoutMs);
|
|
1986
|
+
if (timeoutMs !== undefined)
|
|
1987
|
+
body["timeout_ms"] = timeoutMs;
|
|
1988
|
+
if (opts.maxOutputBytes !== undefined)
|
|
1989
|
+
body["max_output_bytes"] = opts.maxOutputBytes;
|
|
2061
1990
|
return this.request({
|
|
2062
1991
|
method: "POST",
|
|
2063
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/
|
|
2064
|
-
body
|
|
2065
|
-
|
|
2066
|
-
...(opts.ttlS === undefined ? {} : { ttl_s: Math.floor(opts.ttlS) }),
|
|
2067
|
-
...(opts.idleTimeoutS === undefined
|
|
2068
|
-
? {}
|
|
2069
|
-
: { idle_timeout_s: Math.floor(opts.idleTimeoutS) }),
|
|
2070
|
-
},
|
|
1992
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs`,
|
|
1993
|
+
body,
|
|
1994
|
+
...(opts.idempotencyKey ? { idempotencyKey: opts.idempotencyKey } : {}),
|
|
2071
1995
|
});
|
|
2072
1996
|
},
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/ssh/${encodeURIComponent(keyIdOrFingerprint)}`,
|
|
2080
|
-
}),
|
|
2081
|
-
connect: (sandboxId, opts) => (0, ssh_js_1.connectSshTunnel)({
|
|
2082
|
-
url: `${this.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/ssh/connect`,
|
|
2083
|
-
token: opts.token ?? this.requireKey(),
|
|
2084
|
-
userAgent: this.userAgent,
|
|
2085
|
-
...(this.caller ? { caller: this.caller } : {}),
|
|
2086
|
-
onData: opts.onData,
|
|
2087
|
-
onClose: opts.onClose,
|
|
2088
|
-
...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
2089
|
-
refuse: (status, text) => {
|
|
2090
|
-
let payload = {};
|
|
2091
|
-
try {
|
|
2092
|
-
payload = text ? JSON.parse(text) : {};
|
|
2093
|
-
}
|
|
2094
|
-
catch { }
|
|
2095
|
-
const code = payload.error?.code ?? (status === 0 ? "unreachable" : codeForStatus(status));
|
|
2096
|
-
return errorFor(code, payload.error?.message ?? `HTTP ${status} from the SSH tunnel`, payload.error?.fix ?? "See the API response for details.", { status, ...bodyExtra(payload) });
|
|
1997
|
+
execs: {
|
|
1998
|
+
list: async (sandboxId) => {
|
|
1999
|
+
const r = await this.request({
|
|
2000
|
+
method: "GET", path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs`,
|
|
2001
|
+
});
|
|
2002
|
+
return r.execs ?? [];
|
|
2097
2003
|
},
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
q.push(`rows=${Math.floor(opts.rows)}`);
|
|
2106
|
-
const path = `/sandboxes/${encodeURIComponent(sandboxId)}/pty${q.length ? `?${q.join("&")}` : ""}`;
|
|
2107
|
-
const open = () => (0, pty_js_1.connectPty)({
|
|
2108
|
-
url: `${this.apiUrl}${path}`,
|
|
2109
|
-
apiKey: this.requireKey(),
|
|
2110
|
-
userAgent: this.userAgent,
|
|
2111
|
-
...(this.caller ? { caller: this.caller } : {}),
|
|
2112
|
-
...(opts.cmd ? { command: opts.cmd } : {}),
|
|
2113
|
-
...(opts.secrets?.length ? { secrets: opts.secrets } : {}),
|
|
2114
|
-
...(opts.onData ? { onData: opts.onData } : {}),
|
|
2115
|
-
...(opts.onExit ? { onExit: opts.onExit } : {}),
|
|
2116
|
-
...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
2117
|
-
...(opts.resume === undefined ? {} : { resume: opts.resume }),
|
|
2118
|
-
...(opts.reconnect === undefined ? {} : { reconnect: opts.reconnect }),
|
|
2119
|
-
...(opts.onReconnected ? { onReconnected: opts.onReconnected } : {}),
|
|
2120
|
-
refuse: (status, text, retryAfter) => {
|
|
2121
|
-
let payload = {};
|
|
2122
|
-
try {
|
|
2123
|
-
payload = text ? JSON.parse(text) : {};
|
|
2124
|
-
}
|
|
2125
|
-
catch { }
|
|
2126
|
-
const code = payload.error?.code ?? (status === 0 ? "unreachable" : codeForStatus(status));
|
|
2127
|
-
const retryAfterMs = parseRetryAfter(retryAfter ?? null);
|
|
2128
|
-
return errorFor(code, payload.error?.message ?? `HTTP ${status} from GET ${path}`, payload.error?.fix ?? "See the API response for details.", {
|
|
2129
|
-
status,
|
|
2130
|
-
...bodyExtra(payload),
|
|
2131
|
-
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
2004
|
+
get: (sandboxId, execId, opts = {}) => {
|
|
2005
|
+
const wait = opts.waitMs !== undefined && opts.waitMs > 0 ? Math.min(Math.floor(opts.waitMs), WAIT_MS_CAP) : 0;
|
|
2006
|
+
return this.request({
|
|
2007
|
+
method: "GET",
|
|
2008
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}${wait ? `?wait_ms=${wait}` : ""}`,
|
|
2009
|
+
...(wait ? { timeoutMs: wait + this.requestTimeoutMs } : {}),
|
|
2010
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
2132
2011
|
});
|
|
2133
2012
|
},
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2013
|
+
read: async (sandboxId, execId, stream, opts = {}) => {
|
|
2014
|
+
const q = [`stream=${stream}`, `offset=${Math.max(0, Math.floor(opts.offset ?? 0))}`];
|
|
2015
|
+
if (opts.len !== undefined)
|
|
2016
|
+
q.push(`len=${Math.max(0, Math.floor(opts.len))}`);
|
|
2017
|
+
const wait = opts.waitMs !== undefined && opts.waitMs > 0 ? Math.min(Math.floor(opts.waitMs), WAIT_MS_CAP) : 0;
|
|
2018
|
+
if (wait)
|
|
2019
|
+
q.push(`wait_ms=${wait}`);
|
|
2020
|
+
if (wait && opts.untilOutput)
|
|
2021
|
+
q.push("until=output");
|
|
2022
|
+
const r = await this.request({
|
|
2023
|
+
method: "GET",
|
|
2024
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}?${q.join("&")}`,
|
|
2025
|
+
...(wait ? { timeoutMs: wait + this.requestTimeoutMs } : {}),
|
|
2026
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
2027
|
+
});
|
|
2028
|
+
return { data: b64ToBytes(r.data ?? ""), offset: r.offset ?? opts.offset ?? 0, eof: Boolean(r.eof), skipped: r.skipped ?? 0, record: r };
|
|
2029
|
+
},
|
|
2030
|
+
stdin: (sandboxId, execId, opts = {}) => {
|
|
2031
|
+
if (opts.data === undefined && !opts.eof) {
|
|
2032
|
+
throw new core_js_1.InvalidArgumentError("execs.stdin needs data, eof, or both", "Pass { data: 'y\\n' } to write, { eof: true } to close, or both to do each in one call.");
|
|
2147
2033
|
}
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2034
|
+
const bytes = typeof opts.data === "string" ? new TextEncoder().encode(opts.data) : opts.data;
|
|
2035
|
+
return this.request({
|
|
2036
|
+
method: "POST",
|
|
2037
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}/stdin`,
|
|
2038
|
+
body: {
|
|
2039
|
+
...(bytes === undefined ? {} : { data: bytesToB64(bytes) }),
|
|
2040
|
+
...(opts.eof ? { eof: true } : {}),
|
|
2041
|
+
},
|
|
2042
|
+
});
|
|
2043
|
+
},
|
|
2044
|
+
signal: (sandboxId, execId, signal = "SIGTERM") => this.request({
|
|
2045
|
+
method: "POST",
|
|
2046
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}/signal`,
|
|
2047
|
+
body: { signal },
|
|
2048
|
+
}),
|
|
2049
|
+
forget: (sandboxId, execId) => this.request({
|
|
2050
|
+
method: "DELETE",
|
|
2051
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/execs/${encodeURIComponent(execId)}`,
|
|
2052
|
+
}),
|
|
2167
2053
|
},
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2054
|
+
ports: {
|
|
2055
|
+
expose: (sandboxId, port) => {
|
|
2056
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
2057
|
+
throw new core_js_1.InvalidArgumentError(`port must be an integer 1..65535, got ${port}`, "Pass the port your process listens on inside the sandbox, e.g. 3000.");
|
|
2058
|
+
}
|
|
2059
|
+
return (0, core_js_1.withPreviewsFix)(this.request({
|
|
2060
|
+
method: "POST", path: `/sandboxes/${encodeURIComponent(sandboxId)}/ports`, body: { port },
|
|
2061
|
+
}), sandboxId, port);
|
|
2062
|
+
},
|
|
2063
|
+
list: async (sandboxId) => {
|
|
2064
|
+
const r = await (0, core_js_1.withPreviewsFix)(this.request({
|
|
2065
|
+
method: "GET", path: `/sandboxes/${encodeURIComponent(sandboxId)}/ports`,
|
|
2066
|
+
}), sandboxId);
|
|
2067
|
+
return r.ports ?? [];
|
|
2068
|
+
},
|
|
2069
|
+
revoke: (sandboxId, tokenOrPort) => this.request({
|
|
2070
|
+
method: "DELETE",
|
|
2071
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/ports/${encodeURIComponent(String(tokenOrPort))}`,
|
|
2072
|
+
}),
|
|
2176
2073
|
},
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
},
|
|
2196
|
-
probe: (sandboxId, opts = {}) => this.request({
|
|
2197
|
-
method: "POST",
|
|
2198
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/probe`,
|
|
2199
|
-
body: opts.paths?.length ? { paths: opts.paths } : {},
|
|
2200
|
-
}),
|
|
2201
|
-
kill: (sandboxId) => this.request({ method: "DELETE", path: `/sandboxes/${encodeURIComponent(sandboxId)}` }),
|
|
2202
|
-
network: (sandboxId) => this.request({ method: "GET", path: `/sandboxes/${encodeURIComponent(sandboxId)}/network` }),
|
|
2203
|
-
setNetworkNone: (sandboxId) => this.request({
|
|
2204
|
-
method: "POST",
|
|
2205
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/network`,
|
|
2206
|
-
body: { network: "none" },
|
|
2207
|
-
}),
|
|
2208
|
-
patch: (sandboxId, opts) => {
|
|
2209
|
-
const body = {};
|
|
2210
|
-
if (opts.timeoutMs !== undefined) {
|
|
2211
|
-
if (!Number.isInteger(opts.timeoutMs) || opts.timeoutMs <= 0) {
|
|
2212
|
-
throw new core_js_1.InvalidArgumentError("sandboxes.patch needs a positive timeoutMs", "Pass { timeoutMs: 1_800_000 }.");
|
|
2213
|
-
}
|
|
2214
|
-
body["timeout_ms"] = opts.timeoutMs;
|
|
2215
|
-
}
|
|
2216
|
-
if (opts.autoPauseS !== undefined) {
|
|
2217
|
-
if (!Number.isInteger(opts.autoPauseS) || opts.autoPauseS < 0) {
|
|
2218
|
-
throw new core_js_1.InvalidArgumentError("sandboxes.patch needs a non-negative whole number of seconds for autoPauseS", "Pass { autoPauseS: 900 } to pause it after 15 idle minutes, or { autoPauseS: 0 } to turn that off.");
|
|
2219
|
-
}
|
|
2220
|
-
body["auto_pause_s"] = opts.autoPauseS;
|
|
2221
|
-
}
|
|
2222
|
-
if (opts.autoPauseMode !== undefined) {
|
|
2223
|
-
if (opts.autoPauseMode !== "requests" && opts.autoPauseMode !== "activity") {
|
|
2224
|
-
throw new core_js_1.InvalidArgumentError(`sandboxes.patch: autoPauseMode must be "requests" or "activity", got ${JSON.stringify(opts.autoPauseMode)}`, 'Pass { autoPauseMode: "activity" } for an agent that runs inside the sandbox.');
|
|
2225
|
-
}
|
|
2226
|
-
body["auto_pause_mode"] = opts.autoPauseMode;
|
|
2227
|
-
}
|
|
2228
|
-
if (Object.keys(body).length === 0) {
|
|
2229
|
-
throw new core_js_1.InvalidArgumentError("sandboxes.patch was given nothing to change", "Pass { timeoutMs } to extend it, { autoPauseS } to set its idle rule, or both.");
|
|
2230
|
-
}
|
|
2231
|
-
return this.request({
|
|
2232
|
-
method: "PATCH",
|
|
2233
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}`,
|
|
2234
|
-
body,
|
|
2235
|
-
});
|
|
2236
|
-
},
|
|
2237
|
-
files: {
|
|
2238
|
-
list: (sandboxId, path = "/", opts = {}) => {
|
|
2239
|
-
const q = [`path=${encodeURIComponent(path)}`];
|
|
2240
|
-
if (opts.recursive)
|
|
2241
|
-
q.push("recursive=1");
|
|
2242
|
-
if (opts.depth !== undefined)
|
|
2243
|
-
q.push(`depth=${Math.max(0, Math.floor(opts.depth))}`);
|
|
2244
|
-
if (opts.limit !== undefined)
|
|
2245
|
-
q.push(`limit=${Math.max(1, Math.floor(opts.limit))}`);
|
|
2246
|
-
if (opts.hidden === false)
|
|
2247
|
-
q.push("hidden=0");
|
|
2248
|
-
return this.request({
|
|
2074
|
+
ssh: {
|
|
2075
|
+
open: (sandboxId, opts) => {
|
|
2076
|
+
if (!opts.publicKey?.trim()) {
|
|
2077
|
+
throw new core_js_1.InvalidArgumentError("publicKey is required", "Pass the contents of an ssh-ed25519 .pub file, or use `promigence sandbox ssh <id>`, which makes one.");
|
|
2078
|
+
}
|
|
2079
|
+
return this.request({
|
|
2080
|
+
method: "POST",
|
|
2081
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/ssh`,
|
|
2082
|
+
body: {
|
|
2083
|
+
public_key: opts.publicKey.trim(),
|
|
2084
|
+
...(opts.ttlS === undefined ? {} : { ttl_s: Math.floor(opts.ttlS) }),
|
|
2085
|
+
...(opts.idleTimeoutS === undefined
|
|
2086
|
+
? {}
|
|
2087
|
+
: { idle_timeout_s: Math.floor(opts.idleTimeoutS) }),
|
|
2088
|
+
},
|
|
2089
|
+
});
|
|
2090
|
+
},
|
|
2091
|
+
list: (sandboxId) => this.request({
|
|
2249
2092
|
method: "GET",
|
|
2250
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/
|
|
2251
|
-
})
|
|
2093
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/ssh`,
|
|
2094
|
+
}),
|
|
2095
|
+
revoke: (sandboxId, keyIdOrFingerprint) => this.request({
|
|
2096
|
+
method: "DELETE",
|
|
2097
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/ssh/${encodeURIComponent(keyIdOrFingerprint)}`,
|
|
2098
|
+
}),
|
|
2099
|
+
connect: (sandboxId, opts) => (0, ssh_js_1.connectSshTunnel)({
|
|
2100
|
+
url: `${this.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/ssh/connect`,
|
|
2101
|
+
token: opts.token ?? this.requireKey(),
|
|
2102
|
+
userAgent: this.userAgent,
|
|
2103
|
+
...(this.caller ? { caller: this.caller } : {}),
|
|
2104
|
+
onData: opts.onData,
|
|
2105
|
+
onClose: opts.onClose,
|
|
2106
|
+
...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
2107
|
+
refuse: (status, text) => {
|
|
2108
|
+
let payload = {};
|
|
2109
|
+
try {
|
|
2110
|
+
payload = text ? JSON.parse(text) : {};
|
|
2111
|
+
}
|
|
2112
|
+
catch { }
|
|
2113
|
+
const code = payload.error?.code ?? (status === 0 ? "unreachable" : codeForStatus(status));
|
|
2114
|
+
return errorFor(code, payload.error?.message ?? `HTTP ${status} from the SSH tunnel`, payload.error?.fix ?? "See the API response for details.", { status, ...bodyExtra(payload) });
|
|
2115
|
+
},
|
|
2116
|
+
}),
|
|
2252
2117
|
},
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2118
|
+
pty: (sandboxId, opts = {}) => {
|
|
2119
|
+
const q = [];
|
|
2120
|
+
if (opts.cols !== undefined)
|
|
2121
|
+
q.push(`cols=${Math.floor(opts.cols)}`);
|
|
2122
|
+
if (opts.rows !== undefined)
|
|
2123
|
+
q.push(`rows=${Math.floor(opts.rows)}`);
|
|
2124
|
+
const path = `/sandboxes/${encodeURIComponent(sandboxId)}/pty${q.length ? `?${q.join("&")}` : ""}`;
|
|
2125
|
+
const open = () => (0, pty_js_1.connectPty)({
|
|
2126
|
+
url: `${this.apiUrl}${path}`,
|
|
2127
|
+
apiKey: this.requireKey(),
|
|
2128
|
+
userAgent: this.userAgent,
|
|
2129
|
+
...(this.caller ? { caller: this.caller } : {}),
|
|
2130
|
+
...(opts.cmd ? { command: opts.cmd } : {}),
|
|
2131
|
+
...(opts.secrets?.length ? { secrets: opts.secrets } : {}),
|
|
2132
|
+
...(opts.onData ? { onData: opts.onData } : {}),
|
|
2133
|
+
...(opts.onExit ? { onExit: opts.onExit } : {}),
|
|
2134
|
+
...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
2135
|
+
...(opts.resume === undefined ? {} : { resume: opts.resume }),
|
|
2136
|
+
...(opts.reconnect === undefined ? {} : { reconnect: opts.reconnect }),
|
|
2137
|
+
...(opts.onReconnected ? { onReconnected: opts.onReconnected } : {}),
|
|
2138
|
+
refuse: (status, text, retryAfter) => {
|
|
2139
|
+
let payload = {};
|
|
2140
|
+
try {
|
|
2141
|
+
payload = text ? JSON.parse(text) : {};
|
|
2142
|
+
}
|
|
2143
|
+
catch { }
|
|
2144
|
+
const code = payload.error?.code ?? (status === 0 ? "unreachable" : codeForStatus(status));
|
|
2145
|
+
const retryAfterMs = parseRetryAfter(retryAfter ?? null);
|
|
2146
|
+
return errorFor(code, payload.error?.message ?? `HTTP ${status} from GET ${path}`, payload.error?.fix ?? "See the API response for details.", {
|
|
2147
|
+
status,
|
|
2148
|
+
...bodyExtra(payload),
|
|
2149
|
+
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
2150
|
+
});
|
|
2151
|
+
},
|
|
2271
2152
|
});
|
|
2153
|
+
return (async () => {
|
|
2154
|
+
for (let attempt = 0;; attempt++) {
|
|
2155
|
+
try {
|
|
2156
|
+
return await open();
|
|
2157
|
+
}
|
|
2158
|
+
catch (err) {
|
|
2159
|
+
const e = err instanceof core_js_1.PromigenceError ? err : undefined;
|
|
2160
|
+
const wait = e ? this.refusalBackoff(e.status, e.retryAfterMs, e.code, attempt, false) : undefined;
|
|
2161
|
+
if (wait === undefined)
|
|
2162
|
+
throw err;
|
|
2163
|
+
this.stats.retries++;
|
|
2164
|
+
await sleep(wait);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
})();
|
|
2272
2168
|
},
|
|
2273
|
-
|
|
2169
|
+
get: (sandboxId) => {
|
|
2170
|
+
checkSandboxId(sandboxId, "sandboxes.get");
|
|
2171
|
+
return this.request({ method: "GET", path: `/sandboxes/${encodeURIComponent(sandboxId)}` });
|
|
2172
|
+
},
|
|
2173
|
+
pause: (sandboxId) => this.request({
|
|
2274
2174
|
method: "POST",
|
|
2275
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/
|
|
2276
|
-
body: {
|
|
2277
|
-
|
|
2278
|
-
...(opts.mode === undefined ? {} : { mode: modeParam(opts.mode) }),
|
|
2279
|
-
...(opts.parents === undefined ? {} : { parents: opts.parents }),
|
|
2280
|
-
},
|
|
2175
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/pause`,
|
|
2176
|
+
body: {},
|
|
2177
|
+
timeoutMs: 10 * 60_000,
|
|
2281
2178
|
}),
|
|
2282
|
-
|
|
2179
|
+
resume: (sandboxId, opts = {}) => this.request({
|
|
2283
2180
|
method: "POST",
|
|
2284
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/
|
|
2285
|
-
body: {
|
|
2181
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/resume`,
|
|
2182
|
+
body: {
|
|
2183
|
+
...(opts.optimizeFor === undefined ? {} : { optimize_for: opts.optimizeFor }),
|
|
2184
|
+
...(opts.spendCap === undefined ? {} : { spend_cap: checkSpendCap(opts.spendCap) }),
|
|
2185
|
+
},
|
|
2286
2186
|
}),
|
|
2287
|
-
|
|
2187
|
+
snapshot: (sandboxId, opts = {}) => this.request({
|
|
2288
2188
|
method: "POST",
|
|
2289
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/
|
|
2189
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/snapshot`,
|
|
2290
2190
|
body: {
|
|
2291
|
-
|
|
2292
|
-
...(opts.
|
|
2293
|
-
...(opts.
|
|
2191
|
+
...(opts.alias === undefined ? {} : { alias: opts.alias }),
|
|
2192
|
+
...(opts.force === undefined ? {} : { force: opts.force }),
|
|
2193
|
+
...(opts.moveAlias === undefined ? {} : { move_alias: opts.moveAlias }),
|
|
2294
2194
|
},
|
|
2195
|
+
timeoutMs: 10 * 60_000,
|
|
2196
|
+
}),
|
|
2197
|
+
list: async (opts = 50) => (await this.sandboxes.listPage(opts)).items,
|
|
2198
|
+
listPage: async (opts = 50) => {
|
|
2199
|
+
const o = typeof opts === "number" ? { limit: opts } : opts;
|
|
2200
|
+
const q = new URLSearchParams({ limit: String(Math.max(1, Math.trunc(o.limit ?? 50))) });
|
|
2201
|
+
const pairs = Object.entries(o.labels ?? {});
|
|
2202
|
+
if (pairs.length)
|
|
2203
|
+
q.set("labels", pairs.map(([k, v]) => `${k}=${v}`).join(","));
|
|
2204
|
+
if (o.state?.length)
|
|
2205
|
+
q.set("state", o.state.join(","));
|
|
2206
|
+
if (o.cursor)
|
|
2207
|
+
q.set("cursor", o.cursor);
|
|
2208
|
+
const r = await this.request({
|
|
2209
|
+
method: "GET",
|
|
2210
|
+
path: `/sandboxes?${q.toString()}`,
|
|
2211
|
+
});
|
|
2212
|
+
return { items: r.sandboxes ?? [], nextCursor: r.next_cursor ?? null, limit: r.limit ?? (o.limit ?? 50) };
|
|
2213
|
+
},
|
|
2214
|
+
probe: (sandboxId, opts = {}) => this.request({
|
|
2215
|
+
method: "POST",
|
|
2216
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/probe`,
|
|
2217
|
+
body: opts.paths?.length ? { paths: opts.paths } : {},
|
|
2218
|
+
}),
|
|
2219
|
+
kill: (sandboxId) => this.request({ method: "DELETE", path: `/sandboxes/${encodeURIComponent(sandboxId)}` }),
|
|
2220
|
+
network: (sandboxId) => this.request({ method: "GET", path: `/sandboxes/${encodeURIComponent(sandboxId)}/network` }),
|
|
2221
|
+
setNetworkNone: (sandboxId) => this.request({
|
|
2222
|
+
method: "POST",
|
|
2223
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/network`,
|
|
2224
|
+
body: { network: "none" },
|
|
2295
2225
|
}),
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2226
|
+
patch: (sandboxId, opts) => {
|
|
2227
|
+
const body = {};
|
|
2228
|
+
if (opts.timeoutMs !== undefined) {
|
|
2229
|
+
if (!Number.isInteger(opts.timeoutMs) || opts.timeoutMs <= 0) {
|
|
2230
|
+
throw new core_js_1.InvalidArgumentError("sandboxes.patch needs a positive timeoutMs", "Pass { timeoutMs: 1_800_000 }.");
|
|
2231
|
+
}
|
|
2232
|
+
body["timeout_ms"] = opts.timeoutMs;
|
|
2233
|
+
}
|
|
2234
|
+
if (opts.autoPauseS !== undefined) {
|
|
2235
|
+
if (!Number.isInteger(opts.autoPauseS) || opts.autoPauseS < 0) {
|
|
2236
|
+
throw new core_js_1.InvalidArgumentError("sandboxes.patch needs a non-negative whole number of seconds for autoPauseS", "Pass { autoPauseS: 900 } to pause it after 15 idle minutes, or { autoPauseS: 0 } to turn that off.");
|
|
2237
|
+
}
|
|
2238
|
+
body["auto_pause_s"] = opts.autoPauseS;
|
|
2239
|
+
}
|
|
2240
|
+
if (opts.autoPauseMode !== undefined) {
|
|
2241
|
+
if (opts.autoPauseMode !== "requests" && opts.autoPauseMode !== "activity") {
|
|
2242
|
+
throw new core_js_1.InvalidArgumentError(`sandboxes.patch: autoPauseMode must be "requests" or "activity", got ${JSON.stringify(opts.autoPauseMode)}`, 'Pass { autoPauseMode: "activity" } for an agent that runs inside the sandbox.');
|
|
2243
|
+
}
|
|
2244
|
+
body["auto_pause_mode"] = opts.autoPauseMode;
|
|
2245
|
+
}
|
|
2246
|
+
if (Object.keys(body).length === 0) {
|
|
2247
|
+
throw new core_js_1.InvalidArgumentError("sandboxes.patch was given nothing to change", "Pass { timeoutMs } to extend it, { autoPauseS } to set its idle rule, or both.");
|
|
2299
2248
|
}
|
|
2300
2249
|
return this.request({
|
|
2250
|
+
method: "PATCH",
|
|
2251
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}`,
|
|
2252
|
+
body,
|
|
2253
|
+
});
|
|
2254
|
+
},
|
|
2255
|
+
files: {
|
|
2256
|
+
list: (sandboxId, path = "/", opts = {}) => {
|
|
2257
|
+
const q = [`path=${encodeURIComponent(path)}`];
|
|
2258
|
+
if (opts.recursive)
|
|
2259
|
+
q.push("recursive=1");
|
|
2260
|
+
if (opts.depth !== undefined)
|
|
2261
|
+
q.push(`depth=${Math.max(0, Math.floor(opts.depth))}`);
|
|
2262
|
+
if (opts.limit !== undefined)
|
|
2263
|
+
q.push(`limit=${Math.max(1, Math.floor(opts.limit))}`);
|
|
2264
|
+
if (opts.hidden === false)
|
|
2265
|
+
q.push("hidden=0");
|
|
2266
|
+
return this.request({
|
|
2267
|
+
method: "GET",
|
|
2268
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/list?${q.join("&")}`,
|
|
2269
|
+
});
|
|
2270
|
+
},
|
|
2271
|
+
search: (sandboxId, path, opts = {}) => {
|
|
2272
|
+
if (!opts.pattern && !opts.contains) {
|
|
2273
|
+
throw new core_js_1.InvalidArgumentError("files.search needs `pattern`, `contains`, or both", "Pass { pattern: '*.py' } for a name glob, { contains: 'TODO' } for text inside files.");
|
|
2274
|
+
}
|
|
2275
|
+
const q = [`path=${encodeURIComponent(path)}`];
|
|
2276
|
+
if (opts.pattern)
|
|
2277
|
+
q.push(`pattern=${encodeURIComponent(opts.pattern)}`);
|
|
2278
|
+
if (opts.contains)
|
|
2279
|
+
q.push(`contains=${encodeURIComponent(opts.contains)}`);
|
|
2280
|
+
if (opts.ignoreCase)
|
|
2281
|
+
q.push("ignore_case=1");
|
|
2282
|
+
if (opts.limit !== undefined)
|
|
2283
|
+
q.push(`limit=${Math.max(1, Math.floor(opts.limit))}`);
|
|
2284
|
+
if (opts.hidden === false)
|
|
2285
|
+
q.push("hidden=0");
|
|
2286
|
+
return this.request({
|
|
2287
|
+
method: "GET",
|
|
2288
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/search?${q.join("&")}`,
|
|
2289
|
+
});
|
|
2290
|
+
},
|
|
2291
|
+
mkdir: (sandboxId, path, opts = {}) => this.request({
|
|
2301
2292
|
method: "POST",
|
|
2302
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/
|
|
2293
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/mkdir`,
|
|
2303
2294
|
body: {
|
|
2304
2295
|
path,
|
|
2305
2296
|
...(opts.mode === undefined ? {} : { mode: modeParam(opts.mode) }),
|
|
2306
|
-
...(opts.
|
|
2307
|
-
|
|
2297
|
+
...(opts.parents === undefined ? {} : { parents: opts.parents }),
|
|
2298
|
+
},
|
|
2299
|
+
}),
|
|
2300
|
+
move: (sandboxId, from, to, opts = {}) => this.request({
|
|
2301
|
+
method: "POST",
|
|
2302
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/move`,
|
|
2303
|
+
body: { from, to, ...(opts.overwrite === undefined ? {} : { overwrite: opts.overwrite }) },
|
|
2304
|
+
}),
|
|
2305
|
+
copy: (sandboxId, from, to, opts = {}) => this.request({
|
|
2306
|
+
method: "POST",
|
|
2307
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/copy`,
|
|
2308
|
+
body: {
|
|
2309
|
+
from, to,
|
|
2308
2310
|
...(opts.recursive === undefined ? {} : { recursive: opts.recursive }),
|
|
2311
|
+
...(opts.overwrite === undefined ? {} : { overwrite: opts.overwrite }),
|
|
2309
2312
|
},
|
|
2310
|
-
})
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/archive`,
|
|
2315
|
-
body: {
|
|
2316
|
-
path,
|
|
2317
|
-
...(opts.out === undefined ? {} : { out: opts.out }),
|
|
2318
|
-
...(opts.exclude?.length ? { exclude: opts.exclude } : {}),
|
|
2319
|
-
},
|
|
2320
|
-
}),
|
|
2321
|
-
extract: (sandboxId, archive, into, opts = {}) => this.request({
|
|
2322
|
-
method: "POST",
|
|
2323
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/extract`,
|
|
2324
|
-
body: { archive, into, ...(opts.strip === undefined ? {} : { strip: Math.max(0, Math.floor(opts.strip)) }) },
|
|
2325
|
-
}),
|
|
2326
|
-
stat: (sandboxId, path) => this.request({
|
|
2327
|
-
method: "GET",
|
|
2328
|
-
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/stat?path=${encodeURIComponent(path)}`,
|
|
2329
|
-
}),
|
|
2330
|
-
get: (sandboxId, path, opts = {}) => this.bytes("GET", `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`, undefined, opts),
|
|
2331
|
-
download: (sandboxId, path, onChunk, opts = {}) => this.stream(`/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`, onChunk, opts),
|
|
2332
|
-
text: async (sandboxId, path, opts = {}) => new TextDecoder().decode(await this.sandboxes.files.get(sandboxId, path, opts)),
|
|
2333
|
-
put: async (sandboxId, path, data, opts = {}) => {
|
|
2334
|
-
const body = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
2335
|
-
return this.sandboxes.files.putFrom(sandboxId, path, {
|
|
2336
|
-
size: body.length,
|
|
2337
|
-
read: (offset, len) => body.subarray(offset, offset + len),
|
|
2338
|
-
}, opts);
|
|
2339
|
-
},
|
|
2340
|
-
putFrom: async (sandboxId, path, source, opts = {}) => {
|
|
2341
|
-
const mode = opts.mode === undefined ? "" : `&mode=${opts.mode.toString(8)}`;
|
|
2342
|
-
const base = `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}${mode}`;
|
|
2343
|
-
let sent = 0;
|
|
2344
|
-
let chunks = 0;
|
|
2345
|
-
let last;
|
|
2346
|
-
do {
|
|
2347
|
-
const slice = await source.read(sent, Math.min(core_js_1.FILE_PUT_CHUNK_BYTES, source.size - sent));
|
|
2348
|
-
if (slice.length === 0 && sent < source.size) {
|
|
2349
|
-
throw new core_js_1.InvalidArgumentError(`the source of ${path} ended at ${sent} of ${source.size} bytes`, "The file shrank while it was being sent; send it again once nothing is writing it.");
|
|
2313
|
+
}),
|
|
2314
|
+
chmod: (sandboxId, path, opts) => {
|
|
2315
|
+
if (opts.mode === undefined && opts.uid === undefined && opts.gid === undefined) {
|
|
2316
|
+
throw new core_js_1.InvalidArgumentError("files.chmod needs one of mode, uid or gid", "Pass { mode: 0o755 }, { uid: 1000 }, { gid: 1000 }, or any combination.");
|
|
2350
2317
|
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2318
|
+
return this.request({
|
|
2319
|
+
method: "POST",
|
|
2320
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/chmod`,
|
|
2321
|
+
body: {
|
|
2322
|
+
path,
|
|
2323
|
+
...(opts.mode === undefined ? {} : { mode: modeParam(opts.mode) }),
|
|
2324
|
+
...(opts.uid === undefined ? {} : { uid: opts.uid }),
|
|
2325
|
+
...(opts.gid === undefined ? {} : { gid: opts.gid }),
|
|
2326
|
+
...(opts.recursive === undefined ? {} : { recursive: opts.recursive }),
|
|
2327
|
+
},
|
|
2328
|
+
});
|
|
2329
|
+
},
|
|
2330
|
+
archive: (sandboxId, path, opts = {}) => this.request({
|
|
2331
|
+
method: "POST",
|
|
2332
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/archive`,
|
|
2333
|
+
body: {
|
|
2334
|
+
path,
|
|
2335
|
+
...(opts.out === undefined ? {} : { out: opts.out }),
|
|
2336
|
+
...(opts.exclude?.length ? { exclude: opts.exclude } : {}),
|
|
2337
|
+
},
|
|
2338
|
+
}),
|
|
2339
|
+
extract: (sandboxId, archive, into, opts = {}) => this.request({
|
|
2340
|
+
method: "POST",
|
|
2341
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/extract`,
|
|
2342
|
+
body: { archive, into, ...(opts.strip === undefined ? {} : { strip: Math.max(0, Math.floor(opts.strip)) }) },
|
|
2343
|
+
}),
|
|
2344
|
+
stat: (sandboxId, path) => this.request({
|
|
2345
|
+
method: "GET",
|
|
2346
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/stat?path=${encodeURIComponent(path)}`,
|
|
2347
|
+
}),
|
|
2348
|
+
get: (sandboxId, path, opts = {}) => this.bytes("GET", `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`, undefined, opts),
|
|
2349
|
+
download: (sandboxId, path, onChunk, opts = {}) => this.stream(`/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`, onChunk, opts),
|
|
2350
|
+
text: async (sandboxId, path, opts = {}) => new TextDecoder().decode(await this.sandboxes.files.get(sandboxId, path, opts)),
|
|
2351
|
+
put: async (sandboxId, path, data, opts = {}) => {
|
|
2352
|
+
const body = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
2353
|
+
return this.sandboxes.files.putFrom(sandboxId, path, {
|
|
2354
|
+
size: body.length,
|
|
2355
|
+
read: (offset, len) => body.subarray(offset, offset + len),
|
|
2356
|
+
}, opts);
|
|
2357
|
+
},
|
|
2358
|
+
putFrom: async (sandboxId, path, source, opts = {}) => {
|
|
2359
|
+
const mode = opts.mode === undefined ? "" : `&mode=${opts.mode.toString(8)}`;
|
|
2360
|
+
const base = `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}${mode}`;
|
|
2361
|
+
let sent = 0;
|
|
2362
|
+
let chunks = 0;
|
|
2363
|
+
let last;
|
|
2364
|
+
do {
|
|
2365
|
+
const slice = await source.read(sent, Math.min(core_js_1.FILE_PUT_CHUNK_BYTES, source.size - sent));
|
|
2366
|
+
if (slice.length === 0 && sent < source.size) {
|
|
2367
|
+
throw new core_js_1.InvalidArgumentError(`the source of ${path} ended at ${sent} of ${source.size} bytes`, "The file shrank while it was being sent; send it again once nothing is writing it.");
|
|
2368
|
+
}
|
|
2369
|
+
const raw = await this.bytes("PUT", sent === 0 ? base : `${base}&offset=${sent}`, slice, opts);
|
|
2370
|
+
last = JSON.parse(new TextDecoder().decode(raw));
|
|
2371
|
+
sent += slice.length;
|
|
2372
|
+
chunks += 1;
|
|
2373
|
+
} while (sent < source.size);
|
|
2374
|
+
return { ...last, bytes: source.size, chunks };
|
|
2375
|
+
},
|
|
2376
|
+
delete: (sandboxId, path, opts = {}) => this.request({
|
|
2377
|
+
method: "DELETE",
|
|
2378
|
+
path: `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`
|
|
2379
|
+
+ (opts.recursive ? "&recursive=1" : ""),
|
|
2380
|
+
}),
|
|
2357
2381
|
},
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
runId ??= rec.run_id;
|
|
2375
|
-
return hit;
|
|
2376
|
-
};
|
|
2377
|
-
let found;
|
|
2378
|
-
let sandboxId = opts.sandboxId;
|
|
2379
|
-
let routeMissing = false;
|
|
2380
|
-
try {
|
|
2381
|
-
found = await this.request({ method: "GET", path: `/execs/${encodeURIComponent(execId)}` });
|
|
2382
|
-
}
|
|
2383
|
-
catch (err) {
|
|
2384
|
-
if (!(err instanceof core_js_1.NotFoundError))
|
|
2385
|
-
throw err;
|
|
2386
|
-
found = undefined;
|
|
2387
|
-
routeMissing = true;
|
|
2388
|
-
}
|
|
2389
|
-
if (found && opts.runId && found.run_id && found.run_id !== opts.runId)
|
|
2390
|
-
found = undefined;
|
|
2391
|
-
let otherSandbox = false;
|
|
2392
|
-
if (found && opts.sandboxId && found.sandbox_id && found.sandbox_id !== opts.sandboxId) {
|
|
2393
|
-
found = undefined;
|
|
2394
|
-
otherSandbox = true;
|
|
2395
|
-
}
|
|
2396
|
-
if (found)
|
|
2397
|
-
runId ??= found.run_id;
|
|
2398
|
-
if (!found && (routeMissing || otherSandbox) && opts.sandboxId) {
|
|
2382
|
+
output: async (execId, opts = {}) => {
|
|
2383
|
+
if (!execId)
|
|
2384
|
+
throw new core_js_1.InvalidArgumentError("exec output needs an exec_id", "Pass the `exec_id` the exec envelope printed.");
|
|
2385
|
+
let runId = opts.runId;
|
|
2386
|
+
const fromSandbox = async (sid) => {
|
|
2387
|
+
const rec = await this.request({
|
|
2388
|
+
method: "GET", path: `/sandboxes/${encodeURIComponent(sid)}`,
|
|
2389
|
+
});
|
|
2390
|
+
const hit = (rec?.execs ?? []).find((e) => e.exec_id === execId);
|
|
2391
|
+
if (hit && rec?.run_id)
|
|
2392
|
+
runId ??= rec.run_id;
|
|
2393
|
+
return hit;
|
|
2394
|
+
};
|
|
2395
|
+
let found;
|
|
2396
|
+
let sandboxId = opts.sandboxId;
|
|
2397
|
+
let routeMissing = false;
|
|
2399
2398
|
try {
|
|
2400
|
-
found = await
|
|
2399
|
+
found = await this.request({ method: "GET", path: `/execs/${encodeURIComponent(execId)}` });
|
|
2401
2400
|
}
|
|
2402
2401
|
catch (err) {
|
|
2403
2402
|
if (!(err instanceof core_js_1.NotFoundError))
|
|
2404
2403
|
throw err;
|
|
2405
2404
|
found = undefined;
|
|
2405
|
+
routeMissing = true;
|
|
2406
2406
|
}
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2407
|
+
if (found && opts.runId && found.run_id && found.run_id !== opts.runId)
|
|
2408
|
+
found = undefined;
|
|
2409
|
+
let otherSandbox = false;
|
|
2410
|
+
if (found && opts.sandboxId && found.sandbox_id && found.sandbox_id !== opts.sandboxId) {
|
|
2411
|
+
found = undefined;
|
|
2412
|
+
otherSandbox = true;
|
|
2413
|
+
}
|
|
2414
|
+
if (found)
|
|
2415
|
+
runId ??= found.run_id;
|
|
2416
|
+
if (!found && (routeMissing || otherSandbox) && opts.sandboxId) {
|
|
2417
|
+
try {
|
|
2418
|
+
found = await fromSandbox(opts.sandboxId);
|
|
2419
|
+
}
|
|
2420
|
+
catch (err) {
|
|
2421
|
+
if (!(err instanceof core_js_1.NotFoundError))
|
|
2422
|
+
throw err;
|
|
2423
|
+
found = undefined;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
sandboxId ??= found?.sandbox_id;
|
|
2427
|
+
if (!found) {
|
|
2428
|
+
throw new core_js_1.NotFoundError(`exec ${execId} not found${sandboxId ? ` in sandbox ${sandboxId}` : opts.runId ? ` in run ${opts.runId}` : ""}`, "Check the `exec_id` from the exec envelope, and that the sandbox it ran in still exists (`promigence run report <run_id>`).");
|
|
2429
|
+
}
|
|
2430
|
+
return sliceOutput(found, sandboxId, runId, opts.range);
|
|
2431
|
+
},
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2415
2434
|
sandbox(id) {
|
|
2416
2435
|
return {
|
|
2417
2436
|
id,
|
|
@@ -2431,143 +2450,175 @@ class PromigenceClient {
|
|
|
2431
2450
|
probe: (opts = {}) => this.sandboxes.probe(id, opts),
|
|
2432
2451
|
};
|
|
2433
2452
|
}
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
path: `/snapshots/builds/${encodeURIComponent(buildId)}/build-log?tail=${Math.max(1, Math.floor(tail))}`,
|
|
2493
|
-
});
|
|
2494
|
-
return r?.log ?? [];
|
|
2495
|
-
},
|
|
2496
|
-
builds: async (limit = 20) => (await this.snapshots.buildsPage({ limit })).items,
|
|
2497
|
-
buildsPage: async (opts = {}) => {
|
|
2498
|
-
const limit = opts.limit ?? 20;
|
|
2499
|
-
const r = await this.request({ method: "GET", path: `/snapshots/builds?${pageQuery(limit, 500, opts.cursor)}` });
|
|
2500
|
-
return pageOf(r, "builds", limit);
|
|
2501
|
-
},
|
|
2502
|
-
get: (idOrAlias) => this.request({ method: "GET", path: `/snapshots/${seg(idOrAlias)}` }),
|
|
2503
|
-
list: async (opts = {}) => (await this.snapshots.listPage(opts)).items,
|
|
2504
|
-
listPage: async (opts = {}) => {
|
|
2505
|
-
const q = new URLSearchParams();
|
|
2506
|
-
if (opts.limit !== undefined)
|
|
2507
|
-
q.set("limit", String(Math.max(1, Math.trunc(opts.limit))));
|
|
2508
|
-
if (opts.cursor)
|
|
2509
|
-
q.set("cursor", opts.cursor);
|
|
2510
|
-
const qs = q.toString();
|
|
2511
|
-
const r = await this.request({
|
|
2512
|
-
method: "GET", path: `/snapshots${qs ? `?${qs}` : ""}`, ...(this.authenticated ? {} : { anonymous: true }),
|
|
2513
|
-
});
|
|
2514
|
-
if (Array.isArray(r))
|
|
2515
|
-
return { items: r, nextCursor: null, limit: opts.limit ?? r.length };
|
|
2516
|
-
return { items: r?.snapshots ?? [], nextCursor: r?.next_cursor ?? null, limit: r?.limit ?? (opts.limit ?? 0) };
|
|
2517
|
-
},
|
|
2518
|
-
public: async (limit = 20) => {
|
|
2519
|
-
const r = await this.request({
|
|
2453
|
+
group_secrets() {
|
|
2454
|
+
return {
|
|
2455
|
+
set: (name, value, opts = {}) => {
|
|
2456
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name ?? "")) {
|
|
2457
|
+
throw new core_js_1.InvalidArgumentError(`secrets.set: ${JSON.stringify(name ?? null)} is not usable as an environment variable`, "Use letters, digits and underscores, not starting with a digit: OPENAI_API_KEY.");
|
|
2458
|
+
}
|
|
2459
|
+
if (!value) {
|
|
2460
|
+
throw new core_js_1.InvalidArgumentError(`secrets.set: ${name} has an empty value`, `To remove it, call secrets.delete(${JSON.stringify(name)}).`);
|
|
2461
|
+
}
|
|
2462
|
+
const body = { name, value };
|
|
2463
|
+
if (opts.hosts !== undefined)
|
|
2464
|
+
body.hosts = opts.hosts;
|
|
2465
|
+
if (opts.headers !== undefined)
|
|
2466
|
+
body.headers = opts.headers;
|
|
2467
|
+
if (opts.raw !== undefined)
|
|
2468
|
+
body.raw = opts.raw;
|
|
2469
|
+
if (opts.scanResponses !== undefined)
|
|
2470
|
+
body.scan_responses = opts.scanResponses;
|
|
2471
|
+
return this.request({ method: "POST", path: "/secrets", body });
|
|
2472
|
+
},
|
|
2473
|
+
list: () => this.request({ method: "GET", path: "/secrets" }),
|
|
2474
|
+
delete: (name) => this.request({
|
|
2475
|
+
method: "DELETE",
|
|
2476
|
+
path: `/secrets/${encodeURIComponent(name)}`,
|
|
2477
|
+
}),
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
group_images() {
|
|
2481
|
+
return {
|
|
2482
|
+
resolve: (image, opts = {}) => {
|
|
2483
|
+
if (!image?.trim()) {
|
|
2484
|
+
throw new core_js_1.InvalidArgumentError("images.resolve needs an image reference", "Pass a name like 'python:3.12' or 'ghcr.io/org/app:v1'.");
|
|
2485
|
+
}
|
|
2486
|
+
const body = { image: image.trim() };
|
|
2487
|
+
if (opts.registrySecret)
|
|
2488
|
+
body["registry_secret"] = opts.registrySecret;
|
|
2489
|
+
return this.request({ method: "POST", path: "/images/resolve", body });
|
|
2490
|
+
},
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
group_captures() {
|
|
2494
|
+
return {
|
|
2495
|
+
delete: (snapshotId) => this.request({
|
|
2496
|
+
method: "DELETE",
|
|
2497
|
+
path: `/captures/${encodeURIComponent(snapshotId)}`,
|
|
2498
|
+
}),
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
group_snapshots() {
|
|
2502
|
+
return {
|
|
2503
|
+
create: (opts) => {
|
|
2504
|
+
const body = snapshotCreateBody(opts);
|
|
2505
|
+
return this.request({
|
|
2506
|
+
method: "POST", path: "/snapshots", body,
|
|
2507
|
+
...(opts.idempotencyKey ? { idempotencyKey: opts.idempotencyKey } : {}),
|
|
2508
|
+
});
|
|
2509
|
+
},
|
|
2510
|
+
status: (buildId, opts = {}) => this.request({
|
|
2520
2511
|
method: "GET",
|
|
2521
|
-
path: `/snapshots
|
|
2522
|
-
|
|
2523
|
-
})
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2512
|
+
path: `/snapshots/builds/${encodeURIComponent(buildId)}${waitQuery(opts.waitMs)}`,
|
|
2513
|
+
...this.readExtras(opts),
|
|
2514
|
+
}),
|
|
2515
|
+
buildLog: async (buildId, tail = 200) => {
|
|
2516
|
+
const r = await this.request({
|
|
2517
|
+
method: "GET",
|
|
2518
|
+
path: `/snapshots/builds/${encodeURIComponent(buildId)}/build-log?tail=${Math.max(1, Math.floor(tail))}`,
|
|
2519
|
+
});
|
|
2520
|
+
return r?.log ?? [];
|
|
2521
|
+
},
|
|
2522
|
+
builds: async (limit = 20) => (await this.snapshots.buildsPage({ limit })).items,
|
|
2523
|
+
buildsPage: async (opts = {}) => {
|
|
2524
|
+
const limit = opts.limit ?? 20;
|
|
2525
|
+
const r = await this.request({ method: "GET", path: `/snapshots/builds?${pageQuery(limit, 500, opts.cursor)}` });
|
|
2526
|
+
return pageOf(r, "builds", limit);
|
|
2527
|
+
},
|
|
2528
|
+
get: (idOrAlias) => this.request({ method: "GET", path: `/snapshots/${seg(idOrAlias)}` }),
|
|
2529
|
+
list: async (opts = {}) => (await this.snapshots.listPage(opts)).items,
|
|
2530
|
+
listPage: async (opts = {}) => {
|
|
2531
|
+
const q = new URLSearchParams();
|
|
2532
|
+
if (opts.limit !== undefined)
|
|
2533
|
+
q.set("limit", String(Math.max(1, Math.trunc(opts.limit))));
|
|
2534
|
+
if (opts.cursor)
|
|
2535
|
+
q.set("cursor", opts.cursor);
|
|
2536
|
+
const qs = q.toString();
|
|
2537
|
+
const r = await this.request({
|
|
2538
|
+
method: "GET", path: `/snapshots${qs ? `?${qs}` : ""}`, ...(this.authenticated ? {} : { anonymous: true }),
|
|
2539
|
+
});
|
|
2540
|
+
if (Array.isArray(r))
|
|
2541
|
+
return { items: r, nextCursor: null, limit: opts.limit ?? r.length };
|
|
2542
|
+
return { items: r?.snapshots ?? [], nextCursor: r?.next_cursor ?? null, limit: r?.limit ?? (opts.limit ?? 0) };
|
|
2543
|
+
},
|
|
2544
|
+
public: async (limit = 20) => {
|
|
2545
|
+
const r = await this.request({
|
|
2546
|
+
method: "GET",
|
|
2547
|
+
path: `/snapshots?limit=${Math.max(1, Math.floor(limit))}`,
|
|
2548
|
+
anonymous: true,
|
|
2549
|
+
});
|
|
2550
|
+
const all = Array.isArray(r) ? r : (r?.snapshots ?? []);
|
|
2551
|
+
return all.filter((s) => s.visibility === undefined || s.visibility === "public").slice(0, limit);
|
|
2552
|
+
},
|
|
2553
|
+
delete: (idOrAlias) => {
|
|
2554
|
+
if (!idOrAlias)
|
|
2555
|
+
throw new core_js_1.InvalidArgumentError("snapshots.delete needs a snapshot", "Pass a snap_ id, an alias, or alias@commit7.");
|
|
2556
|
+
return this.request({ method: "DELETE", path: `/snapshots/${seg(idOrAlias)}` });
|
|
2557
|
+
},
|
|
2558
|
+
alias: (idOrAlias, alias) => this.request({
|
|
2559
|
+
method: "PUT",
|
|
2560
|
+
path: `/snapshots/${seg(idOrAlias)}/alias`,
|
|
2561
|
+
body: { alias },
|
|
2562
|
+
}),
|
|
2563
|
+
waitBuild: async (buildId, opts = {}) => {
|
|
2564
|
+
const r = await this.waitLoop(opts, BUILD_POLL_MS, { build_id: buildId, state: "running" }, (read) => this.snapshots.status(buildId, read), (b) => b.state !== "running" && b.state !== "queued");
|
|
2565
|
+
return { build: r.value, timedOut: r.timedOut, waitedMs: r.waitedMs };
|
|
2566
|
+
},
|
|
2567
|
+
verify: (id, opts = {}) => {
|
|
2568
|
+
if (!id)
|
|
2569
|
+
throw new core_js_1.InvalidArgumentError("snapshots.verify needs a snapshot", "Pass a snap_ id, an alias, or alias@commit7.");
|
|
2570
|
+
const body = {};
|
|
2571
|
+
if (opts.timeoutMs !== undefined)
|
|
2572
|
+
body["timeout_ms"] = opts.timeoutMs;
|
|
2573
|
+
if (opts.spendCap !== undefined)
|
|
2574
|
+
body["spend_cap"] = opts.spendCap;
|
|
2575
|
+
return this.request({
|
|
2576
|
+
method: "POST",
|
|
2577
|
+
path: `/snapshots/${encodeURIComponent(id)}/verify`,
|
|
2578
|
+
body,
|
|
2579
|
+
});
|
|
2580
|
+
},
|
|
2581
|
+
manifest: (idOrAlias) => this.request({ method: "GET", path: `/snapshots/${seg(idOrAlias)}/manifest` }),
|
|
2582
|
+
bundle: async (idOrAlias, opts = {}) => {
|
|
2583
|
+
const [record, m] = await Promise.all([
|
|
2584
|
+
this.snapshots.get(idOrAlias).catch(() => undefined),
|
|
2585
|
+
this.snapshots.manifest(idOrAlias),
|
|
2586
|
+
]);
|
|
2587
|
+
if (!m?.manifest || typeof m.manifest !== "object") {
|
|
2588
|
+
throw new core_js_1.NotFoundError(`snapshot ${idOrAlias} has no manifest to export`, "Only a verified snapshot has one: check 'promigence snapshot inspect' first.");
|
|
2589
|
+
}
|
|
2590
|
+
const alias = opts.alias ?? record?.alias ?? undefined;
|
|
2591
|
+
const bundle = (0, bundle_js_1.buildBundle)(m.manifest, { ...(alias ? { alias } : {}) });
|
|
2592
|
+
return { bundle, served_by: "client", ...(record ? { record } : {}) };
|
|
2593
|
+
},
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2569
2596
|
}
|
|
2570
2597
|
exports.PromigenceClient = PromigenceClient;
|
|
2598
|
+
const API_GROUPS = ["templates", "billing", "keys", "orgs", "credits", "github", "gitlab", "runs", "sandboxes", "secrets", "images", "captures", "snapshots"];
|
|
2599
|
+
function installLazyGroups(proto) {
|
|
2600
|
+
for (const name of API_GROUPS) {
|
|
2601
|
+
const build = `group_${name}`;
|
|
2602
|
+
const own = (self, value) => {
|
|
2603
|
+
Object.defineProperty(self, name, { value, writable: true, enumerable: true, configurable: true });
|
|
2604
|
+
};
|
|
2605
|
+
Object.defineProperty(proto, name, {
|
|
2606
|
+
configurable: true,
|
|
2607
|
+
enumerable: false,
|
|
2608
|
+
get() {
|
|
2609
|
+
const value = this[build].call(this);
|
|
2610
|
+
own(this, value);
|
|
2611
|
+
return value;
|
|
2612
|
+
},
|
|
2613
|
+
set(value) {
|
|
2614
|
+
own(this, value);
|
|
2615
|
+
},
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
installLazyGroups(PromigenceClient.prototype);
|
|
2620
|
+
const lazyGroupTypesMatch = [true, true, true, true, true, true, true, true, true, true, true, true, true];
|
|
2621
|
+
void lazyGroupTypesMatch;
|
|
2571
2622
|
const POLL_LADDER_MS = [10, 25, 25, 25, 25, 25, 25, 25, 25, 50, 50, 50, 100, 100, 100, 200];
|
|
2572
2623
|
function pollInterval(iteration) {
|
|
2573
2624
|
const step = POLL_LADDER_MS[iteration];
|