@promigence/sdk 0.1.0-beta.15 → 0.1.0-beta.17
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 +173 -19
- package/dist/cjs/client.js +451 -129
- package/dist/cjs/command-errors.d.ts +1 -0
- package/dist/cjs/command-errors.js +2 -0
- package/dist/cjs/context.d.ts +20 -4
- package/dist/cjs/context.js +82 -5
- package/dist/cjs/core.d.ts +56 -5
- package/dist/cjs/core.js +49 -5
- package/dist/cjs/handles.js +2 -1
- package/dist/cjs/http2.d.ts +8 -0
- package/dist/cjs/http2.js +27 -1
- package/dist/cjs/models.gen.d.ts +269 -30
- package/dist/cjs/proxy.js +32 -3
- package/dist/cjs/sandbox.d.ts +38 -13
- package/dist/cjs/sandbox.js +29 -5
- package/dist/cjs/transport-worker.js +10 -1
- package/dist/client.d.ts +173 -19
- package/dist/client.js +450 -126
- package/dist/command-errors.d.ts +1 -0
- package/dist/command-errors.js +3 -1
- package/dist/context.d.ts +20 -4
- package/dist/context.js +79 -4
- package/dist/core.d.ts +56 -5
- package/dist/core.js +44 -4
- package/dist/handles.js +2 -1
- package/dist/http2.d.ts +8 -0
- package/dist/http2.js +27 -1
- package/dist/models.gen.d.ts +269 -30
- package/dist/proxy.js +33 -4
- package/dist/sandbox.d.ts +38 -13
- package/dist/sandbox.js +29 -5
- package/dist/transport-worker.js +10 -1
- package/package.json +1 -1
package/dist/sandbox.d.ts
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { inspect, type InspectOptions } from "node:util";
|
|
13
13
|
import { type CommandResult, type CommandRunOpts, type ForkOpts, type OptimizeFor, type SandboxOpts, type Tier } from "./core.js";
|
|
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";
|
|
14
|
+
import { PromigenceClient, type BackgroundExecRecord, type DirEntry, type DirListOpts, type ExtractResult, type FileInfo, type FileMatch, type Page, type PortExposeOpts, 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.
|
|
@@ -285,6 +289,7 @@ export declare class Sandbox {
|
|
|
285
289
|
/**
|
|
286
290
|
* Run `cmd` and resolve with its result. A non-zero exit throws `CommandExitError`; a command
|
|
287
291
|
* killed at its own `timeoutMs` throws `CommandTimeoutError` (a `CommandExitError` too).
|
|
292
|
+
* With `throwOnExit: false` both resolve instead, and the result's `timedOut` tells them apart.
|
|
288
293
|
*/
|
|
289
294
|
run(cmd: string, opts?: ForegroundRunOpts): Promise<CommandResult>;
|
|
290
295
|
/** Background commands in this sandbox (running and ended, until killed with forget). */
|
|
@@ -312,9 +317,20 @@ export declare class Sandbox {
|
|
|
312
317
|
resize(pid: number, size: PtySize): Promise<void>;
|
|
313
318
|
kill(pid: number): Promise<boolean>;
|
|
314
319
|
};
|
|
315
|
-
/**
|
|
320
|
+
/**
|
|
321
|
+
* Preview URLs for ports inside the sandbox (`getHost` gives the address alone).
|
|
322
|
+
*
|
|
323
|
+
* Anyone holding a preview URL can open it; revoke it any time, and it ends with the sandbox.
|
|
324
|
+
* If your account's preview URLs aren't on, `expose` throws `PreviewsUnavailableError`; write to
|
|
325
|
+
* support@promigence.ai to have them turned on, and meanwhile reach the port with
|
|
326
|
+
* `promigence sandbox ssh <sid> -- -N -L <port>:localhost:<port>`.
|
|
327
|
+
*/
|
|
316
328
|
readonly ports: {
|
|
317
|
-
|
|
329
|
+
/**
|
|
330
|
+
* Publish `port`: `{ length: "long" }` for a longer, harder-to-guess URL (apps with sensitive
|
|
331
|
+
* data); omitted, the length this sandbox's project or organization chose (short by default).
|
|
332
|
+
*/
|
|
333
|
+
expose(port: number, opts?: PortExposeOpts): Promise<PortRecord>;
|
|
318
334
|
list(): Promise<PortRecord[]>;
|
|
319
335
|
/** A token revokes that URL; a port number every URL for it. Returns the revoked tokens. */
|
|
320
336
|
revoke(tokenOrPort: string | number): Promise<string[]>;
|
|
@@ -458,13 +474,16 @@ export declare class Sandbox {
|
|
|
458
474
|
}>;
|
|
459
475
|
isRunning(): Promise<boolean>;
|
|
460
476
|
/**
|
|
461
|
-
* `PATCH /sandboxes/:id` —
|
|
477
|
+
* `PATCH /sandboxes/:id` — set this sandbox's timeout, in milliseconds.
|
|
478
|
+
*
|
|
479
|
+
* The timeout is the sandbox's total lifetime, counted from its start, not from now:
|
|
480
|
+
* `setTimeout(600_000)` on a sandbox that has run 2 minutes leaves it 8 more.
|
|
462
481
|
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
482
|
+
* - Raising it commits the added seconds, which has to fit in what is left of the spend cap you set
|
|
483
|
+
* (the run's, or the org default): past it, it throws `SpendCapError` (409) rather than silently
|
|
484
|
+
* extending.
|
|
485
|
+
* - Lowering it goes down to what the sandbox has run plus 60 s, and no further
|
|
486
|
+
* (`InvalidArgumentError` below that). To stop it now, `kill()` it.
|
|
468
487
|
*/
|
|
469
488
|
setTimeout(ms: number): Promise<void>;
|
|
470
489
|
/**
|
|
@@ -476,8 +495,9 @@ export declare class Sandbox {
|
|
|
476
495
|
* the window may be. Bring it back with `resume()`, which is the warm path.
|
|
477
496
|
*
|
|
478
497
|
* `mode: "activity"` is for an agent that runs inside the sandbox on its own: it also waits
|
|
479
|
-
* until
|
|
480
|
-
*
|
|
498
|
+
* until, for the whole window, the sandbox has used under 5% of one core, sent and received no
|
|
499
|
+
* network byte and held no connection out open; disk writes do not count (`AutoPauseMode`).
|
|
500
|
+
* Omitted, the mode is left as it is (`"requests"` unless set).
|
|
481
501
|
*/
|
|
482
502
|
setAutoPause(seconds: number, mode?: AutoPauseMode): Promise<void>;
|
|
483
503
|
/** The idle rule in force: seconds before it pauses itself, `0` when it never does. */
|
|
@@ -499,7 +519,12 @@ export declare class Sandbox {
|
|
|
499
519
|
* full `url` with its scheme.
|
|
500
520
|
*
|
|
501
521
|
* Async, because the address is a capability minted on demand rather than
|
|
502
|
-
* a hostname derived from the id. An existing URL for the port is reused, not re-minted
|
|
522
|
+
* a hostname derived from the id. An existing URL for the port is reused, not re-minted; a new
|
|
523
|
+
* one has the length this sandbox's project or organization chose (short by default).
|
|
524
|
+
*
|
|
525
|
+
* If your account's preview URLs aren't on, this throws `PreviewsUnavailableError`; write to
|
|
526
|
+
* support@promigence.ai to have them turned on, and meanwhile reach the port with
|
|
527
|
+
* `promigence sandbox ssh <sid> -- -N -L <port>:localhost:<port>`.
|
|
503
528
|
*/
|
|
504
529
|
getHost(port: number): Promise<string>;
|
|
505
530
|
/**
|
package/dist/sandbox.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { inspect } from "node:util";
|
|
2
|
-
import { ApiError, CommandExitError, EnvFailedError, IMAGE_BUILD_TIMEOUT_MS, InvalidArgumentError, NotFoundError, TimeoutError, UnsupportedError, WAIT_TIMEOUT_MS, withPreviewsFix, } from "./core.js";
|
|
2
|
+
import { ApiError, CommandExitError, EnvFailedError, IMAGE_BUILD_TIMEOUT_MS, InvalidArgumentError, NotFoundError, SpendCapError, TimeoutError, UnsupportedError, WAIT_TIMEOUT_MS, withPreviewsFix, } from "./core.js";
|
|
3
3
|
import { PromigenceClient, LONG_POLL_MS, checkImageRef, checkImageVersioned, checkSandboxId, errorFor, newIdempotencyKey, toCommandResult, isWaiting, } from "./client.js";
|
|
4
4
|
import { CommandHandle } from "./handles.js";
|
|
5
5
|
import { CommandTimeoutError, answerTimedOut } from "./command-errors.js";
|
|
6
6
|
import { hintArg } from "./hint.js";
|
|
7
|
+
export const 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 " +
|
|
8
|
+
"sandbox with the longer `timeoutMs` and a `spendCap` that covers it.";
|
|
7
9
|
const EXEC_TIMEOUT_DEFAULT_MS = 15 * 60_000;
|
|
8
10
|
function textSink(cb) {
|
|
9
11
|
if (!cb)
|
|
@@ -37,6 +39,15 @@ export function ptyCommand(opts) {
|
|
|
37
39
|
parts.push(opts.cmd ?? DEFAULT_PTY_CMD);
|
|
38
40
|
return parts.join(" ");
|
|
39
41
|
}
|
|
42
|
+
function allowList(hosts) {
|
|
43
|
+
if (!Array.isArray(hosts)) {
|
|
44
|
+
throw new InvalidArgumentError("allowHosts must be a list of hosts", 'Pass allowHosts: ["github.com"].');
|
|
45
|
+
}
|
|
46
|
+
if (!hosts.some((h) => typeof h !== "string" || h.trim() !== "")) {
|
|
47
|
+
throw new 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.');
|
|
48
|
+
}
|
|
49
|
+
return hosts;
|
|
50
|
+
}
|
|
40
51
|
function permString(mode) {
|
|
41
52
|
const r = (m) => `${m & 4 ? "r" : "-"}${m & 2 ? "w" : "-"}${m & 1 ? "x" : "-"}`;
|
|
42
53
|
return r((mode >> 6) & 7) + r((mode >> 3) & 7) + r(mode & 7);
|
|
@@ -89,6 +100,8 @@ export async function imageSnapshot(client, opts, hooks = {}) {
|
|
|
89
100
|
create.tier = opts.tier;
|
|
90
101
|
if (opts.alias !== undefined)
|
|
91
102
|
create.alias = opts.alias;
|
|
103
|
+
if (opts.moveAlias)
|
|
104
|
+
create.moveAlias = true;
|
|
92
105
|
if (opts.registrySecret !== undefined)
|
|
93
106
|
create.registrySecret = opts.registrySecret;
|
|
94
107
|
let build = (await client.snapshots.create(create)) ?? {};
|
|
@@ -348,7 +361,7 @@ export class Sandbox {
|
|
|
348
361
|
},
|
|
349
362
|
};
|
|
350
363
|
this.ports = {
|
|
351
|
-
expose: (port) => this.#client.sandboxes.ports.expose(this.sandboxId, port),
|
|
364
|
+
expose: (port, opts) => this.#client.sandboxes.ports.expose(this.sandboxId, port, opts),
|
|
352
365
|
list: () => this.#client.sandboxes.ports.list(this.sandboxId),
|
|
353
366
|
revoke: async (tokenOrPort) => (await this.#client.sandboxes.ports.revoke(this.sandboxId, tokenOrPort)).revoked,
|
|
354
367
|
};
|
|
@@ -362,6 +375,9 @@ export class Sandbox {
|
|
|
362
375
|
return format === "bytes" ? bytes : new TextDecoder().decode(bytes);
|
|
363
376
|
}),
|
|
364
377
|
write: async (path, data, opts = {}) => {
|
|
378
|
+
if (typeof path !== "string") {
|
|
379
|
+
throw new 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).");
|
|
380
|
+
}
|
|
365
381
|
let body;
|
|
366
382
|
if (typeof data === "string" || data instanceof Uint8Array)
|
|
367
383
|
body = data;
|
|
@@ -556,8 +572,8 @@ export class Sandbox {
|
|
|
556
572
|
start.timeoutMs = opts.timeoutMs;
|
|
557
573
|
if (opts.allowInternetAccess === false)
|
|
558
574
|
start.network = "none";
|
|
559
|
-
if (opts.allowHosts
|
|
560
|
-
start.network = { allow: opts.allowHosts };
|
|
575
|
+
if (opts.allowHosts !== undefined && opts.allowHosts !== null)
|
|
576
|
+
start.network = { allow: allowList(opts.allowHosts) };
|
|
561
577
|
if (opts.metadata)
|
|
562
578
|
start.labels = opts.metadata;
|
|
563
579
|
if (opts.secrets?.length)
|
|
@@ -695,7 +711,15 @@ export class Sandbox {
|
|
|
695
711
|
return rec.state === "ready" || rec.state === "running";
|
|
696
712
|
}
|
|
697
713
|
async setTimeout(ms) {
|
|
698
|
-
|
|
714
|
+
try {
|
|
715
|
+
await this.#client.sandboxes.patch(this.sandboxId, { timeoutMs: ms });
|
|
716
|
+
}
|
|
717
|
+
catch (e) {
|
|
718
|
+
if (e instanceof SpendCapError && e.code === "cap_exceeded") {
|
|
719
|
+
Object.defineProperty(e, "fix", { value: EXTEND_OVER_CAP_FIX, enumerable: true, configurable: true });
|
|
720
|
+
}
|
|
721
|
+
throw e;
|
|
722
|
+
}
|
|
699
723
|
this.timeoutMs = ms;
|
|
700
724
|
}
|
|
701
725
|
async setAutoPause(seconds, mode) {
|
package/dist/transport-worker.js
CHANGED
|
@@ -290,6 +290,7 @@ function start(init) {
|
|
|
290
290
|
let rrAny = 0;
|
|
291
291
|
let pumpTimer;
|
|
292
292
|
let pumpSoon;
|
|
293
|
+
let turnsHeld = false;
|
|
293
294
|
const freeNow = (l, now) => {
|
|
294
295
|
for (let i = 0; i < l.length; i++) {
|
|
295
296
|
const k = (rr + i) % l.length;
|
|
@@ -304,6 +305,8 @@ function start(init) {
|
|
|
304
305
|
const pump = () => {
|
|
305
306
|
pumpTimer = undefined;
|
|
306
307
|
pumpSoon = undefined;
|
|
308
|
+
if (turnsHeld)
|
|
309
|
+
return;
|
|
307
310
|
while (waiting.length > 0) {
|
|
308
311
|
const l = live();
|
|
309
312
|
if (l.length === 0) {
|
|
@@ -346,7 +349,7 @@ function start(init) {
|
|
|
346
349
|
rrAny = (rrAny % l.length) + 1;
|
|
347
350
|
return e;
|
|
348
351
|
}
|
|
349
|
-
if (waiting.length === 0) {
|
|
352
|
+
if (waiting.length === 0 && !turnsHeld) {
|
|
350
353
|
const now = hrNow();
|
|
351
354
|
const e = freeNow(l, now);
|
|
352
355
|
if (e !== undefined) {
|
|
@@ -527,6 +530,12 @@ function start(init) {
|
|
|
527
530
|
}
|
|
528
531
|
else if (m?.t === "crash-for-test")
|
|
529
532
|
setImmediate(() => { throw new Error("transport thread crash (test)"); });
|
|
533
|
+
else if (m?.t === "hold-turns-for-test")
|
|
534
|
+
turnsHeld = true;
|
|
535
|
+
else if (m?.t === "release-turns-for-test") {
|
|
536
|
+
turnsHeld = false;
|
|
537
|
+
pump();
|
|
538
|
+
}
|
|
530
539
|
});
|
|
531
540
|
void open();
|
|
532
541
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promigence/sdk",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.17",
|
|
4
4
|
"description": "The fastest sandboxes for AI agents and code. The Promigence TypeScript SDK: snapshot an environment once, fork it as often as you need, run commands in every copy. A simple Sandbox class and the full PromigenceClient, with zero runtime dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://www.promigence.ai",
|