withruntime 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BLAXEL.md +4 -4
- package/DAYTONA.md +52 -40
- package/E2B.md +65 -62
- package/README.md +15 -7
- package/README.src.md +15 -7
- package/VERCEL.md +34 -22
- package/compatibility-lock.json +16 -16
- package/dist/blaxel/errors.js +1 -1
- package/dist/blaxel/lookup.d.ts +3 -2
- package/dist/blaxel/lookup.js +3 -2
- package/dist/blaxel/pagination.d.ts +2 -2
- package/dist/blaxel/pagination.js +45 -17
- package/dist/blaxel/preview.d.ts +2 -1
- package/dist/blaxel/preview.js +15 -13
- package/dist/blaxel/process.js +56 -10
- package/dist/blaxel/sandbox.d.ts +2 -0
- package/dist/blaxel/sandbox.js +2 -0
- package/dist/blaxel/snapshot.js +3 -5
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +262 -56
- package/dist/client.d.ts +41 -3
- package/dist/client.js +67 -11
- package/dist/cloudflare/identity.d.ts +7 -0
- package/dist/cloudflare/identity.js +13 -0
- package/dist/cloudflare/index.d.ts +9 -0
- package/dist/cloudflare/index.js +47 -9
- package/dist/cloudflare/worker.d.ts +4 -0
- package/dist/cloudflare/worker.js +166 -16
- package/dist/codesandbox/guest-service.js +57 -20
- package/dist/codesandbox/index.js +42 -8
- package/dist/compat/core.d.ts +9 -8
- package/dist/compat/core.js +60 -21
- package/dist/compat/files.js +22 -4
- package/dist/compat/lease.d.ts +36 -0
- package/dist/compat/lease.js +87 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +107 -38
- package/dist/daytona/computer-use.d.ts +162 -0
- package/dist/daytona/computer-use.js +324 -0
- package/dist/daytona/context.d.ts +10 -0
- package/dist/daytona/context.js +24 -0
- package/dist/daytona/daytona.d.ts +20 -0
- package/dist/daytona/daytona.js +134 -20
- package/dist/daytona/filesystem.d.ts +17 -4
- package/dist/daytona/filesystem.js +129 -38
- package/dist/daytona/index.d.ts +3 -27
- package/dist/daytona/index.js +2 -7
- package/dist/daytona/process.d.ts +63 -6
- package/dist/daytona/process.js +224 -23
- package/dist/daytona/sandbox.d.ts +20 -6
- package/dist/daytona/sandbox.js +55 -32
- package/dist/e2b/client.d.ts +5 -0
- package/dist/e2b/client.js +6 -2
- package/dist/e2b/commands.d.ts +43 -12
- package/dist/e2b/commands.js +217 -64
- package/dist/e2b/errors.d.ts +7 -0
- package/dist/e2b/errors.js +20 -0
- package/dist/e2b/filesystem.d.ts +2 -1
- package/dist/e2b/filesystem.js +43 -6
- package/dist/e2b/index.d.ts +3 -2
- package/dist/e2b/index.js +2 -1
- package/dist/e2b/pty.d.ts +24 -0
- package/dist/e2b/pty.js +83 -0
- package/dist/e2b/sandbox.d.ts +17 -6
- package/dist/e2b/sandbox.js +239 -43
- package/dist/e2b/users.d.ts +30 -0
- package/dist/e2b/users.js +185 -0
- package/dist/file-source.d.ts +16 -0
- package/dist/file-source.js +162 -0
- package/dist/freestyle/index.d.ts +9 -1
- package/dist/freestyle/index.js +38 -5
- package/dist/index.d.ts +3 -1
- package/dist/login.js +16 -10
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +6 -1
- package/dist/modal/index.js +2 -1
- package/dist/network-cli.js +139 -43
- package/dist/openai-agents/index.js +9 -45
- package/dist/page.d.ts +1 -1
- package/dist/page.js +2 -2
- package/dist/products/browser.d.ts +60 -0
- package/dist/products/browser.js +37 -0
- package/dist/products/desktop.d.ts +2 -2
- package/dist/products/desktop.js +2 -2
- package/dist/products/images.d.ts +3 -1
- package/dist/products/images.js +14 -2
- package/dist/products/index.d.ts +14 -0
- package/dist/products/index.js +12 -0
- package/dist/products/network-products.d.ts +7 -0
- package/dist/products/network-products.js +3 -0
- package/dist/products/observability.d.ts +44 -0
- package/dist/products/observability.js +6 -0
- package/dist/products/previews.d.ts +8 -2
- package/dist/products/previews.js +11 -3
- package/dist/products/private-network.d.ts +22 -0
- package/dist/products/private-network.js +10 -0
- package/dist/products/volumes.d.ts +42 -0
- package/dist/products/volumes.js +36 -0
- package/dist/products/watch.js +61 -8
- package/dist/proxy.js +3 -1
- package/dist/runloop/index.d.ts +13 -27
- package/dist/runloop/index.js +149 -52
- package/dist/sandbox.d.ts +59 -8
- package/dist/sandbox.js +479 -68
- package/dist/snapshots.d.ts +9 -4
- package/dist/sprites/index.js +69 -33
- package/dist/tar.d.ts +3 -1
- package/dist/tar.js +140 -14
- package/dist/transport.d.ts +4 -7
- package/dist/transport.js +205 -122
- package/dist/types.d.ts +5 -2
- package/dist/vercel/command.d.ts +1 -1
- package/dist/vercel/command.js +3 -2
- package/dist/vercel/errors.d.ts +1 -1
- package/dist/vercel/errors.js +7 -1
- package/dist/vercel/index.d.ts +1 -7
- package/dist/vercel/index.js +1 -3
- package/dist/vercel/sandbox-user.d.ts +70 -0
- package/dist/vercel/sandbox-user.js +206 -0
- package/dist/vercel/sandbox.d.ts +55 -20
- package/dist/vercel/sandbox.js +353 -99
- package/dist/vercel/snapshot.d.ts +3 -2
- package/dist/vercel/snapshot.js +9 -5
- package/package.json +2 -1
package/dist/network-cli.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, open, rename, unlink } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
2
5
|
import { RuntimeError } from "./errors.js";
|
|
3
6
|
import { named } from "./cli-name.js";
|
|
4
7
|
import { generateWireGuardKeyPair } from "./wireguard.js";
|
|
@@ -67,6 +70,12 @@ export const NETWORK_HELP = {
|
|
|
67
70
|
upstream-proxy remove Connections leave directly again
|
|
68
71
|
A proxy that refuses or cannot be reached fails the connection; nothing goes
|
|
69
72
|
around it. Paid accounts only.
|
|
73
|
+
|
|
74
|
+
private on Your sandboxes reach each other by name, over TCP:
|
|
75
|
+
<name>.sandbox.internal:<port> or <id>.sandbox.internal:<port>
|
|
76
|
+
private off They stop; open connections between them are cut
|
|
77
|
+
private status On or off
|
|
78
|
+
Only your own sandboxes answer. Paid accounts only, and free.
|
|
70
79
|
`,
|
|
71
80
|
};
|
|
72
81
|
function usage(product, message) {
|
|
@@ -92,15 +101,63 @@ function portNumber(product, text) {
|
|
|
92
101
|
throw usage(product, "Give a port from 1 to 65535.");
|
|
93
102
|
return value;
|
|
94
103
|
}
|
|
104
|
+
/** A fresh private inode prevents disclosure through permissive files, symlinks
|
|
105
|
+
* and hard links. Rename publishes complete contents without a readable window. */
|
|
106
|
+
async function privateOutput(path, text) {
|
|
107
|
+
const temporary = join(dirname(path), `.${basename(path)}.${randomUUID()}.tmp`);
|
|
108
|
+
try {
|
|
109
|
+
const handle = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 0o600);
|
|
110
|
+
try {
|
|
111
|
+
await handle.writeFile(text);
|
|
112
|
+
await handle.sync();
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
await handle.close();
|
|
116
|
+
}
|
|
117
|
+
return temporary;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
await unlink(temporary).catch(() => undefined);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Check and stage before changing a remote peer. If the request or final write
|
|
125
|
+
* fails, this private draft preserves the newly generated key for recovery. */
|
|
126
|
+
async function prepareOutput(path, privateKey) {
|
|
127
|
+
try {
|
|
128
|
+
const info = await lstat(path);
|
|
129
|
+
if (!info.isFile() || info.isSymbolicLink())
|
|
130
|
+
throw usage("tunnel", "The output must be a regular file, not a directory or symbolic link.");
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error.code !== "ENOENT")
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
const recovery = await privateOutput(path, `# DRAFT: request or output incomplete. Keep this file for recovery.\n[Interface]\nPrivateKey = ${privateKey ?? "<your private key>"}\n`);
|
|
137
|
+
return {
|
|
138
|
+
recovery,
|
|
139
|
+
async publish(config) {
|
|
140
|
+
const temporary = await privateOutput(path, config);
|
|
141
|
+
try {
|
|
142
|
+
await rename(temporary, path);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
await unlink(temporary).catch(() => undefined);
|
|
146
|
+
}
|
|
147
|
+
await unlink(recovery);
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
95
151
|
/** Writes a wg-quick file readable by its owner alone, or prints it. */
|
|
96
|
-
async function deliver(out, config, path, hint, ready = true) {
|
|
152
|
+
async function deliver(out, config, path, hint, ready = true, prepared) {
|
|
97
153
|
if (!config) {
|
|
98
154
|
out.error(hint);
|
|
99
155
|
return;
|
|
100
156
|
}
|
|
101
157
|
if (path) {
|
|
102
|
-
|
|
103
|
-
|
|
158
|
+
if (!prepared)
|
|
159
|
+
throw new Error("Tunnel output was not prepared.");
|
|
160
|
+
await prepared.publish(config);
|
|
104
161
|
out.error(ready
|
|
105
162
|
? `Wrote ${path}. Bring it up with: sudo wg-quick up ${path.startsWith("/") ? path : `./${path}`}`
|
|
106
163
|
: `Wrote ${path}. ${hint}`);
|
|
@@ -276,52 +333,69 @@ export async function networkProductCommand(product, argv, args, out, client) {
|
|
|
276
333
|
case "peers": {
|
|
277
334
|
const action = rest[0];
|
|
278
335
|
if (action === "add" || action === "rotate") {
|
|
336
|
+
const argument = need(rest[1], action === "add" ? "the peer's name, such as office" : "a peer id");
|
|
279
337
|
// The pair is made here unless a key is given: the private key never
|
|
280
338
|
// leaves this machine, and goes only into the file written below.
|
|
281
339
|
const given = flag("public-key");
|
|
282
340
|
const pair = given ? undefined : generateWireGuardKeyPair();
|
|
283
341
|
const publicKey = given ?? pair.publicKey;
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
342
|
+
const path = out.json ? undefined : (flag("out") ?? (pair ? "runtime.conf" : undefined));
|
|
343
|
+
const prepared = path ? await prepareOutput(path, pair?.privateKey) : undefined;
|
|
344
|
+
try {
|
|
345
|
+
const answer = action === "add"
|
|
346
|
+
? await runtime.tunnel.addPeer({
|
|
347
|
+
name: argument,
|
|
348
|
+
publicKey,
|
|
349
|
+
routes: args.flags.get("route") ?? [],
|
|
350
|
+
})
|
|
351
|
+
: await runtime.tunnel.rotatePeer(argument, { publicKey });
|
|
352
|
+
const configReady = answer.configReady ?? !!answer.config;
|
|
353
|
+
const draftHint = "Keep this file: it preserves your private key. Read runtime tunnel get when the gateway is ready, replace PublicKey in [Peer] with gatewayPublicKey, then run wg-quick up.";
|
|
354
|
+
// Older servers returned no config while the gateway was starting.
|
|
355
|
+
// Preserve the locally generated key in a draft rather than discard it.
|
|
356
|
+
const supplied = answer.config ??
|
|
357
|
+
[
|
|
358
|
+
"# DRAFT: gateway key missing. Keep this file; it preserves your private key.",
|
|
359
|
+
"[Interface]",
|
|
360
|
+
`PrivateKey = ${pair?.privateKey ?? "<your private key>"}`,
|
|
361
|
+
`Address = ${answer.peer.address}/32`,
|
|
362
|
+
"",
|
|
363
|
+
"[Peer]",
|
|
364
|
+
`PublicKey = ${answer.tunnel.gatewayPublicKey ?? "<gatewayPublicKey from runtime tunnel get>"}`,
|
|
365
|
+
`Endpoint = ${answer.tunnel.endpoint}`,
|
|
366
|
+
`AllowedIPs = ${answer.tunnel.subnet}`,
|
|
367
|
+
"PersistentKeepalive = 25",
|
|
368
|
+
"",
|
|
369
|
+
].join("\n");
|
|
370
|
+
const privateKeyRow = /^[ \t]*PrivateKey[ \t]*=[^\r\n]*$/gm;
|
|
371
|
+
if (pair && (supplied.match(privateKeyRow)?.length ?? 0) !== 1)
|
|
372
|
+
throw new RuntimeError({
|
|
373
|
+
code: "invalid_response",
|
|
374
|
+
status: 0,
|
|
375
|
+
message: "Runtime returned a tunnel configuration without exactly one private-key field.",
|
|
376
|
+
});
|
|
377
|
+
const config = pair
|
|
378
|
+
? supplied.replace(privateKeyRow, `PrivateKey = ${pair.privateKey}`)
|
|
379
|
+
: supplied;
|
|
380
|
+
if (out.json) {
|
|
381
|
+
print("", {
|
|
382
|
+
...answer,
|
|
383
|
+
config,
|
|
384
|
+
configReady,
|
|
385
|
+
...(!configReady ? { hint: draftHint } : {}),
|
|
386
|
+
});
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
if (answer.tunnel.funded === false)
|
|
390
|
+
out.error(fundingState(answer.tunnel));
|
|
391
|
+
await deliver(out, config, path, configReady ? answer.hint : draftHint, configReady, prepared);
|
|
319
392
|
return 0;
|
|
320
393
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
394
|
+
catch (error) {
|
|
395
|
+
if (prepared)
|
|
396
|
+
out.error(`The peer request or output did not finish. Your private draft is saved at ${prepared.recovery}; keep it for recovery.`);
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
325
399
|
}
|
|
326
400
|
if (action === "rm" || action === "remove") {
|
|
327
401
|
const after = await runtime.tunnel.removePeer(need(rest[1], "a peer id"));
|
|
@@ -332,6 +406,28 @@ export async function networkProductCommand(product, argv, args, out, client) {
|
|
|
332
406
|
}
|
|
333
407
|
}
|
|
334
408
|
}
|
|
409
|
+
if (product === "network" && verb === "private") {
|
|
410
|
+
const describe = (state) => state.enabled
|
|
411
|
+
? "On: your sandboxes reach each other at <name>.sandbox.internal:<port> over TCP."
|
|
412
|
+
: state.allowed
|
|
413
|
+
? "Off. Turn it on: runtime network private on"
|
|
414
|
+
: "Off. Reaching your sandboxes by name is for paid accounts; add credit to turn it on.";
|
|
415
|
+
switch (rest[0]) {
|
|
416
|
+
case "on":
|
|
417
|
+
case "off": {
|
|
418
|
+
const state = await runtime.network.private.set({ enabled: rest[0] === "on" });
|
|
419
|
+
print(state.enabled ? describe(state) : "Off. Open connections between your sandboxes are cut.", state);
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
case "status":
|
|
423
|
+
case undefined: {
|
|
424
|
+
const state = await runtime.network.private.get();
|
|
425
|
+
print(describe(state), state);
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
throw usage(product, "private takes on, off or status.");
|
|
430
|
+
}
|
|
335
431
|
if (product === "network") {
|
|
336
432
|
if (verb !== "upstream-proxy")
|
|
337
433
|
throw usage(product, `Unknown network command ${verb}.`);
|
|
@@ -531,29 +531,11 @@ export class RuntimeCloudSandboxSession {
|
|
|
531
531
|
// Workspace archives.
|
|
532
532
|
async persistWorkspace() {
|
|
533
533
|
const root = this.state.manifest.root;
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
"-c",
|
|
540
|
-
`mkdir -p -- ${STAGING} && tar ${skip.join(" ")} -C "$1" -cf - . > "$2"`,
|
|
541
|
-
"sh",
|
|
542
|
-
root,
|
|
543
|
-
staging,
|
|
544
|
-
], {
|
|
545
|
-
cwd: "/",
|
|
546
|
-
timeoutMs: this.state.execTimeoutMs,
|
|
547
|
-
}));
|
|
548
|
-
if (packed.exitCode !== 0)
|
|
549
|
-
throw new SandboxProviderError(`${PROVIDER} could not archive the workspace.`, {
|
|
550
|
-
stderr: packed.stderr,
|
|
551
|
-
});
|
|
552
|
-
return await this.#call((sbx) => sbx.files.read(staging));
|
|
553
|
-
}
|
|
554
|
-
finally {
|
|
555
|
-
await this.#call((sbx) => sbx.files.remove(staging)).catch(() => undefined);
|
|
556
|
-
}
|
|
534
|
+
return this.#call((sbx) => sbx.files.archive(root, {
|
|
535
|
+
gzip: false,
|
|
536
|
+
...(root === FILES_HOME ? { exclude: [".openai-agents-staging"] } : {}),
|
|
537
|
+
timeoutMs: this.state.execTimeoutMs,
|
|
538
|
+
}));
|
|
557
539
|
}
|
|
558
540
|
async hydrateWorkspace(data) {
|
|
559
541
|
const bytes = typeof data === "string"
|
|
@@ -561,28 +543,10 @@ export class RuntimeCloudSandboxSession {
|
|
|
561
543
|
: data instanceof Uint8Array
|
|
562
544
|
? data
|
|
563
545
|
: new Uint8Array(data);
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
"sh",
|
|
569
|
-
"-c",
|
|
570
|
-
'mkdir -p -- "$1" && tar -C "$1" -xf "$2"',
|
|
571
|
-
"sh",
|
|
572
|
-
this.state.manifest.root,
|
|
573
|
-
staging,
|
|
574
|
-
], {
|
|
575
|
-
cwd: "/",
|
|
576
|
-
timeoutMs: this.state.execTimeoutMs,
|
|
577
|
-
}));
|
|
578
|
-
if (unpacked.exitCode !== 0)
|
|
579
|
-
throw new SandboxProviderError(`${PROVIDER} could not restore the workspace.`, {
|
|
580
|
-
stderr: unpacked.stderr,
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
finally {
|
|
584
|
-
await this.#call((sbx) => sbx.files.remove(staging)).catch(() => undefined);
|
|
585
|
-
}
|
|
546
|
+
await this.#call((sbx) => sbx.files.unarchive(this.state.manifest.root, bytes, {
|
|
547
|
+
gzip: false,
|
|
548
|
+
timeoutMs: this.state.execTimeoutMs,
|
|
549
|
+
}));
|
|
586
550
|
}
|
|
587
551
|
// Lifecycle.
|
|
588
552
|
async running() {
|
package/dist/page.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare class Page<T> implements AsyncIterable<T> {
|
|
|
5
5
|
private readonly fetchPage;
|
|
6
6
|
constructor(data: T[], nextCursor: string | null, fetchPage: (cursor: string) => Promise<Page<T>>);
|
|
7
7
|
get hasMore(): boolean;
|
|
8
|
-
next(): Promise<Page<T> | null>;
|
|
8
|
+
next(cursor?: string | null): Promise<Page<T> | null>;
|
|
9
9
|
/** Every page, fetched as you go. */
|
|
10
10
|
pages(): AsyncGenerator<Page<T>>;
|
|
11
11
|
[Symbol.asyncIterator](): AsyncGenerator<T>;
|
package/dist/page.js
CHANGED
|
@@ -11,8 +11,8 @@ export class Page {
|
|
|
11
11
|
get hasMore() {
|
|
12
12
|
return this.nextCursor !== null;
|
|
13
13
|
}
|
|
14
|
-
async next() {
|
|
15
|
-
return
|
|
14
|
+
async next(cursor = this.nextCursor) {
|
|
15
|
+
return cursor === null ? null : this.fetchPage(cursor);
|
|
16
16
|
}
|
|
17
17
|
/** Every page, fetched as you go. */
|
|
18
18
|
async *pages() {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Sandbox } from "../sandbox.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
export type SandboxBrowser = {
|
|
4
|
+
running: false;
|
|
5
|
+
} | {
|
|
6
|
+
running: true;
|
|
7
|
+
headless: boolean;
|
|
8
|
+
/** Chromium's version, e.g. "Chrome/154.0.8037.92". */
|
|
9
|
+
version: string;
|
|
10
|
+
/** The browser's DevTools WebSocket with its private token: give it as it
|
|
11
|
+
* is to Playwright's `chromium.connectOverCDP`, `puppeteer.connect({
|
|
12
|
+
* browserWSEndpoint })`, browser-use or Stagehand. */
|
|
13
|
+
cdpUrl: string;
|
|
14
|
+
/** The DevTools HTTP endpoint; send `headers` with it. */
|
|
15
|
+
httpUrl: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
/** When the token in cdpUrl ends; `get()` returns a fresh one. */
|
|
18
|
+
expiresAt: string | null;
|
|
19
|
+
/** headless: false only: the desktop's live view. */
|
|
20
|
+
streamUrl?: string;
|
|
21
|
+
};
|
|
22
|
+
export type BrowserStartOptions = {
|
|
23
|
+
/** true (the default): no window. false: on the sandbox's desktop, with a live view. */
|
|
24
|
+
headless?: boolean;
|
|
25
|
+
width?: number;
|
|
26
|
+
height?: number;
|
|
27
|
+
};
|
|
28
|
+
/** `sbx.browser`: Chromium in the sandbox, for agents that speak CDP.
|
|
29
|
+
*
|
|
30
|
+
* const { cdpUrl } = await sbx.browser.start();
|
|
31
|
+
* const browser = await chromium.connectOverCDP(cdpUrl);
|
|
32
|
+
*
|
|
33
|
+
* Inside the sandbox the same browser is at http://127.0.0.1:9222. */
|
|
34
|
+
export declare function sandboxBrowser(t: Transport, sandbox: Sandbox): {
|
|
35
|
+
/** Starts Chromium and returns its CDP address. The first start in a
|
|
36
|
+
* sandbox installs Chromium, about a minute; this waits for it (up to 10
|
|
37
|
+
* minutes). Starting a running browser returns it as it is. */
|
|
38
|
+
start(input?: BrowserStartOptions, options?: RequestOptions): Promise<{
|
|
39
|
+
running: true;
|
|
40
|
+
headless: boolean;
|
|
41
|
+
/** Chromium's version, e.g. "Chrome/154.0.8037.92". */
|
|
42
|
+
version: string;
|
|
43
|
+
/** The browser's DevTools WebSocket with its private token: give it as it
|
|
44
|
+
* is to Playwright's `chromium.connectOverCDP`, `puppeteer.connect({
|
|
45
|
+
* browserWSEndpoint })`, browser-use or Stagehand. */
|
|
46
|
+
cdpUrl: string;
|
|
47
|
+
/** The DevTools HTTP endpoint; send `headers` with it. */
|
|
48
|
+
httpUrl: string;
|
|
49
|
+
headers: Record<string, string>;
|
|
50
|
+
/** When the token in cdpUrl ends; `get()` returns a fresh one. */
|
|
51
|
+
expiresAt: string | null;
|
|
52
|
+
/** headless: false only: the desktop's live view. */
|
|
53
|
+
streamUrl?: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Whether it runs, and a fresh cdpUrl when it does. */
|
|
56
|
+
get: (options?: RequestOptions) => Promise<SandboxBrowser>;
|
|
57
|
+
stop: (options?: RequestOptions) => Promise<{
|
|
58
|
+
stopped: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** `sbx.browser`: Chromium in the sandbox, for agents that speak CDP.
|
|
2
|
+
*
|
|
3
|
+
* const { cdpUrl } = await sbx.browser.start();
|
|
4
|
+
* const browser = await chromium.connectOverCDP(cdpUrl);
|
|
5
|
+
*
|
|
6
|
+
* Inside the sandbox the same browser is at http://127.0.0.1:9222. */
|
|
7
|
+
export function sandboxBrowser(t, sandbox) {
|
|
8
|
+
const base = () => `/v1/sandboxes/${encodeURIComponent(sandbox.id)}/browser`;
|
|
9
|
+
return {
|
|
10
|
+
/** Starts Chromium and returns its CDP address. The first start in a
|
|
11
|
+
* sandbox installs Chromium, about a minute; this waits for it (up to 10
|
|
12
|
+
* minutes). Starting a running browser returns it as it is. */
|
|
13
|
+
async start(input = {}, options) {
|
|
14
|
+
const deadline = Date.now() + 600_000;
|
|
15
|
+
for (;;) {
|
|
16
|
+
try {
|
|
17
|
+
return await t.json({
|
|
18
|
+
method: "POST",
|
|
19
|
+
path: `${base()}:start`,
|
|
20
|
+
body: input,
|
|
21
|
+
...options,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const code = error.code;
|
|
26
|
+
if ((code !== "browser_installing" && code !== "desktop_installing") ||
|
|
27
|
+
Date.now() > deadline)
|
|
28
|
+
throw error;
|
|
29
|
+
await new Promise((resolve) => setTimeout(resolve, error.retryAfterMs ?? 10_000));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
/** Whether it runs, and a fresh cdpUrl when it does. */
|
|
34
|
+
get: (options) => t.json({ method: "GET", path: base(), ...options }),
|
|
35
|
+
stop: (options) => t.json({ method: "POST", path: `${base()}:stop`, ...options }),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -115,8 +115,8 @@ export declare function sandboxDesktop(t: Transport, sandbox: Sandbox): {
|
|
|
115
115
|
focus: (windowId: string, options?: RequestOptions) => Promise<{
|
|
116
116
|
ok: true;
|
|
117
117
|
}>;
|
|
118
|
-
/** Opens `url` in
|
|
119
|
-
* start,
|
|
118
|
+
/** Opens `url` in Chromium on the desktop. Right after a sandbox's first
|
|
119
|
+
* start, Chromium may still be installing; this waits for it. */
|
|
120
120
|
open(url: string, options?: RequestOptions): Promise<{
|
|
121
121
|
ok: true;
|
|
122
122
|
}>;
|
package/dist/products/desktop.js
CHANGED
|
@@ -50,8 +50,8 @@ export function sandboxDesktop(t, sandbox) {
|
|
|
50
50
|
cursor: (options) => act({ action: "cursor" }, options),
|
|
51
51
|
windows: async (options) => (await act({ action: "windows" }, options)).windows,
|
|
52
52
|
focus: (windowId, options) => act({ action: "focus", windowId }, options),
|
|
53
|
-
/** Opens `url` in
|
|
54
|
-
* start,
|
|
53
|
+
/** Opens `url` in Chromium on the desktop. Right after a sandbox's first
|
|
54
|
+
* start, Chromium may still be installing; this waits for it. */
|
|
55
55
|
async open(url, options) {
|
|
56
56
|
const deadline = Date.now() + 600_000;
|
|
57
57
|
for (;;) {
|
|
@@ -212,7 +212,9 @@ export declare function images(t: Transport): {
|
|
|
212
212
|
/** Point `tag` of the image's name at this version. */
|
|
213
213
|
tag(ref: string, tag: string, options?: RequestOptions): Promise<Image>;
|
|
214
214
|
untag(ref: string, tag: string, options?: RequestOptions): Promise<Image>;
|
|
215
|
-
/** Delete one version (by id, name:tag or name@version) and its tags.
|
|
215
|
+
/** Delete one version (by id, name:tag or name@version) and its tags. A
|
|
216
|
+
* bare name deletes its latest tag's version, or its only version when
|
|
217
|
+
* it has one and no latest tag; with several, the error names its tags. */
|
|
216
218
|
delete(ref: string, options?: RequestOptions): Promise<Image>;
|
|
217
219
|
/** Credentials for private registries. The secret is sealed on arrival
|
|
218
220
|
* and never returned. */
|
package/dist/products/images.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RuntimeError } from "../errors.js";
|
|
1
2
|
import { Page } from "../page.js";
|
|
2
3
|
const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
|
|
3
4
|
const PARALLEL_UPLOADS = 4;
|
|
@@ -189,11 +190,22 @@ export function images(t) {
|
|
|
189
190
|
...options,
|
|
190
191
|
});
|
|
191
192
|
},
|
|
192
|
-
/** Delete one version (by id, name:tag or name@version) and its tags.
|
|
193
|
+
/** Delete one version (by id, name:tag or name@version) and its tags. A
|
|
194
|
+
* bare name deletes its latest tag's version, or its only version when
|
|
195
|
+
* it has one and no latest tag; with several, the error names its tags. */
|
|
193
196
|
async delete(ref, options) {
|
|
197
|
+
const id = await idOf(ref, options).catch(async (error) => {
|
|
198
|
+
if (!(error instanceof RuntimeError && error.code === "image_not_found") ||
|
|
199
|
+
/[:@]/.test(ref))
|
|
200
|
+
throw error;
|
|
201
|
+
const live = (await list({ name: ref, limit: 100 }, options)).data.filter((image) => image.state !== "deleted" && image.state !== "deleting");
|
|
202
|
+
if (live.length !== 1)
|
|
203
|
+
throw error;
|
|
204
|
+
return live[0].id;
|
|
205
|
+
});
|
|
194
206
|
return t.json({
|
|
195
207
|
method: "POST",
|
|
196
|
-
path: `/v1/images/${enc(
|
|
208
|
+
path: `/v1/images/${enc(id)}:delete`,
|
|
197
209
|
body: {},
|
|
198
210
|
...options,
|
|
199
211
|
});
|
package/dist/products/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Transport } from "../transport.js";
|
|
|
2
2
|
import type { Sandbox } from "../sandbox.js";
|
|
3
3
|
import { images } from "./images.js";
|
|
4
4
|
import { sandboxDesktop } from "./desktop.js";
|
|
5
|
+
import { sandboxBrowser } from "./browser.js";
|
|
5
6
|
import { sandboxInterpreter } from "./interpreter.js";
|
|
6
7
|
import { sandboxNetwork } from "./network.js";
|
|
7
8
|
import { addresses, domains, network, ports, tunnel } from "./network-products.js";
|
|
@@ -40,11 +41,22 @@ export declare const clientExtensions: {
|
|
|
40
41
|
tunnel: typeof tunnel;
|
|
41
42
|
network: typeof network;
|
|
42
43
|
};
|
|
44
|
+
/** Canonical CLI product names share the existing factory instance. Account
|
|
45
|
+
* commands such as secrets, events and billing retain their current names. */
|
|
46
|
+
export declare const clientProductAliases: {
|
|
47
|
+
readonly image: "images";
|
|
48
|
+
readonly volume: "volumes";
|
|
49
|
+
readonly job: "jobs";
|
|
50
|
+
readonly domain: "domains";
|
|
51
|
+
readonly port: "ports";
|
|
52
|
+
readonly address: "addresses";
|
|
53
|
+
};
|
|
43
54
|
export declare const sandboxExtensions: {
|
|
44
55
|
interpreter: typeof sandboxInterpreter;
|
|
45
56
|
previews: typeof sandboxPreviews;
|
|
46
57
|
network: typeof sandboxNetwork;
|
|
47
58
|
desktop: typeof sandboxDesktop;
|
|
59
|
+
browser: typeof sandboxBrowser;
|
|
48
60
|
mounts: typeof sandboxMounts;
|
|
49
61
|
tailscale: typeof sandboxTailscale;
|
|
50
62
|
metrics: typeof sandboxMetrics;
|
|
@@ -52,6 +64,8 @@ export declare const sandboxExtensions: {
|
|
|
52
64
|
};
|
|
53
65
|
export type ClientExtensions = {
|
|
54
66
|
readonly [K in keyof typeof clientExtensions]: ReturnType<(typeof clientExtensions)[K]>;
|
|
67
|
+
} & {
|
|
68
|
+
readonly [K in keyof typeof clientProductAliases]: ReturnType<(typeof clientExtensions)[(typeof clientProductAliases)[K]]>;
|
|
55
69
|
};
|
|
56
70
|
export type SandboxExtensions = {
|
|
57
71
|
readonly [K in keyof typeof sandboxExtensions]: ReturnType<(typeof sandboxExtensions)[K]>;
|
package/dist/products/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { images } from "./images.js";
|
|
2
2
|
import { sandboxDesktop } from "./desktop.js";
|
|
3
|
+
import { sandboxBrowser } from "./browser.js";
|
|
3
4
|
import { sandboxInterpreter } from "./interpreter.js";
|
|
4
5
|
import { sandboxNetwork } from "./network.js";
|
|
5
6
|
import { addresses, domains, network, ports, tunnel } from "./network-products.js";
|
|
@@ -53,11 +54,22 @@ export const clientExtensions = {
|
|
|
53
54
|
tunnel,
|
|
54
55
|
network,
|
|
55
56
|
};
|
|
57
|
+
/** Canonical CLI product names share the existing factory instance. Account
|
|
58
|
+
* commands such as secrets, events and billing retain their current names. */
|
|
59
|
+
export const clientProductAliases = {
|
|
60
|
+
image: "images",
|
|
61
|
+
volume: "volumes",
|
|
62
|
+
job: "jobs",
|
|
63
|
+
domain: "domains",
|
|
64
|
+
port: "ports",
|
|
65
|
+
address: "addresses",
|
|
66
|
+
};
|
|
56
67
|
export const sandboxExtensions = {
|
|
57
68
|
interpreter: sandboxInterpreter,
|
|
58
69
|
previews: sandboxPreviews,
|
|
59
70
|
network: sandboxNetwork,
|
|
60
71
|
desktop: sandboxDesktop,
|
|
72
|
+
browser: sandboxBrowser,
|
|
61
73
|
mounts: sandboxMounts,
|
|
62
74
|
tailscale: sandboxTailscale,
|
|
63
75
|
metrics: sandboxMetrics,
|
|
@@ -195,6 +195,13 @@ export type SetUpstreamProxy = {
|
|
|
195
195
|
* logs apply. A proxy that refuses or cannot be reached fails the connection
|
|
196
196
|
* (upstream-proxy-failed); nothing goes around it. Paid accounts only. */
|
|
197
197
|
export declare function network(t: Transport): {
|
|
198
|
+
/** Your sandboxes reaching each other by name (`private-network.ts`). */
|
|
199
|
+
private: {
|
|
200
|
+
get: (options?: RequestOptions) => Promise<import("./private-network.js").PrivateNetwork>;
|
|
201
|
+
set: (input: {
|
|
202
|
+
enabled: boolean;
|
|
203
|
+
}, options?: RequestOptions) => Promise<import("./private-network.js").PrivateNetwork>;
|
|
204
|
+
};
|
|
198
205
|
upstreamProxy: {
|
|
199
206
|
/** Send outbound connections through your proxy, replacing any set before. */
|
|
200
207
|
set: (input: SetUpstreamProxy, options?: RequestOptions) => Promise<UpstreamProxy & {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { privateNetwork } from "./private-network.js";
|
|
1
2
|
const enc = encodeURIComponent;
|
|
2
3
|
/** `runtime.domains`: serve a sandbox's port at your own hostname, with HTTPS. */
|
|
3
4
|
export function domains(t) {
|
|
@@ -91,6 +92,8 @@ export function tunnel(t) {
|
|
|
91
92
|
export function network(t) {
|
|
92
93
|
const path = "/v1/network/upstream-proxy";
|
|
93
94
|
return {
|
|
95
|
+
/** Your sandboxes reaching each other by name (`private-network.ts`). */
|
|
96
|
+
private: privateNetwork(t),
|
|
94
97
|
upstreamProxy: {
|
|
95
98
|
/** Send outbound connections through your proxy, replacing any set before. */
|
|
96
99
|
set: (input, options) => t.json({
|
|
@@ -10,6 +10,11 @@ export type MetricPoint = {
|
|
|
10
10
|
cpuCores: number | null;
|
|
11
11
|
/** The busiest interval between two readings in the bucket. */
|
|
12
12
|
cpuPeakPercent: number | null;
|
|
13
|
+
/** Measured wall-time percentage ready to run while waiting for CPU; null
|
|
14
|
+
* without two complete readings of the same run. Older servers omit it. */
|
|
15
|
+
cpuWaitPercent?: number | null;
|
|
16
|
+
/** Measured wall-time percentage with a thread waiting for memory. */
|
|
17
|
+
memoryStallPercent?: number | null;
|
|
13
18
|
/** Average resident memory, bytes. */
|
|
14
19
|
memoryBytes: number;
|
|
15
20
|
memoryPeakBytes: number;
|
|
@@ -96,6 +101,45 @@ type List<T> = {
|
|
|
96
101
|
data: T[];
|
|
97
102
|
nextCursor: string | null;
|
|
98
103
|
};
|
|
104
|
+
export type UsageExportQuery = {
|
|
105
|
+
since: string;
|
|
106
|
+
until: string;
|
|
107
|
+
cursor?: string;
|
|
108
|
+
limit?: number;
|
|
109
|
+
};
|
|
110
|
+
export type UsageExportRow = {
|
|
111
|
+
intervalId: string;
|
|
112
|
+
resourceId: string;
|
|
113
|
+
name: string | null;
|
|
114
|
+
kind: string;
|
|
115
|
+
meter: string;
|
|
116
|
+
fundedBy: "credit" | "trial";
|
|
117
|
+
startsAt: string;
|
|
118
|
+
endsAt: string;
|
|
119
|
+
settledAt: string;
|
|
120
|
+
billedMilliseconds: string | null;
|
|
121
|
+
chargedMicros: string;
|
|
122
|
+
quotedRates: Array<{
|
|
123
|
+
component: string;
|
|
124
|
+
unit: string;
|
|
125
|
+
rateMicros: string;
|
|
126
|
+
}>;
|
|
127
|
+
/** Exact JSON text, preserving integers beyond JavaScript's number range. */
|
|
128
|
+
measurementsJson: string | null;
|
|
129
|
+
evidence: "measured" | "partial" | "missing" | null;
|
|
130
|
+
};
|
|
131
|
+
export type UsageExportPage = {
|
|
132
|
+
since: string;
|
|
133
|
+
until: string;
|
|
134
|
+
data: UsageExportRow[];
|
|
135
|
+
nextCursor: string | null;
|
|
136
|
+
/** Concatenate pages in order; only the first page includes the header. */
|
|
137
|
+
csv: string;
|
|
138
|
+
};
|
|
139
|
+
/** Top-level cross-product settled usage, selected by UTC settlement timestamps
|
|
140
|
+
* in [since, until). Pending holds are excluded. Follow nextCursor using the
|
|
141
|
+
* same range; each page checks current permissions. Pages are live reads. */
|
|
142
|
+
export declare function usageExport(t: Transport): (query: UsageExportQuery, options?: RequestOptions) => Promise<UsageExportPage>;
|
|
99
143
|
/** `sbx.metrics()`: this sandbox's measured CPU and memory. */
|
|
100
144
|
export declare function sandboxMetrics(t: Transport, sandbox: Sandbox): (options?: {
|
|
101
145
|
range?: MetricRange;
|