@promigence/sdk 0.1.0-beta.15 → 0.1.0-beta.16
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 +1 -1
- package/dist/cjs/caller.js +1 -1
- package/dist/cjs/client.d.ts +50 -9
- package/dist/cjs/client.js +77 -47
- package/dist/cjs/command-errors.d.ts +1 -0
- package/dist/cjs/command-errors.js +2 -0
- package/dist/cjs/context.d.ts +19 -4
- package/dist/cjs/context.js +77 -5
- package/dist/cjs/core.d.ts +32 -4
- package/dist/cjs/core.js +28 -5
- package/dist/cjs/http2.d.ts +8 -0
- package/dist/cjs/http2.js +22 -1
- package/dist/cjs/models.gen.d.ts +64 -7
- package/dist/cjs/sandbox.d.ts +26 -8
- package/dist/cjs/sandbox.js +28 -4
- package/dist/cjs/transport-worker.js +10 -1
- package/dist/client.d.ts +50 -9
- package/dist/client.js +76 -44
- package/dist/command-errors.d.ts +1 -0
- package/dist/command-errors.js +3 -1
- package/dist/context.d.ts +19 -4
- package/dist/context.js +74 -4
- package/dist/core.d.ts +32 -4
- package/dist/core.js +24 -4
- package/dist/http2.d.ts +8 -0
- package/dist/http2.js +22 -1
- package/dist/models.gen.d.ts +64 -7
- package/dist/sandbox.d.ts +26 -8
- package/dist/sandbox.js +28 -4
- package/dist/transport-worker.js +10 -1
- package/package.json +1 -1
package/dist/cjs/core.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/** Promigence SDK — constants, error hierarchy and shared types. */
|
|
2
|
-
export declare const VERSION = "0.1.0-beta.
|
|
2
|
+
export declare const VERSION = "0.1.0-beta.16";
|
|
3
3
|
export declare const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
/** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
|
|
5
5
|
export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
6
|
+
/** The row of `code` in the error reference. */
|
|
7
|
+
export declare function errorsDocsFor(code: string): string;
|
|
6
8
|
export declare const DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
|
7
9
|
/** Where to write about a key. A key is self-serve: `promigence login` signs up or in and stores one. */
|
|
8
10
|
export declare const KEY_CONTACT = "support@promigence.ai";
|
|
@@ -43,8 +45,8 @@ export interface PromigenceErrorInit {
|
|
|
43
45
|
/** The API error's `retryable`: nothing ran and the same request, sent again as is, can succeed. */
|
|
44
46
|
retryable?: boolean;
|
|
45
47
|
}
|
|
46
|
-
/** What a caller reads instead: a fact about
|
|
47
|
-
export declare const PREVIEWS_UNAVAILABLE = "preview URLs are not
|
|
48
|
+
/** What a caller reads instead: a fact about their account, not a mistake of theirs. */
|
|
49
|
+
export declare const PREVIEWS_UNAVAILABLE = "preview URLs are not turned on for this account";
|
|
48
50
|
export declare function previewsFix(sandboxId?: string, port?: string | number): string;
|
|
49
51
|
/**
|
|
50
52
|
* A `fix` that opens by repeating the message says only what it adds: the API's `usage` fix is
|
|
@@ -97,7 +99,13 @@ export declare class PromigenceError extends Error {
|
|
|
97
99
|
guide: string;
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Bad arguments (e.g. an unknown template, a missing spend cap). exit 2
|
|
104
|
+
*
|
|
105
|
+
* `code` says who refused: `invalid_argument` is the SDK, before anything was sent; `usage` is the
|
|
106
|
+
* API, refusing what it was sent. Nothing ran and nothing was billed either way, so catch this
|
|
107
|
+
* class (or exit 2) to handle both.
|
|
108
|
+
*/
|
|
101
109
|
export declare class InvalidArgumentError extends PromigenceError {
|
|
102
110
|
/**
|
|
103
111
|
* `code` defaults to `invalid_argument` — what the SDK raises for a bad argument it catches
|
|
@@ -122,6 +130,26 @@ export declare function previewsOff(message: string): boolean;
|
|
|
122
130
|
export declare class PreviewsUnavailableError extends InvalidArgumentError {
|
|
123
131
|
constructor(sandboxId?: string, port?: string | number);
|
|
124
132
|
}
|
|
133
|
+
/** The sandbox and its state when an API message is the "has ended" refusal, else `undefined`. */
|
|
134
|
+
export declare function sandboxEndedIn(message: string): {
|
|
135
|
+
sandboxId: string;
|
|
136
|
+
state: string;
|
|
137
|
+
} | undefined;
|
|
138
|
+
/** What a caller of this SDK does about an ended sandbox. */
|
|
139
|
+
export declare const SANDBOX_ENDED_FIX = "Start a new sandbox: Sandbox.create(). This one's getInfo() says how it ended (`outcome`).";
|
|
140
|
+
/**
|
|
141
|
+
* A call to a sandbox that has ended (`state` `ended` or `failed`): it does not come back. Still an
|
|
142
|
+
* `InvalidArgumentError` with code `usage` and exit 2, as the API answers it, so a catch written
|
|
143
|
+
* against earlier releases still catches it; catch this class to tell an ended sandbox from a bad
|
|
144
|
+
* argument. `sandboxId` and `state` name the sandbox and how it stands.
|
|
145
|
+
*/
|
|
146
|
+
export declare class SandboxEndedError extends InvalidArgumentError {
|
|
147
|
+
/** The sandbox that has ended. */
|
|
148
|
+
readonly sandboxId: string;
|
|
149
|
+
/** Its state: `ended`, or `failed` for one that ended on a failure. */
|
|
150
|
+
readonly state: string;
|
|
151
|
+
constructor(message: string, sandboxId: string, state: string, fix?: string);
|
|
152
|
+
}
|
|
125
153
|
/** The environment failed (our fault, never the task's). exit 3 */
|
|
126
154
|
export declare class EnvFailedError extends PromigenceError {
|
|
127
155
|
constructor(code: string, message: string, fix: string);
|
package/dist/cjs/core.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EXIT_BY_CODE = exports.NotReleasedError = exports.ApiError = exports.ServiceRestartError = exports.TimeoutError = exports.RateLimitError = exports.UnreachableError = exports.NotFoundError = exports.AuthError = exports.NotVerifiedError = exports.SpendCapError = exports.CommandExitError = exports.EnvFailedError = exports.PreviewsUnavailableError = exports.UnsupportedError = exports.InvalidArgumentError = exports.PromigenceError = exports.PREVIEWS_UNAVAILABLE = exports.FILE_PUT_CHUNK_BYTES = exports.MAX_OUTPUT_BYTES_DEFAULT = exports.IMAGE_BUILD_LIST_PRICE = exports.IMAGE_BUILD_TIMEOUT_MS = exports.WAIT_TIMEOUT_MS = exports.KEY_CONTACT = exports.DEFAULT_API_URL = exports.ERRORS_DOCS_URL = exports.DOCS_URL = exports.VERSION = void 0;
|
|
3
|
+
exports.EXIT_BY_CODE = exports.NotReleasedError = exports.ApiError = exports.ServiceRestartError = exports.TimeoutError = exports.RateLimitError = exports.UnreachableError = exports.NotFoundError = exports.AuthError = exports.NotVerifiedError = exports.SpendCapError = exports.CommandExitError = exports.EnvFailedError = exports.SandboxEndedError = exports.SANDBOX_ENDED_FIX = exports.PreviewsUnavailableError = exports.UnsupportedError = exports.InvalidArgumentError = exports.PromigenceError = exports.PREVIEWS_UNAVAILABLE = exports.FILE_PUT_CHUNK_BYTES = exports.MAX_OUTPUT_BYTES_DEFAULT = exports.IMAGE_BUILD_LIST_PRICE = exports.IMAGE_BUILD_TIMEOUT_MS = exports.WAIT_TIMEOUT_MS = exports.KEY_CONTACT = exports.DEFAULT_API_URL = exports.ERRORS_DOCS_URL = exports.DOCS_URL = exports.VERSION = void 0;
|
|
4
|
+
exports.errorsDocsFor = errorsDocsFor;
|
|
4
5
|
exports.previewsFix = previewsFix;
|
|
5
6
|
exports.withPreviewsFix = withPreviewsFix;
|
|
6
7
|
exports.fixBeyondMessage = fixBeyondMessage;
|
|
7
8
|
exports.previewsOff = previewsOff;
|
|
9
|
+
exports.sandboxEndedIn = sandboxEndedIn;
|
|
8
10
|
exports.exitForCode = exitForCode;
|
|
9
11
|
const hint_js_1 = require("./hint.js");
|
|
10
|
-
exports.VERSION = "0.1.0-beta.
|
|
12
|
+
exports.VERSION = "0.1.0-beta.16";
|
|
11
13
|
exports.DOCS_URL = "https://promigence.ai";
|
|
12
14
|
exports.ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
15
|
+
function errorsDocsFor(code) {
|
|
16
|
+
return `${exports.ERRORS_DOCS_URL}#${code}`;
|
|
17
|
+
}
|
|
13
18
|
exports.DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
|
14
19
|
exports.KEY_CONTACT = "support@promigence.ai";
|
|
15
20
|
exports.WAIT_TIMEOUT_MS = 100_000;
|
|
@@ -18,10 +23,11 @@ exports.IMAGE_BUILD_LIST_PRICE = 0.1656;
|
|
|
18
23
|
exports.MAX_OUTPUT_BYTES_DEFAULT = 64 * 1024;
|
|
19
24
|
exports.FILE_PUT_CHUNK_BYTES = 16 * 1024 * 1024;
|
|
20
25
|
const PREVIEWS_OFF = /^previews are not enabled on this deployment\.?$/i;
|
|
21
|
-
exports.PREVIEWS_UNAVAILABLE = "preview URLs are not
|
|
26
|
+
exports.PREVIEWS_UNAVAILABLE = "preview URLs are not turned on for this account";
|
|
22
27
|
function previewsFix(sandboxId = "SID", port = 8000) {
|
|
23
28
|
return `Reach the port over SSH instead: \`promigence sandbox ssh ${(0, hint_js_1.hintArg)(sandboxId)} -- -N -L ${(0, hint_js_1.hintArg)(`${port}:localhost:${port}`)}\` ` +
|
|
24
|
-
`forwards http://localhost:${port} here to port ${port} in the sandbox until Ctrl-C
|
|
29
|
+
`forwards http://localhost:${port} here to port ${port} in the sandbox until Ctrl-C. ` +
|
|
30
|
+
"Write to support@promigence.ai to have preview URLs turned on.";
|
|
25
31
|
}
|
|
26
32
|
async function withPreviewsFix(call, sandboxId, port = "PORT") {
|
|
27
33
|
try {
|
|
@@ -60,7 +66,7 @@ class PromigenceError extends Error {
|
|
|
60
66
|
this.name = new.target.name;
|
|
61
67
|
this.code = code;
|
|
62
68
|
this.fix = previewsOff ? previewsFix() : fixBeyondMessage(init.message, init.fix);
|
|
63
|
-
this.docs = init.docs ??
|
|
69
|
+
this.docs = init.docs ?? errorsDocsFor(code);
|
|
64
70
|
this.exitCode = init.exitCode ?? 1;
|
|
65
71
|
this.cliExit = init.exitCode ?? 1;
|
|
66
72
|
this.guide = `promigence agent-guide --section errors --code ${(0, hint_js_1.hintArg)(code)}`;
|
|
@@ -111,6 +117,22 @@ class PreviewsUnavailableError extends InvalidArgumentError {
|
|
|
111
117
|
}
|
|
112
118
|
}
|
|
113
119
|
exports.PreviewsUnavailableError = PreviewsUnavailableError;
|
|
120
|
+
const SANDBOX_ENDED = /^sandbox (\S+) has ended \(`([a-z_]+)`\)/;
|
|
121
|
+
function sandboxEndedIn(message) {
|
|
122
|
+
const m = SANDBOX_ENDED.exec(message.trim());
|
|
123
|
+
return m ? { sandboxId: m[1], state: m[2] } : undefined;
|
|
124
|
+
}
|
|
125
|
+
exports.SANDBOX_ENDED_FIX = "Start a new sandbox: Sandbox.create(). This one's getInfo() says how it ended (`outcome`).";
|
|
126
|
+
class SandboxEndedError extends InvalidArgumentError {
|
|
127
|
+
sandboxId;
|
|
128
|
+
state;
|
|
129
|
+
constructor(message, sandboxId, state, fix = exports.SANDBOX_ENDED_FIX) {
|
|
130
|
+
super(message, fix, "usage");
|
|
131
|
+
this.sandboxId = sandboxId;
|
|
132
|
+
this.state = state;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.SandboxEndedError = SandboxEndedError;
|
|
114
136
|
class EnvFailedError extends PromigenceError {
|
|
115
137
|
constructor(code, message, fix) {
|
|
116
138
|
super({ code, message, fix, exitCode: 3 });
|
|
@@ -240,6 +262,7 @@ exports.EXIT_BY_CODE = {
|
|
|
240
262
|
env_failed: 3, snapshot_rejected: 6, build_failed: 3, snapshot_failed: 4,
|
|
241
263
|
not_connected: 7,
|
|
242
264
|
service_restart: 3,
|
|
265
|
+
digest_mismatch: 4,
|
|
243
266
|
fork_failed: 3, start_timeout: 3, readiness_failed: 3, verify_too_slow: 3,
|
|
244
267
|
verify_each_failed: 3, toolchain_mismatch: 3, sandbox_unresponsive: 3, oom_within_tier: 3,
|
|
245
268
|
disk_full_within_tier: 3, sandbox_interrupted: 3, integrity_mismatch: 3, egress_unavailable: 3,
|
package/dist/cjs/http2.d.ts
CHANGED
|
@@ -197,10 +197,18 @@ export declare const __http2Internals: {
|
|
|
197
197
|
gapUs: number;
|
|
198
198
|
pace: string;
|
|
199
199
|
};
|
|
200
|
+
/** The offset that turns the thread's stamps into trace times (`clockOffset`), read from the two clocks given. */
|
|
201
|
+
clockOffset(hr: () => number, now: () => number): number;
|
|
200
202
|
/** Tests: make the transport thread stop answering (it reads nothing more), as a stuck one would. */
|
|
201
203
|
wedgeThreadForTest(): void;
|
|
202
204
|
/** Tests: make the transport thread fail as a crash would. */
|
|
203
205
|
crashThreadForTest(): void;
|
|
206
|
+
/**
|
|
207
|
+
* Tests: the transport thread gives no spaced request its turn until `releaseTurnsForTest`. Both go as messages, in order
|
|
208
|
+
* with the requests and their cancels, so what is sent between them reaches the thread while every spaced request waits.
|
|
209
|
+
*/
|
|
210
|
+
holdTurnsForTest(): void;
|
|
211
|
+
releaseTurnsForTest(): void;
|
|
204
212
|
/** The early lookup, for tests: start one with a platform, DNS servers and hosts file of the test's own. */
|
|
205
213
|
early: {
|
|
206
214
|
start(host: string): boolean;
|
package/dist/cjs/http2.js
CHANGED
|
@@ -1351,6 +1351,12 @@ const THREAD_GRACE_MS = 1_000;
|
|
|
1351
1351
|
const THREAD_SWEEP_MS = 250;
|
|
1352
1352
|
const THREAD_MARKS = 4096;
|
|
1353
1353
|
const THREAD_PHASES = ["w.got", "w.sent", "w.head", "w.end"];
|
|
1354
|
+
function clockOffset(hr = () => Number(process.hrtime.bigint()) / 1e6, now = () => performance.now(), n = 5) {
|
|
1355
|
+
let best = -Infinity;
|
|
1356
|
+
for (let i = 0; i < n; i++)
|
|
1357
|
+
best = Math.max(best, hr() - now());
|
|
1358
|
+
return best;
|
|
1359
|
+
}
|
|
1354
1360
|
class TransportThread {
|
|
1355
1361
|
origin;
|
|
1356
1362
|
worker;
|
|
@@ -1376,7 +1382,7 @@ class TransportThread {
|
|
|
1376
1382
|
posts = 0;
|
|
1377
1383
|
answered = 0;
|
|
1378
1384
|
pace = THREAD_DEFAULTS.pace;
|
|
1379
|
-
hrOffset =
|
|
1385
|
+
hrOffset = clockOffset();
|
|
1380
1386
|
constructor(origin, worker, port, acks) {
|
|
1381
1387
|
this.origin = origin;
|
|
1382
1388
|
this.worker = worker;
|
|
@@ -2066,6 +2072,9 @@ exports.__http2Internals = {
|
|
|
2066
2072
|
threadKnobs(env) {
|
|
2067
2073
|
return threadKnobs(env);
|
|
2068
2074
|
},
|
|
2075
|
+
clockOffset(hr, now) {
|
|
2076
|
+
return clockOffset(hr, now);
|
|
2077
|
+
},
|
|
2069
2078
|
wedgeThreadForTest() {
|
|
2070
2079
|
try {
|
|
2071
2080
|
transportThread?.port.postMessage({ t: "wedge-for-test" });
|
|
@@ -2078,6 +2087,18 @@ exports.__http2Internals = {
|
|
|
2078
2087
|
}
|
|
2079
2088
|
catch { }
|
|
2080
2089
|
},
|
|
2090
|
+
holdTurnsForTest() {
|
|
2091
|
+
try {
|
|
2092
|
+
transportThread?.port.postMessage({ t: "hold-turns-for-test" });
|
|
2093
|
+
}
|
|
2094
|
+
catch { }
|
|
2095
|
+
},
|
|
2096
|
+
releaseTurnsForTest() {
|
|
2097
|
+
try {
|
|
2098
|
+
transportThread?.port.postMessage({ t: "release-turns-for-test" });
|
|
2099
|
+
}
|
|
2100
|
+
catch { }
|
|
2101
|
+
},
|
|
2081
2102
|
early: {
|
|
2082
2103
|
start(host) {
|
|
2083
2104
|
return earlyLookupFor(host.toLowerCase()) !== undefined;
|
package/dist/cjs/models.gen.d.ts
CHANGED
|
@@ -739,6 +739,11 @@ export interface BuildQueued {
|
|
|
739
739
|
* True when a verification command was given.
|
|
740
740
|
*/
|
|
741
741
|
verified: boolean;
|
|
742
|
+
/**
|
|
743
|
+
* What to know before the build ends: with `move_alias`, which snapshot gives up the alias
|
|
744
|
+
* to it once it is stored.
|
|
745
|
+
*/
|
|
746
|
+
warnings?: Array<string>;
|
|
742
747
|
}
|
|
743
748
|
/**
|
|
744
749
|
* Builds in the period.
|
|
@@ -3074,7 +3079,7 @@ export interface NameCreate {
|
|
|
3074
3079
|
*/
|
|
3075
3080
|
name: string;
|
|
3076
3081
|
}
|
|
3077
|
-
export type Network = "none" | "egress";
|
|
3082
|
+
export type Network = "none" | "egress" | "allow";
|
|
3078
3083
|
/**
|
|
3079
3084
|
* An allowlist: the only hosts the sandbox may reach.
|
|
3080
3085
|
*/
|
|
@@ -3528,6 +3533,11 @@ export interface Port {
|
|
|
3528
3533
|
* Unix nanoseconds.
|
|
3529
3534
|
*/
|
|
3530
3535
|
created_unix_ns?: number | null;
|
|
3536
|
+
/**
|
|
3537
|
+
* When the URL stops working on its own, Unix nanoseconds; `null` when it works until it is
|
|
3538
|
+
* revoked or the sandbox ends.
|
|
3539
|
+
*/
|
|
3540
|
+
expires_unix_ns?: number | null;
|
|
3531
3541
|
/**
|
|
3532
3542
|
* The same publication addressed by name rather than by path. Apps that emit absolute paths
|
|
3533
3543
|
* need this form.
|
|
@@ -3539,8 +3549,8 @@ export interface Port {
|
|
|
3539
3549
|
port?: number | null;
|
|
3540
3550
|
sandbox_id: string;
|
|
3541
3551
|
/**
|
|
3542
|
-
* The token the URL carries. Pass it
|
|
3543
|
-
* revoke this one URL.
|
|
3552
|
+
* The token the URL carries. Pass it in the body of `POST /v1/sandboxes/{id}/ports/revoke`
|
|
3553
|
+
* (`{"token": …}`) to revoke this one URL.
|
|
3544
3554
|
*/
|
|
3545
3555
|
token?: string | null;
|
|
3546
3556
|
/**
|
|
@@ -3563,6 +3573,25 @@ export interface PortOpen {
|
|
|
3563
3573
|
* 1–65535.
|
|
3564
3574
|
*/
|
|
3565
3575
|
port: number;
|
|
3576
|
+
/**
|
|
3577
|
+
* Seconds until the URL stops working on its own, 1 to 2592000 (30 days). Absent: it works
|
|
3578
|
+
* until it is revoked or the sandbox ends.
|
|
3579
|
+
*/
|
|
3580
|
+
ttl_s?: number | null;
|
|
3581
|
+
}
|
|
3582
|
+
/**
|
|
3583
|
+
* `POST /v1/sandboxes/{id}/ports/revoke`: revoke one URL by its token, or every URL of a port,
|
|
3584
|
+
* with what it names in the body. The token never travels in a URL.
|
|
3585
|
+
*/
|
|
3586
|
+
export interface PortRevoke {
|
|
3587
|
+
/**
|
|
3588
|
+
* A port: every URL minted for it is revoked. Exactly one of `token` and `port`.
|
|
3589
|
+
*/
|
|
3590
|
+
port?: number | null;
|
|
3591
|
+
/**
|
|
3592
|
+
* The token of the one URL to revoke.
|
|
3593
|
+
*/
|
|
3594
|
+
token?: string | null;
|
|
3566
3595
|
}
|
|
3567
3596
|
/**
|
|
3568
3597
|
* `POST /v1/billing/portal` — a link to the billing portal.
|
|
@@ -3571,7 +3600,7 @@ export interface PortalSession {
|
|
|
3571
3600
|
url: string;
|
|
3572
3601
|
}
|
|
3573
3602
|
/**
|
|
3574
|
-
* `DELETE /v1/sandboxes/{id}/ports/{token_or_port}`.
|
|
3603
|
+
* `POST /v1/sandboxes/{id}/ports/revoke` and `DELETE /v1/sandboxes/{id}/ports/{token_or_port}`.
|
|
3575
3604
|
*/
|
|
3576
3605
|
export interface PortsRevoked {
|
|
3577
3606
|
ok: boolean;
|
|
@@ -4158,6 +4187,11 @@ export interface ReplayStats {
|
|
|
4158
4187
|
success_pct: number;
|
|
4159
4188
|
}
|
|
4160
4189
|
export interface Requested {
|
|
4190
|
+
/**
|
|
4191
|
+
* With `network: "allow"`: the only destinations the run's sandboxes may reach, as the run
|
|
4192
|
+
* named them. Absent for the other modes.
|
|
4193
|
+
*/
|
|
4194
|
+
allow?: Array<string>;
|
|
4161
4195
|
allow_partial: boolean;
|
|
4162
4196
|
count: number;
|
|
4163
4197
|
/**
|
|
@@ -4736,6 +4770,12 @@ export interface Sandbox {
|
|
|
4736
4770
|
*/
|
|
4737
4771
|
holdout_passed?: boolean | null;
|
|
4738
4772
|
holdout_score?: number | null;
|
|
4773
|
+
/**
|
|
4774
|
+
* `PROMIGENCE_INDEX`: this sandbox's place in its run, from 0. A retry, a replay or a
|
|
4775
|
+
* reopened failure keeps its original's. Absent when the run's command set the variable
|
|
4776
|
+
* itself.
|
|
4777
|
+
*/
|
|
4778
|
+
index?: number | null;
|
|
4739
4779
|
/**
|
|
4740
4780
|
* True when this is a one-time wait: once the waiting sandboxes can start, the rest of the
|
|
4741
4781
|
* run starts at once too. False otherwise.
|
|
@@ -4763,6 +4803,12 @@ export interface Sandbox {
|
|
|
4763
4803
|
retry_of?: string | null;
|
|
4764
4804
|
sandbox_id: string;
|
|
4765
4805
|
score?: number | null;
|
|
4806
|
+
/**
|
|
4807
|
+
* `PROMIGENCE_SEED`: the number this sandbox's commands were given in that variable. A
|
|
4808
|
+
* retry, a replay or a reopened failure keeps its original's. Absent when the run's command
|
|
4809
|
+
* set the variable itself.
|
|
4810
|
+
*/
|
|
4811
|
+
seed?: number | null;
|
|
4766
4812
|
state: SandboxState;
|
|
4767
4813
|
t_end?: string | null;
|
|
4768
4814
|
t_handoff?: string | null;
|
|
@@ -5692,6 +5738,11 @@ export interface SessionTrial {
|
|
|
5692
5738
|
*/
|
|
5693
5739
|
export interface SnapshotAliasSet {
|
|
5694
5740
|
alias: string | null;
|
|
5741
|
+
/**
|
|
5742
|
+
* Take the name from the snapshot of the project that has it, which stays, unnamed. Without
|
|
5743
|
+
* it, a name another snapshot has is refused.
|
|
5744
|
+
*/
|
|
5745
|
+
move_alias?: boolean;
|
|
5695
5746
|
}
|
|
5696
5747
|
/**
|
|
5697
5748
|
* What an image occupies in storage: `allocated`, which can be less than its size.
|
|
@@ -5745,6 +5796,11 @@ export interface SnapshotCreate {
|
|
|
5745
5796
|
* A container image, `REF@sha256:<digest>`; a tag is resolved to its digest first.
|
|
5746
5797
|
*/
|
|
5747
5798
|
image?: string | null;
|
|
5799
|
+
/**
|
|
5800
|
+
* With `alias`: take the name from the snapshot of the project that has it, which stays,
|
|
5801
|
+
* unnamed. Without it, an `alias` another snapshot has is refused and nothing is built.
|
|
5802
|
+
*/
|
|
5803
|
+
move_alias?: boolean;
|
|
5748
5804
|
/**
|
|
5749
5805
|
* `github:` repositories: a branch, a tag or a commit sha, resolved to the commit it names
|
|
5750
5806
|
* when the build is accepted. Default: the repository's default branch.
|
|
@@ -5815,9 +5871,10 @@ export interface SnapshotDeduped {
|
|
|
5815
5871
|
alias_applied?: boolean | null;
|
|
5816
5872
|
/**
|
|
5817
5873
|
* The alias that was asked for, when the snapshot did not hold it. The snapshot takes it
|
|
5818
|
-
* (`alias_applied: true`) as a new build would: another snapshot of the project
|
|
5819
|
-
*
|
|
5820
|
-
*
|
|
5874
|
+
* (`alias_applied: true`) as a new build would: a name another snapshot of the project has
|
|
5875
|
+
* is taken only with `move_alias`, and that snapshot keeps its row, without a name. A
|
|
5876
|
+
* snapshot has one alias, so a name it had before gives way. `warnings` says which names
|
|
5877
|
+
* moved.
|
|
5821
5878
|
*/
|
|
5822
5879
|
alias_requested?: string | null;
|
|
5823
5880
|
/**
|
package/dist/cjs/sandbox.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ import { type CommandResult, type CommandRunOpts, type ForkOpts, type OptimizeFo
|
|
|
14
14
|
import { PromigenceClient, type BackgroundExecRecord, type DirEntry, type DirListOpts, type ExtractResult, type FileInfo, type FileMatch, type Page, type PortRecord, type AutoPauseMode, type SandboxRecord, type SearchOpts, type SecretBinding, type SnapshotBuild, type VerifySpec } from "./client.js";
|
|
15
15
|
import { CommandHandle } from "./handles.js";
|
|
16
16
|
import type { PtyExit, PtySession, PtySize } from "./pty.js";
|
|
17
|
+
/** `setTimeout`'s fix when the longer timeout does not fit under the run's spend cap. */
|
|
18
|
+
export declare const EXTEND_OVER_CAP_FIX: string;
|
|
17
19
|
/**
|
|
18
20
|
* `commands.run` options for a foreground command. The call resolves with the whole result when
|
|
19
21
|
* the command ends; `onStdout` / `onStderr` also receive its output as it is written.
|
|
@@ -150,6 +152,8 @@ export interface SandboxCreateOpts extends SandboxOpts {
|
|
|
150
152
|
start?: string | string[];
|
|
151
153
|
tier?: Tier;
|
|
152
154
|
alias?: string;
|
|
155
|
+
/** With `alias`: take the name from the snapshot that has it (refused without this). */
|
|
156
|
+
moveAlias?: boolean;
|
|
153
157
|
/** Name of a stored secret holding the registry credentials. */
|
|
154
158
|
registrySecret?: string;
|
|
155
159
|
/** Bound on the first build of an image (default 15 min). Expiry cancels nothing. */
|
|
@@ -196,7 +200,7 @@ export interface SandboxCreateOpts extends SandboxOpts {
|
|
|
196
200
|
* inputs) was built before, or 202 with a `build_id`. The **body**, not the status, decides: an
|
|
197
201
|
* idempotency replay answers 202 with a document that may already be `verified`.
|
|
198
202
|
*/
|
|
199
|
-
export declare function imageSnapshot(client: PromigenceClient, opts: Pick<SandboxCreateOpts, "image" | "setup" | "verify" | "start" | "tier" | "alias" | "registrySecret" | "buildTimeoutMs">, hooks?: {
|
|
203
|
+
export declare function imageSnapshot(client: PromigenceClient, opts: Pick<SandboxCreateOpts, "image" | "setup" | "verify" | "start" | "tier" | "alias" | "moveAlias" | "registrySecret" | "buildTimeoutMs">, hooks?: {
|
|
200
204
|
/**
|
|
201
205
|
* The `POST /snapshots` answer, before any wait: `build_price` (the most it can cost) and
|
|
202
206
|
* whether it was `deduped` or `joined` (nothing new charged). An implicit build costs money, so a caller that prints prices prints this one too.
|
|
@@ -312,7 +316,14 @@ export declare class Sandbox {
|
|
|
312
316
|
resize(pid: number, size: PtySize): Promise<void>;
|
|
313
317
|
kill(pid: number): Promise<boolean>;
|
|
314
318
|
};
|
|
315
|
-
/**
|
|
319
|
+
/**
|
|
320
|
+
* Preview URLs for ports inside the sandbox (`getHost` gives the address alone).
|
|
321
|
+
*
|
|
322
|
+
* Anyone holding a preview URL can open it; revoke it any time, and it ends with the sandbox.
|
|
323
|
+
* If your account's preview URLs aren't on, `expose` throws `PreviewsUnavailableError`; write to
|
|
324
|
+
* support@promigence.ai to have them turned on, and meanwhile reach the port with
|
|
325
|
+
* `promigence sandbox ssh <sid> -- -N -L <port>:localhost:<port>`.
|
|
326
|
+
*/
|
|
316
327
|
readonly ports: {
|
|
317
328
|
expose(port: number): Promise<PortRecord>;
|
|
318
329
|
list(): Promise<PortRecord[]>;
|
|
@@ -458,13 +469,16 @@ export declare class Sandbox {
|
|
|
458
469
|
}>;
|
|
459
470
|
isRunning(): Promise<boolean>;
|
|
460
471
|
/**
|
|
461
|
-
* `PATCH /sandboxes/:id` —
|
|
472
|
+
* `PATCH /sandboxes/:id` — set this sandbox's timeout, in milliseconds.
|
|
462
473
|
*
|
|
463
|
-
* The timeout is the sandbox's total lifetime,
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
474
|
+
* The timeout is the sandbox's total lifetime, counted from its start, not from now:
|
|
475
|
+
* `setTimeout(600_000)` on a sandbox that has run 2 minutes leaves it 8 more.
|
|
476
|
+
*
|
|
477
|
+
* - Raising it commits the added seconds, which has to fit in what is left of the spend cap you set
|
|
478
|
+
* (the run's, or the org default): past it, it throws `SpendCapError` (409) rather than silently
|
|
479
|
+
* extending.
|
|
480
|
+
* - Lowering it goes down to what the sandbox has run plus 60 s, and no further
|
|
481
|
+
* (`InvalidArgumentError` below that). To stop it now, `kill()` it.
|
|
468
482
|
*/
|
|
469
483
|
setTimeout(ms: number): Promise<void>;
|
|
470
484
|
/**
|
|
@@ -500,6 +514,10 @@ export declare class Sandbox {
|
|
|
500
514
|
*
|
|
501
515
|
* Async, because the address is a capability minted on demand rather than
|
|
502
516
|
* a hostname derived from the id. An existing URL for the port is reused, not re-minted.
|
|
517
|
+
*
|
|
518
|
+
* If your account's preview URLs aren't on, this throws `PreviewsUnavailableError`; write to
|
|
519
|
+
* support@promigence.ai to have them turned on, and meanwhile reach the port with
|
|
520
|
+
* `promigence sandbox ssh <sid> -- -N -L <port>:localhost:<port>`.
|
|
503
521
|
*/
|
|
504
522
|
getHost(port: number): Promise<string>;
|
|
505
523
|
/**
|
package/dist/cjs/sandbox.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Sandbox = exports.DEFAULT_SNAPSHOT = exports.DEFAULT_PTY_CMD = void 0;
|
|
3
|
+
exports.Sandbox = exports.DEFAULT_SNAPSHOT = exports.DEFAULT_PTY_CMD = exports.EXTEND_OVER_CAP_FIX = void 0;
|
|
4
4
|
exports.shellQuote = shellQuote;
|
|
5
5
|
exports.ptyCommand = ptyCommand;
|
|
6
6
|
exports.toEntryInfo = toEntryInfo;
|
|
@@ -14,6 +14,8 @@ const client_js_1 = require("./client.js");
|
|
|
14
14
|
const handles_js_1 = require("./handles.js");
|
|
15
15
|
const command_errors_js_1 = require("./command-errors.js");
|
|
16
16
|
const hint_js_1 = require("./hint.js");
|
|
17
|
+
exports.EXTEND_OVER_CAP_FIX = "Raising the timeout must fit in what is left of the run's spend cap, and this does not: start a new " +
|
|
18
|
+
"sandbox with the longer `timeoutMs` and a `spendCap` that covers it.";
|
|
17
19
|
const EXEC_TIMEOUT_DEFAULT_MS = 15 * 60_000;
|
|
18
20
|
function textSink(cb) {
|
|
19
21
|
if (!cb)
|
|
@@ -47,6 +49,15 @@ function ptyCommand(opts) {
|
|
|
47
49
|
parts.push(opts.cmd ?? exports.DEFAULT_PTY_CMD);
|
|
48
50
|
return parts.join(" ");
|
|
49
51
|
}
|
|
52
|
+
function allowList(hosts) {
|
|
53
|
+
if (!Array.isArray(hosts)) {
|
|
54
|
+
throw new core_js_1.InvalidArgumentError("allowHosts must be a list of hosts", 'Pass allowHosts: ["github.com"].');
|
|
55
|
+
}
|
|
56
|
+
if (!hosts.some((h) => typeof h !== "string" || h.trim() !== "")) {
|
|
57
|
+
throw new core_js_1.InvalidArgumentError("allowHosts names no host, so the allow-list is empty", 'Name each host the sandbox may reach: allowHosts: ["github.com"]. For no network pass allowInternetAccess: false; to reach anything leave allowHosts out.');
|
|
58
|
+
}
|
|
59
|
+
return hosts;
|
|
60
|
+
}
|
|
50
61
|
function permString(mode) {
|
|
51
62
|
const r = (m) => `${m & 4 ? "r" : "-"}${m & 2 ? "w" : "-"}${m & 1 ? "x" : "-"}`;
|
|
52
63
|
return r((mode >> 6) & 7) + r((mode >> 3) & 7) + r(mode & 7);
|
|
@@ -99,6 +110,8 @@ async function imageSnapshot(client, opts, hooks = {}) {
|
|
|
99
110
|
create.tier = opts.tier;
|
|
100
111
|
if (opts.alias !== undefined)
|
|
101
112
|
create.alias = opts.alias;
|
|
113
|
+
if (opts.moveAlias)
|
|
114
|
+
create.moveAlias = true;
|
|
102
115
|
if (opts.registrySecret !== undefined)
|
|
103
116
|
create.registrySecret = opts.registrySecret;
|
|
104
117
|
let build = (await client.snapshots.create(create)) ?? {};
|
|
@@ -372,6 +385,9 @@ class Sandbox {
|
|
|
372
385
|
return format === "bytes" ? bytes : new TextDecoder().decode(bytes);
|
|
373
386
|
}),
|
|
374
387
|
write: async (path, data, opts = {}) => {
|
|
388
|
+
if (typeof path !== "string") {
|
|
389
|
+
throw new core_js_1.InvalidArgumentError(`files.write takes one path and its data; got ${Array.isArray(path) ? "an array" : path === null ? "null" : typeof path} as the path`, "Write each file with its own call: for (const f of files) await sandbox.files.write(f.path, f.data).");
|
|
390
|
+
}
|
|
375
391
|
let body;
|
|
376
392
|
if (typeof data === "string" || data instanceof Uint8Array)
|
|
377
393
|
body = data;
|
|
@@ -566,8 +582,8 @@ class Sandbox {
|
|
|
566
582
|
start.timeoutMs = opts.timeoutMs;
|
|
567
583
|
if (opts.allowInternetAccess === false)
|
|
568
584
|
start.network = "none";
|
|
569
|
-
if (opts.allowHosts
|
|
570
|
-
start.network = { allow: opts.allowHosts };
|
|
585
|
+
if (opts.allowHosts !== undefined && opts.allowHosts !== null)
|
|
586
|
+
start.network = { allow: allowList(opts.allowHosts) };
|
|
571
587
|
if (opts.metadata)
|
|
572
588
|
start.labels = opts.metadata;
|
|
573
589
|
if (opts.secrets?.length)
|
|
@@ -705,7 +721,15 @@ class Sandbox {
|
|
|
705
721
|
return rec.state === "ready" || rec.state === "running";
|
|
706
722
|
}
|
|
707
723
|
async setTimeout(ms) {
|
|
708
|
-
|
|
724
|
+
try {
|
|
725
|
+
await this.#client.sandboxes.patch(this.sandboxId, { timeoutMs: ms });
|
|
726
|
+
}
|
|
727
|
+
catch (e) {
|
|
728
|
+
if (e instanceof core_js_1.SpendCapError && e.code === "cap_exceeded") {
|
|
729
|
+
Object.defineProperty(e, "fix", { value: exports.EXTEND_OVER_CAP_FIX, enumerable: true, configurable: true });
|
|
730
|
+
}
|
|
731
|
+
throw e;
|
|
732
|
+
}
|
|
709
733
|
this.timeoutMs = ms;
|
|
710
734
|
}
|
|
711
735
|
async setAutoPause(seconds, mode) {
|
|
@@ -292,6 +292,7 @@ function start(init) {
|
|
|
292
292
|
let rrAny = 0;
|
|
293
293
|
let pumpTimer;
|
|
294
294
|
let pumpSoon;
|
|
295
|
+
let turnsHeld = false;
|
|
295
296
|
const freeNow = (l, now) => {
|
|
296
297
|
for (let i = 0; i < l.length; i++) {
|
|
297
298
|
const k = (rr + i) % l.length;
|
|
@@ -306,6 +307,8 @@ function start(init) {
|
|
|
306
307
|
const pump = () => {
|
|
307
308
|
pumpTimer = undefined;
|
|
308
309
|
pumpSoon = undefined;
|
|
310
|
+
if (turnsHeld)
|
|
311
|
+
return;
|
|
309
312
|
while (waiting.length > 0) {
|
|
310
313
|
const l = live();
|
|
311
314
|
if (l.length === 0) {
|
|
@@ -348,7 +351,7 @@ function start(init) {
|
|
|
348
351
|
rrAny = (rrAny % l.length) + 1;
|
|
349
352
|
return e;
|
|
350
353
|
}
|
|
351
|
-
if (waiting.length === 0) {
|
|
354
|
+
if (waiting.length === 0 && !turnsHeld) {
|
|
352
355
|
const now = hrNow();
|
|
353
356
|
const e = freeNow(l, now);
|
|
354
357
|
if (e !== undefined) {
|
|
@@ -529,6 +532,12 @@ function start(init) {
|
|
|
529
532
|
}
|
|
530
533
|
else if (m?.t === "crash-for-test")
|
|
531
534
|
setImmediate(() => { throw new Error("transport thread crash (test)"); });
|
|
535
|
+
else if (m?.t === "hold-turns-for-test")
|
|
536
|
+
turnsHeld = true;
|
|
537
|
+
else if (m?.t === "release-turns-for-test") {
|
|
538
|
+
turnsHeld = false;
|
|
539
|
+
pump();
|
|
540
|
+
}
|
|
532
541
|
});
|
|
533
542
|
void open();
|
|
534
543
|
}
|