withruntime 0.11.0 → 0.11.2
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 +8 -9
- package/DAYTONA.md +8 -9
- package/E2B.md +63 -21
- package/README.md +6 -5
- package/README.src.md +6 -5
- package/VERCEL.md +3 -2
- package/compatibility-lock.json +69 -14
- package/dist/api-defaults.d.ts +1 -1
- package/dist/api-defaults.js +1 -1
- package/dist/blaxel/errors.js +4 -4
- package/dist/blaxel/lookup.d.ts +2 -1
- package/dist/blaxel/lookup.js +2 -1
- package/dist/blaxel/sandbox.d.ts +8 -7
- package/dist/blaxel/sandbox.js +16 -12
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +99 -27
- package/dist/client.d.ts +16 -60
- package/dist/client.js +14 -103
- package/dist/compat/files.js +5 -1
- package/dist/compat/lease.d.ts +1 -0
- package/dist/compat/lease.js +14 -8
- package/dist/credentials.d.ts +9 -0
- package/dist/credentials.js +26 -4
- package/dist/daytona/daytona.d.ts +1 -2
- package/dist/daytona/daytona.js +30 -14
- package/dist/daytona/sandbox.d.ts +9 -4
- package/dist/daytona/sandbox.js +24 -5
- package/dist/e2b/client.d.ts +15 -2
- package/dist/e2b/client.js +61 -7
- package/dist/e2b/commands.d.ts +3 -0
- package/dist/e2b/commands.js +16 -5
- package/dist/e2b/errors.d.ts +26 -2
- package/dist/e2b/errors.js +38 -5
- package/dist/e2b/filesystem.d.ts +1 -1
- package/dist/e2b/filesystem.js +82 -16
- package/dist/e2b/index.d.ts +2 -2
- package/dist/e2b/index.js +2 -2
- package/dist/e2b/sandbox.d.ts +25 -12
- package/dist/e2b/sandbox.js +196 -165
- package/dist/e2b/unsupported.d.ts +19 -0
- package/dist/e2b/unsupported.js +21 -0
- package/dist/e2b/users.d.ts +9 -0
- package/dist/e2b/users.js +22 -0
- package/dist/errors.d.ts +10 -4
- package/dist/errors.js +18 -10
- package/dist/identity.js +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/machine.d.ts +180 -0
- package/dist/machine.js +504 -0
- package/dist/modal/index.js +6 -2
- package/dist/network-cli.js +7 -7
- package/dist/openai-agents/index.d.ts +1 -1
- package/dist/openai-agents/index.js +1 -1
- package/dist/products/browser.d.ts +2 -1
- package/dist/products/browser.js +9 -19
- package/dist/products/desktop.d.ts +2 -1
- package/dist/products/desktop.js +5 -30
- package/dist/products/jobs.d.ts +6 -5
- package/dist/products/jobs.js +6 -3
- package/dist/products/limits.d.ts +4 -10
- package/dist/products/mcp.d.ts +5 -1
- package/dist/products/mcp.js +29 -8
- package/dist/products/network-products.d.ts +1 -1
- package/dist/products/network-products.js +1 -1
- package/dist/products/network.d.ts +1 -1
- package/dist/products/observability.d.ts +1 -1
- package/dist/products/private-network.d.ts +1 -1
- package/dist/products/secrets.d.ts +3 -3
- package/dist/products/switching.d.ts +2 -3
- package/dist/runloop/index.js +6 -2
- package/dist/sandbox.d.ts +7 -84
- package/dist/sandbox.js +58 -345
- package/dist/tar.d.ts +2 -0
- package/dist/tar.js +74 -16
- package/dist/transport.d.ts +36 -8
- package/dist/transport.js +174 -37
- package/dist/types.d.ts +102 -12
- package/dist/vercel/filesystem.js +3 -1
- package/dist/vercel/sandbox.d.ts +18 -12
- package/dist/vercel/sandbox.js +27 -15
- package/dist/wait.d.ts +12 -0
- package/dist/wait.js +47 -0
- package/package.json +1 -1
package/dist/compat/lease.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { RuntimeError } from "../errors.js";
|
|
2
|
-
/* A rival's sandbox may be asked to live longer than one Runtime
|
|
3
|
-
hour at most, ARCHITECTURE.md section 10): Vercel's `timeout` over an
|
|
4
|
-
Daytona's `autoStopInterval` over an hour
|
|
5
|
-
reaching toward the time asked for while their sandbox object lives,
|
|
6
|
-
extension at a time, never past that time.
|
|
2
|
+
/* A rival's sandbox may be asked to live longer than one Runtime time limit
|
|
3
|
+
(an hour at most, ARCHITECTURE.md section 10): Vercel's `timeout` over an
|
|
4
|
+
hour, Daytona's `autoStopInterval` over an hour. The drop-ins keep the
|
|
5
|
+
limit reaching toward the time asked for while their sandbox object lives,
|
|
6
|
+
one extension at a time, never past that time. A sandbox created with no
|
|
7
|
+
time limit renews itself on the server (0300; `endsAt` null) and is left
|
|
8
|
+
alone. */
|
|
7
9
|
/** The most one lease runs ahead of now. */
|
|
8
10
|
export const LEASE_MAX_SECONDS = 3600;
|
|
9
11
|
/** Whole seconds to add to a lease ending at `expiresAt` (epoch ms) so it
|
|
@@ -39,7 +41,10 @@ export class LeaseKeeper {
|
|
|
39
41
|
#schedule() {
|
|
40
42
|
if (this.#ended || this.#timer)
|
|
41
43
|
return;
|
|
42
|
-
const
|
|
44
|
+
const info = this.#sandbox().info;
|
|
45
|
+
if (info.endsAt === null)
|
|
46
|
+
return;
|
|
47
|
+
const expiresAt = Date.parse(info.endsAt ?? info.expiresAt);
|
|
43
48
|
if (!Number.isFinite(expiresAt) || this.#until() <= expiresAt)
|
|
44
49
|
return;
|
|
45
50
|
this.#timer = setTimeout(() => {
|
|
@@ -61,9 +66,10 @@ export class LeaseKeeper {
|
|
|
61
66
|
}
|
|
62
67
|
async #extend(now) {
|
|
63
68
|
const sandbox = this.#sandbox();
|
|
64
|
-
|
|
69
|
+
// No time limit: the server renews it, and an extension would change nothing.
|
|
70
|
+
if (sandbox.state !== "running" || sandbox.info.endsAt === null)
|
|
65
71
|
return;
|
|
66
|
-
const seconds = extensionSeconds(Date.parse(sandbox.info.expiresAt), this.#until(), Date.now(), now ? Infinity : this.#marginMs());
|
|
72
|
+
const seconds = extensionSeconds(Date.parse(sandbox.info.endsAt ?? sandbox.info.expiresAt), this.#until(), Date.now(), now ? Infinity : this.#marginMs());
|
|
67
73
|
if (seconds < 1)
|
|
68
74
|
return;
|
|
69
75
|
try {
|
package/dist/credentials.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ export type SavedConnection = {
|
|
|
10
10
|
/** The account's name when it was saved, for `runtime account`. */
|
|
11
11
|
orgName?: string;
|
|
12
12
|
};
|
|
13
|
+
/** Saved accounts could not be updated because another command holds their
|
|
14
|
+
* lock: still `busy` after the whole wait, or `interrupted`, left by a command
|
|
15
|
+
* that died holding it. The message names the lock file to remove. */
|
|
16
|
+
export declare class CredentialsLockedError extends Error {
|
|
17
|
+
readonly lockFile: string;
|
|
18
|
+
readonly reason: "busy" | "interrupted";
|
|
19
|
+
readonly name = "CredentialsLockedError";
|
|
20
|
+
constructor(message: string, lockFile: string, reason: "busy" | "interrupted");
|
|
21
|
+
}
|
|
13
22
|
export declare function connectionOrigins(env: NodeJS.ProcessEnv): {
|
|
14
23
|
api: string;
|
|
15
24
|
auth: string;
|
package/dist/credentials.js
CHANGED
|
@@ -4,6 +4,23 @@ import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { isAbsolute, join } from "node:path";
|
|
6
6
|
import { apiOrigin, missingKey } from "./transport.js";
|
|
7
|
+
/** Saved accounts could not be updated because another command holds their
|
|
8
|
+
* lock: still `busy` after the whole wait, or `interrupted`, left by a command
|
|
9
|
+
* that died holding it. The message names the lock file to remove. */
|
|
10
|
+
export class CredentialsLockedError extends Error {
|
|
11
|
+
lockFile;
|
|
12
|
+
reason;
|
|
13
|
+
name = "CredentialsLockedError";
|
|
14
|
+
constructor(message, lockFile, reason) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.lockFile = lockFile;
|
|
17
|
+
this.reason = reason;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** How long a save waits for another command's update to finish. An update
|
|
21
|
+
* is one read, modify and write of small files, so only a heavily loaded
|
|
22
|
+
* machine comes near it. */
|
|
23
|
+
const LOCK_WAIT_MS = 30_000;
|
|
7
24
|
/** How many other accounts' connections one machine keeps beside the one in use. */
|
|
8
25
|
const SAVED_ACCOUNTS = 20;
|
|
9
26
|
export function connectionOrigins(env) {
|
|
@@ -107,7 +124,7 @@ export function connectionStore(env, directory = defaultDirectory(env)) {
|
|
|
107
124
|
* Never steal a lock: stale cleanup can otherwise delete a new live lock. */
|
|
108
125
|
async function updateAccounts(update) {
|
|
109
126
|
await privateDirectory(true);
|
|
110
|
-
const until = performance.now() +
|
|
127
|
+
const until = performance.now() + LOCK_WAIT_MS;
|
|
111
128
|
let handle;
|
|
112
129
|
for (;;) {
|
|
113
130
|
try {
|
|
@@ -127,16 +144,21 @@ export function connectionStore(env, directory = defaultDirectory(env)) {
|
|
|
127
144
|
// The holder may not have finished writing its process id yet.
|
|
128
145
|
}
|
|
129
146
|
if (typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
|
|
147
|
+
let gone = false;
|
|
130
148
|
try {
|
|
131
149
|
process.kill(pid, 0);
|
|
132
150
|
}
|
|
133
151
|
catch (error) {
|
|
134
|
-
|
|
135
|
-
throw new Error(`A previous Runtime credential update was interrupted. Remove ${lockFile} and retry.`);
|
|
152
|
+
gone = error.code === "ESRCH";
|
|
136
153
|
}
|
|
154
|
+
// A holder that finished between our read and our check has exited
|
|
155
|
+
// too, and its lock is gone or another's: wait for the lock again.
|
|
156
|
+
// Only a lock still naming the dead process was left by one that died.
|
|
157
|
+
if (gone && (await readPrivate(lockFile, 1024, "credential update lock")) === text)
|
|
158
|
+
throw new CredentialsLockedError(`A previous Runtime credential update was interrupted. Remove ${lockFile} and retry.`, lockFile, "interrupted");
|
|
137
159
|
}
|
|
138
160
|
if (performance.now() >= until)
|
|
139
|
-
throw new
|
|
161
|
+
throw new CredentialsLockedError(`Another Runtime command is updating saved accounts. Retry when it finishes. If no command is running, remove ${lockFile} and retry.`, lockFile, "busy");
|
|
140
162
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
141
163
|
}
|
|
142
164
|
try {
|
|
@@ -177,8 +177,7 @@ export declare class Daytona implements AsyncDisposable {
|
|
|
177
177
|
[Symbol.asyncDispose](): Promise<void>;
|
|
178
178
|
/** Creates a sandbox and waits until it runs, with Daytona's defaults: 1
|
|
179
179
|
* vCPU, 1 GiB, 3 GiB disk, pausing after 15 minutes without calls. Funding
|
|
180
|
-
* is left to Runtime: the
|
|
181
|
-
* prepaid credit, exactly as withruntime's own create. */
|
|
180
|
+
* is left to Runtime: the included usage first, then prepaid credit, exactly as withruntime's own create. */
|
|
182
181
|
create(params?: CreateSandboxFromSnapshotParams | CreateSandboxFromImageParams, options?: {
|
|
183
182
|
timeout?: number;
|
|
184
183
|
onSnapshotCreateLogs?: (chunk: string) => void;
|
package/dist/daytona/daytona.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { clientFor, } from "./client.js";
|
|
2
2
|
import { DaytonaError, DaytonaNotFoundError, guard, NotSupportedError } from "./errors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { LEASE_MAX_SECONDS } from "../compat/lease.js";
|
|
4
|
+
import { idlePauseOf, idleSeconds, networkRules, notFound, retentionDays, Sandbox, windowSeconds, } from "./sandbox.js";
|
|
4
5
|
/* Daytona's client over Runtime's. `new Daytona()` then `daytona.create()`
|
|
5
6
|
makes a Runtime sandbox with Daytona's defaults. */
|
|
6
7
|
/** Daytona's default machine: 1 vCPU, 1 GiB memory, 3 GiB disk (checked
|
|
@@ -182,8 +183,7 @@ export class Daytona {
|
|
|
182
183
|
async [Symbol.asyncDispose]() { }
|
|
183
184
|
/** Creates a sandbox and waits until it runs, with Daytona's defaults: 1
|
|
184
185
|
* vCPU, 1 GiB, 3 GiB disk, pausing after 15 minutes without calls. Funding
|
|
185
|
-
* is left to Runtime: the
|
|
186
|
-
* prepaid credit, exactly as withruntime's own create. */
|
|
186
|
+
* is left to Runtime: the included usage first, then prepaid credit, exactly as withruntime's own create. */
|
|
187
187
|
async create(params = {}, options = {}) {
|
|
188
188
|
refuseCreate(params);
|
|
189
189
|
const client = params.withruntime?.client ?? this.#client;
|
|
@@ -211,9 +211,15 @@ export class Daytona {
|
|
|
211
211
|
memoryMiB: Math.round(resources.memory * 1024),
|
|
212
212
|
diskMiB: Math.round(resources.disk * 1024),
|
|
213
213
|
}),
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
// A time to live is a time limit, kept by the lease. Without one there
|
|
215
|
+
// is none: it runs while it works and autoStopInterval is its idle
|
|
216
|
+
// pause, counted by the sandbox itself, so a long command is never
|
|
217
|
+
// frozen for want of calls from this client (0300).
|
|
218
|
+
...(lifecycle.deadline
|
|
219
|
+
? {
|
|
220
|
+
timeoutSeconds: Math.max(60, Math.min(lifecycle.windowSeconds, Math.floor((lifecycle.deadline - Date.now()) / 1000))),
|
|
221
|
+
}
|
|
222
|
+
: { idlePauseSeconds: idlePauseOf(lifecycle.autoStopInterval) }),
|
|
217
223
|
onLeaseEnd: lifecycle.ephemeral || lifecycle.deadline ? "stop" : "pause",
|
|
218
224
|
...(params.name ? { name: params.name } : {}),
|
|
219
225
|
...(labels ? { labels } : {}),
|
|
@@ -303,14 +309,24 @@ export class Daytona {
|
|
|
303
309
|
language: runtime.info.labels[LANGUAGE_LABEL] ?? "python",
|
|
304
310
|
public: false,
|
|
305
311
|
...(user ? { user } : {}),
|
|
306
|
-
lifecycle:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
lifecycle: runtime.info.timeoutSeconds
|
|
313
|
+
? {
|
|
314
|
+
windowSeconds: runtime.info.timeoutSeconds,
|
|
315
|
+
idleSeconds: runtime.info.timeoutSeconds,
|
|
316
|
+
ephemeral: onLeaseEnd === "stop",
|
|
317
|
+
autoStopInterval: Math.round(runtime.info.timeoutSeconds / 60),
|
|
318
|
+
autoArchiveInterval: 0,
|
|
319
|
+
autoDeleteInterval: -1,
|
|
320
|
+
}
|
|
321
|
+
: {
|
|
322
|
+
// No time limit: its idle pause is its autoStopInterval.
|
|
323
|
+
windowSeconds: LEASE_MAX_SECONDS,
|
|
324
|
+
idleSeconds: Infinity,
|
|
325
|
+
ephemeral: onLeaseEnd === "stop",
|
|
326
|
+
autoStopInterval: Math.round((runtime.info.idlePauseSeconds ?? 0) / 60),
|
|
327
|
+
autoArchiveInterval: 0,
|
|
328
|
+
autoDeleteInterval: -1,
|
|
329
|
+
},
|
|
314
330
|
});
|
|
315
331
|
}
|
|
316
332
|
/** Live and stopped (paused) sandboxes, oldest first. Await it for the old
|
|
@@ -125,7 +125,7 @@ export declare class Sandbox {
|
|
|
125
125
|
/** Pauses the sandbox with its memory. */
|
|
126
126
|
pause(_timeout?: number): Promise<void>;
|
|
127
127
|
/** Runtime has no archive tier: a stopped sandbox stays paused, kept for
|
|
128
|
-
* its retention (30 days paid, 7
|
|
128
|
+
* its retention (30 days paid, 7 without credit, or autoDeleteInterval). */
|
|
129
129
|
archive(): Promise<void>;
|
|
130
130
|
/** Ends the sandbox for good. */
|
|
131
131
|
delete(_timeout?: number): Promise<void>;
|
|
@@ -134,8 +134,9 @@ export declare class Sandbox {
|
|
|
134
134
|
refreshData(): Promise<void>;
|
|
135
135
|
/** Counts as activity: moves the lease a full autoStop window on. */
|
|
136
136
|
refreshActivity(): Promise<void>;
|
|
137
|
-
/** The sandbox pauses after `interval` minutes
|
|
138
|
-
*
|
|
137
|
+
/** The sandbox pauses after `interval` minutes with nothing happening in
|
|
138
|
+
* it; 0 never. With a time to live, after that long without a call from
|
|
139
|
+
* this client, and past an hour only while this object lives. */
|
|
139
140
|
setAutostopInterval(interval: number): Promise<void>;
|
|
140
141
|
setAutoPauseInterval(interval: number): Promise<void>;
|
|
141
142
|
/** Runtime has no archive tier; recorded only. */
|
|
@@ -192,8 +193,12 @@ export declare class Sandbox {
|
|
|
192
193
|
}
|
|
193
194
|
/** Daytona's autoStopInterval (minutes; 0 is off) as the lease window. */
|
|
194
195
|
export declare function windowSeconds(autoStopMinutes: number): number;
|
|
196
|
+
/** Daytona's autoStopInterval (minutes; 0 is never) as the idle pause of a
|
|
197
|
+
* sandbox with no time limit: seconds with nothing happening in it, counted
|
|
198
|
+
* by the sandbox itself (`idlePauseSeconds`, 0 never, at most a day). */
|
|
199
|
+
export declare function idlePauseOf(autoStopMinutes: number): number;
|
|
195
200
|
/** Daytona's autoStopInterval (minutes; 0 is never) as seconds without a
|
|
196
|
-
* call before
|
|
201
|
+
* call before a sandbox with a time to live pauses. */
|
|
197
202
|
export declare function idleSeconds(autoStopMinutes: number): number;
|
|
198
203
|
/** Minutes as Runtime's retention: whole days, 1 to 365. */
|
|
199
204
|
export declare function retentionDays(minutes: number): number;
|
package/dist/daytona/sandbox.js
CHANGED
|
@@ -238,7 +238,11 @@ export class Sandbox {
|
|
|
238
238
|
if (state === "stopped" || state === "stopping")
|
|
239
239
|
throw new DaytonaError(`Sandbox ${this.id} was deleted and cannot start.`, 410);
|
|
240
240
|
if (state === "paused" || state === "pausing")
|
|
241
|
-
await guard("sandbox", () =>
|
|
241
|
+
await guard("sandbox", () =>
|
|
242
|
+
// One with no time limit (timeoutSeconds 0) wakes with none.
|
|
243
|
+
this.#rt.wake(this.#rt.info.timeoutSeconds
|
|
244
|
+
? { timeoutSeconds: this.#options.lifecycle.windowSeconds }
|
|
245
|
+
: {}));
|
|
242
246
|
this.#pausedByPause = false;
|
|
243
247
|
}
|
|
244
248
|
/** Stops the sandbox: Runtime pauses it, keeping its files and memory, so
|
|
@@ -265,7 +269,7 @@ export class Sandbox {
|
|
|
265
269
|
this.#pausedByPause = true;
|
|
266
270
|
}
|
|
267
271
|
/** Runtime has no archive tier: a stopped sandbox stays paused, kept for
|
|
268
|
-
* its retention (30 days paid, 7
|
|
272
|
+
* its retention (30 days paid, 7 without credit, or autoDeleteInterval). */
|
|
269
273
|
async archive() {
|
|
270
274
|
await this.stop();
|
|
271
275
|
}
|
|
@@ -291,11 +295,16 @@ export class Sandbox {
|
|
|
291
295
|
async refreshActivity() {
|
|
292
296
|
await guard("sandbox", () => this.#live(true));
|
|
293
297
|
}
|
|
294
|
-
/** The sandbox pauses after `interval` minutes
|
|
295
|
-
*
|
|
298
|
+
/** The sandbox pauses after `interval` minutes with nothing happening in
|
|
299
|
+
* it; 0 never. With a time to live, after that long without a call from
|
|
300
|
+
* this client, and past an hour only while this object lives. */
|
|
296
301
|
async setAutostopInterval(interval) {
|
|
297
302
|
const lifecycle = this.#options.lifecycle;
|
|
298
303
|
lifecycle.autoStopInterval = interval;
|
|
304
|
+
if (!this.#rt.info.timeoutSeconds) {
|
|
305
|
+
await guard("sandbox", () => this.#rt.update({ idlePauseSeconds: idlePauseOf(interval) }));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
299
308
|
lifecycle.windowSeconds = windowSeconds(interval);
|
|
300
309
|
lifecycle.idleSeconds = idleSeconds(interval);
|
|
301
310
|
await guard("sandbox", () => this.#live());
|
|
@@ -321,6 +330,8 @@ export class Sandbox {
|
|
|
321
330
|
delete this.#options.lifecycle.deadline;
|
|
322
331
|
return;
|
|
323
332
|
}
|
|
333
|
+
if (!this.#rt.info.timeoutSeconds)
|
|
334
|
+
throw new NotSupportedError("A time to live on a sandbox made without one", "It has no time limit and pauses when idle; pass ttlMinutes to create(), or call stop() or delete() when the work is done.");
|
|
324
335
|
const deadline = Date.parse(this.#rt.info.createdAt) + ttlMinutes * 60_000;
|
|
325
336
|
if (Date.parse(this.#rt.info.expiresAt) > deadline)
|
|
326
337
|
throw new NotSupportedError("A time to live shorter than the current lease", "Runtime leases only move later; call stop() or delete() when the work is done.");
|
|
@@ -444,8 +455,16 @@ export function windowSeconds(autoStopMinutes) {
|
|
|
444
455
|
return 3600;
|
|
445
456
|
return Math.min(3600, Math.max(60, Math.round(autoStopMinutes * 60)));
|
|
446
457
|
}
|
|
458
|
+
/** Daytona's autoStopInterval (minutes; 0 is never) as the idle pause of a
|
|
459
|
+
* sandbox with no time limit: seconds with nothing happening in it, counted
|
|
460
|
+
* by the sandbox itself (`idlePauseSeconds`, 0 never, at most a day). */
|
|
461
|
+
export function idlePauseOf(autoStopMinutes) {
|
|
462
|
+
if (autoStopMinutes <= 0)
|
|
463
|
+
return 0;
|
|
464
|
+
return Math.min(86_400, Math.max(60, Math.round(autoStopMinutes * 60)));
|
|
465
|
+
}
|
|
447
466
|
/** Daytona's autoStopInterval (minutes; 0 is never) as seconds without a
|
|
448
|
-
* call before
|
|
467
|
+
* call before a sandbox with a time to live pauses. */
|
|
449
468
|
export function idleSeconds(autoStopMinutes) {
|
|
450
469
|
if (autoStopMinutes <= 0)
|
|
451
470
|
return Infinity;
|
package/dist/e2b/client.d.ts
CHANGED
|
@@ -43,15 +43,28 @@ export interface RuntimeOpts {
|
|
|
43
43
|
/** Refuses the E2B-only connection options: they point at E2B, and quietly
|
|
44
44
|
* dropping one would send the call somewhere its author did not mean. */
|
|
45
45
|
export declare function refuseE2BConnection(opts?: ConnectionOpts): void;
|
|
46
|
+
/** Connections the adapter's own client may hold at once: E2B's SDK has no
|
|
47
|
+
* cap of its own, so an orchestrator that drives a thousand sandboxes from one
|
|
48
|
+
* process runs every command at once. 4,096 is the widest room Runtime's edge
|
|
49
|
+
* gives an address that has used a valid key (api.md, "Limits"); the edge and
|
|
50
|
+
* the API answer anything past an account's room with a retry, which the
|
|
51
|
+
* client waits out. Runtime's own SDK keeps 48. */
|
|
52
|
+
export declare const E2B_MAX_CONNECTIONS = 4096;
|
|
53
|
+
/** Said once per process, the first time a call has to wait for a
|
|
54
|
+
* connection: E2B never holds a call back, so a caller should hear it. */
|
|
55
|
+
export declare function warnQueued(maxConnections: number): void;
|
|
46
56
|
/** The Runtime client for these options: an explicit client, else one per key,
|
|
47
57
|
* made once and reused so its connections stay open. */
|
|
48
58
|
export declare function clientFor(opts?: ConnectionOpts & {
|
|
49
59
|
runtime?: RuntimeOpts;
|
|
50
60
|
}): Runtime;
|
|
51
61
|
/** Explicit key, then RUNTIME_API_KEY, then E2B_API_KEY when it holds a
|
|
52
|
-
* Runtime key; undefined lets the Runtime SDK find the
|
|
62
|
+
* Runtime key; undefined, when none is set, lets the Runtime SDK find the
|
|
63
|
+
* saved login. An E2B key is refused, never sent. */
|
|
53
64
|
export declare function pickKey(explicit?: string): string | undefined;
|
|
54
|
-
/**
|
|
65
|
+
/** Where the key in use came from, as an error names it. */
|
|
66
|
+
export declare function keySource(explicit?: string): string;
|
|
67
|
+
/** Test hook: forget the cached clients and the warnings. */
|
|
55
68
|
export declare function resetClients(): void;
|
|
56
69
|
/** Request options for a Runtime call from E2B's. */
|
|
57
70
|
export declare function request(opts?: {
|
package/dist/e2b/client.js
CHANGED
|
@@ -26,6 +26,23 @@ export function refuseE2BConnection(opts = {}) {
|
|
|
26
26
|
throw new NotSupportedError("E2B's debug mode (a local envd)", "Remove debug: true; there is no local Runtime guest to connect to.");
|
|
27
27
|
}
|
|
28
28
|
const clients = new Map();
|
|
29
|
+
/** Connections the adapter's own client may hold at once: E2B's SDK has no
|
|
30
|
+
* cap of its own, so an orchestrator that drives a thousand sandboxes from one
|
|
31
|
+
* process runs every command at once. 4,096 is the widest room Runtime's edge
|
|
32
|
+
* gives an address that has used a valid key (api.md, "Limits"); the edge and
|
|
33
|
+
* the API answer anything past an account's room with a retry, which the
|
|
34
|
+
* client waits out. Runtime's own SDK keeps 48. */
|
|
35
|
+
export const E2B_MAX_CONNECTIONS = 4096;
|
|
36
|
+
let warnedQueued = false;
|
|
37
|
+
/** Said once per process, the first time a call has to wait for a
|
|
38
|
+
* connection: E2B never holds a call back, so a caller should hear it. */
|
|
39
|
+
export function warnQueued(maxConnections) {
|
|
40
|
+
if (warnedQueued || typeof process === "undefined" || !process.emitWarning)
|
|
41
|
+
return;
|
|
42
|
+
warnedQueued = true;
|
|
43
|
+
process.emitWarning(`More than ${maxConnections} calls and command streams are open at once from this process, so the rest wait their turn. ` +
|
|
44
|
+
"Pass runtime: { client: new Runtime({ maxConnections }) } for more.", { code: "RUNTIME_E2B_QUEUED" });
|
|
45
|
+
}
|
|
29
46
|
/** The Runtime client for these options: an explicit client, else one per key,
|
|
30
47
|
* made once and reused so its connections stay open. */
|
|
31
48
|
export function clientFor(opts = {}) {
|
|
@@ -36,30 +53,67 @@ export function clientFor(opts = {}) {
|
|
|
36
53
|
const cacheKey = key ?? "";
|
|
37
54
|
let client = clients.get(cacheKey);
|
|
38
55
|
if (!client) {
|
|
39
|
-
client = new Runtime(
|
|
56
|
+
client = new Runtime({
|
|
57
|
+
...(key ? { apiKey: key } : {}),
|
|
58
|
+
maxConnections: E2B_MAX_CONNECTIONS,
|
|
59
|
+
onQueued: warnQueued,
|
|
60
|
+
});
|
|
40
61
|
clients.set(cacheKey, client);
|
|
41
62
|
}
|
|
42
63
|
return client;
|
|
43
64
|
}
|
|
44
65
|
/** Explicit key, then RUNTIME_API_KEY, then E2B_API_KEY when it holds a
|
|
45
|
-
* Runtime key; undefined lets the Runtime SDK find the
|
|
66
|
+
* Runtime key; undefined, when none is set, lets the Runtime SDK find the
|
|
67
|
+
* saved login. An E2B key is refused, never sent. */
|
|
46
68
|
export function pickKey(explicit) {
|
|
47
69
|
const runtimeKey = env("RUNTIME_API_KEY");
|
|
48
70
|
if (explicit !== undefined && !isE2BKey(explicit))
|
|
49
71
|
return explicit;
|
|
50
|
-
if (runtimeKey)
|
|
72
|
+
if (runtimeKey) {
|
|
73
|
+
warnTwoKeys(runtimeKey, env("E2B_API_KEY"));
|
|
51
74
|
return runtimeKey;
|
|
75
|
+
}
|
|
52
76
|
if (explicit !== undefined)
|
|
53
77
|
throw new AuthenticationError("The apiKey passed is an E2B API key (e2b_...), and it was not sent. Set RUNTIME_API_KEY to a Runtime key " +
|
|
54
78
|
"(https://withruntime.com/account/keys, or run `npx withruntime login`), or pass the Runtime key as apiKey.");
|
|
55
|
-
// An E2B key left in E2B_API_KEY is
|
|
56
|
-
//
|
|
79
|
+
// An E2B key left in E2B_API_KEY is never sent, and never quietly passed
|
|
80
|
+
// over for the key `npx withruntime login` saved, which may be another
|
|
81
|
+
// account's.
|
|
57
82
|
const e2bVariable = env("E2B_API_KEY");
|
|
58
|
-
|
|
83
|
+
if (e2bVariable && isE2BKey(e2bVariable))
|
|
84
|
+
throw new AuthenticationError("E2B_API_KEY holds an E2B API key (e2b_...), and it was not sent. Set RUNTIME_API_KEY to a Runtime key " +
|
|
85
|
+
"(https://withruntime.com/account/keys, or run `npx withruntime login`), or put the Runtime key in E2B_API_KEY.");
|
|
86
|
+
return e2bVariable || undefined;
|
|
87
|
+
}
|
|
88
|
+
/** Where the key in use came from, as an error names it. */
|
|
89
|
+
export function keySource(explicit) {
|
|
90
|
+
if (explicit !== undefined && !isE2BKey(explicit))
|
|
91
|
+
return "apiKey";
|
|
92
|
+
if (env("RUNTIME_API_KEY"))
|
|
93
|
+
return "RUNTIME_API_KEY";
|
|
94
|
+
if (env("E2B_API_KEY"))
|
|
95
|
+
return "E2B_API_KEY";
|
|
96
|
+
return "the saved login";
|
|
97
|
+
}
|
|
98
|
+
let warnedTwoKeys = false;
|
|
99
|
+
/** Said once per process: RUNTIME_API_KEY wins, as in Runtime's own SDK, but
|
|
100
|
+
* code written for E2B means E2B_API_KEY, so two different Runtime keys are
|
|
101
|
+
* likely two accounts, and the wrong one fails later as a missing image
|
|
102
|
+
* (5 October 2026). */
|
|
103
|
+
function warnTwoKeys(runtimeKey, e2bVariable) {
|
|
104
|
+
if (!e2bVariable || isE2BKey(e2bVariable) || e2bVariable === runtimeKey)
|
|
105
|
+
return;
|
|
106
|
+
if (warnedTwoKeys || typeof process === "undefined" || !process.emitWarning)
|
|
107
|
+
return;
|
|
108
|
+
warnedTwoKeys = true;
|
|
109
|
+
process.emitWarning("RUNTIME_API_KEY and E2B_API_KEY hold different Runtime keys, which may be different accounts; this package uses " +
|
|
110
|
+
"RUNTIME_API_KEY. Unset one, or set both to the same key.", { code: "RUNTIME_E2B_TWO_KEYS" });
|
|
59
111
|
}
|
|
60
|
-
/** Test hook: forget the cached clients. */
|
|
112
|
+
/** Test hook: forget the cached clients and the warnings. */
|
|
61
113
|
export function resetClients() {
|
|
62
114
|
clients.clear();
|
|
115
|
+
warnedQueued = false;
|
|
116
|
+
warnedTwoKeys = false;
|
|
63
117
|
}
|
|
64
118
|
/** Request options for a Runtime call from E2B's. */
|
|
65
119
|
export function request(opts = {}) {
|
package/dist/e2b/commands.d.ts
CHANGED
|
@@ -43,6 +43,9 @@ export interface SandboxContext {
|
|
|
43
43
|
/** Makes /home/user (E2B's home) lead to /workspace (Runtime's), once, when
|
|
44
44
|
* something names it. */
|
|
45
45
|
ensureHome(text: string | undefined, options?: RequestOptions): Promise<void>;
|
|
46
|
+
/** Whether /home/user leads to /workspace, linking it first if nothing is
|
|
47
|
+
* there; false when that cannot be told. Never fails. */
|
|
48
|
+
homeIsWorkspace(): Promise<boolean>;
|
|
46
49
|
}
|
|
47
50
|
/** E2B's pids are numbers; Runtime's process ids are strings. The number is a
|
|
48
51
|
* 31-bit FNV-1a hash of the id, so every client derives the same one. */
|
package/dist/e2b/commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CommandExitError, guard, InvalidArgumentError, SandboxError, TimeoutError, translate, } from "./errors.js";
|
|
2
|
-
import { commandAs, runAs } from "./users.js";
|
|
1
|
+
import { CommandExitError, guard, InvalidArgumentError, SandboxError, SandboxNotFoundError, TimeoutError, translate, } from "./errors.js";
|
|
2
|
+
import { commandAs, listedAs, runAs } from "./users.js";
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
4
4
|
/** How long a command may run on Runtime: a day, E2B's longest sandbox. E2B's
|
|
5
5
|
* own `timeoutMs` bounds only the connection, so the process gets this. */
|
|
@@ -238,12 +238,19 @@ export class Commands {
|
|
|
238
238
|
function describe(info) {
|
|
239
239
|
return {
|
|
240
240
|
pid: pidOf(info.id),
|
|
241
|
-
|
|
242
|
-
args: ["-c", info.command],
|
|
241
|
+
...listedAs(info.command),
|
|
243
242
|
envs: {},
|
|
244
243
|
cwd: info.cwd,
|
|
245
244
|
};
|
|
246
245
|
}
|
|
246
|
+
/** A command whose sandbox went away under it, as E2B says it: a
|
|
247
|
+
* TimeoutError, which code written for E2B catches (5 October 2026: it was a
|
|
248
|
+
* SandboxNotFoundError). */
|
|
249
|
+
function sandboxEnded(cause) {
|
|
250
|
+
const error = new TimeoutError("The command ended before the stream completed: the sandbox was killed or reached its end of life.");
|
|
251
|
+
error.cause = cause;
|
|
252
|
+
return error;
|
|
253
|
+
}
|
|
247
254
|
/** Reader cancellation also releases a callback whose promise never settles. */
|
|
248
255
|
async function deliver(signal, callback) {
|
|
249
256
|
signal.throwIfAborted();
|
|
@@ -349,7 +356,11 @@ export class CommandHandle {
|
|
|
349
356
|
if (!this.#disconnected && !this.#failure) {
|
|
350
357
|
const translated = translate(error, "sandbox");
|
|
351
358
|
this.#failure =
|
|
352
|
-
translated instanceof
|
|
359
|
+
translated instanceof SandboxNotFoundError
|
|
360
|
+
? sandboxEnded(translated)
|
|
361
|
+
: translated instanceof Error
|
|
362
|
+
? translated
|
|
363
|
+
: new SandboxError(String(translated));
|
|
353
364
|
}
|
|
354
365
|
}
|
|
355
366
|
finally {
|
package/dist/e2b/errors.d.ts
CHANGED
|
@@ -37,6 +37,31 @@ export declare class ServiceBusyError extends Error {
|
|
|
37
37
|
requestId?: string;
|
|
38
38
|
constructor(message: string);
|
|
39
39
|
}
|
|
40
|
+
/** E2B's other error classes, by the same names and parents, so a module
|
|
41
|
+
* that imports or catches them loads and works: nothing in this package
|
|
42
|
+
* throws them, since the features behind them (template builds, git,
|
|
43
|
+
* volumes, secrets) answer NotSupportedError here. */
|
|
44
|
+
export declare class GitAuthError extends AuthenticationError {
|
|
45
|
+
}
|
|
46
|
+
export declare class GitUpstreamError extends SandboxError {
|
|
47
|
+
}
|
|
48
|
+
export declare class BuildError extends Error {
|
|
49
|
+
constructor(message?: string);
|
|
50
|
+
}
|
|
51
|
+
export declare class FileUploadError extends BuildError {
|
|
52
|
+
}
|
|
53
|
+
export declare class VolumeError extends Error {
|
|
54
|
+
constructor(message?: string);
|
|
55
|
+
}
|
|
56
|
+
export declare class VolumeNotFoundError extends VolumeError {
|
|
57
|
+
}
|
|
58
|
+
export declare class VolumePathNotFoundError extends VolumeError {
|
|
59
|
+
}
|
|
60
|
+
export declare class SecretError extends Error {
|
|
61
|
+
constructor(message?: string);
|
|
62
|
+
}
|
|
63
|
+
export declare class SecretNotFoundError extends SecretError {
|
|
64
|
+
}
|
|
40
65
|
/** A call that E2B supports and Runtime does not, or not in the same way.
|
|
41
66
|
* Thrown before anything is done, never after doing something different.
|
|
42
67
|
* `feature` names what was asked for; `alternative` says what to use on
|
|
@@ -46,8 +71,7 @@ export declare class NotSupportedError extends SandboxError {
|
|
|
46
71
|
readonly alternative: string;
|
|
47
72
|
constructor(feature: string, alternative: string, message?: string);
|
|
48
73
|
}
|
|
49
|
-
/** Runtime's addition, for `getHost` and `getPublicHost`: a
|
|
50
|
-
* sandbox shares a port only privately, so a request needs the port's token,
|
|
74
|
+
/** Runtime's addition, for `getHost` and `getPublicHost`: a sandbox without credit shares a port only privately, so a request needs the port's token,
|
|
51
75
|
* and a host name alone cannot carry one. The message says what works. A
|
|
52
76
|
* NotSupportedError, so code that catches those catches this too. */
|
|
53
77
|
export declare class PublicPreviewNotAllowedError extends NotSupportedError {
|
package/dist/e2b/errors.js
CHANGED
|
@@ -58,6 +58,40 @@ export class ServiceBusyError extends Error {
|
|
|
58
58
|
this.name = new.target.name;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
/** E2B's other error classes, by the same names and parents, so a module
|
|
62
|
+
* that imports or catches them loads and works: nothing in this package
|
|
63
|
+
* throws them, since the features behind them (template builds, git,
|
|
64
|
+
* volumes, secrets) answer NotSupportedError here. */
|
|
65
|
+
export class GitAuthError extends AuthenticationError {
|
|
66
|
+
}
|
|
67
|
+
export class GitUpstreamError extends SandboxError {
|
|
68
|
+
}
|
|
69
|
+
export class BuildError extends Error {
|
|
70
|
+
constructor(message) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = new.target.name;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class FileUploadError extends BuildError {
|
|
76
|
+
}
|
|
77
|
+
export class VolumeError extends Error {
|
|
78
|
+
constructor(message) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = new.target.name;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export class VolumeNotFoundError extends VolumeError {
|
|
84
|
+
}
|
|
85
|
+
export class VolumePathNotFoundError extends VolumeError {
|
|
86
|
+
}
|
|
87
|
+
export class SecretError extends Error {
|
|
88
|
+
constructor(message) {
|
|
89
|
+
super(message);
|
|
90
|
+
this.name = new.target.name;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export class SecretNotFoundError extends SecretError {
|
|
94
|
+
}
|
|
61
95
|
/** A call that E2B supports and Runtime does not, or not in the same way.
|
|
62
96
|
* Thrown before anything is done, never after doing something different.
|
|
63
97
|
* `feature` names what was asked for; `alternative` says what to use on
|
|
@@ -72,19 +106,18 @@ export class NotSupportedError extends SandboxError {
|
|
|
72
106
|
this.code = "not_supported";
|
|
73
107
|
}
|
|
74
108
|
}
|
|
75
|
-
/** Runtime's addition, for `getHost` and `getPublicHost`: a
|
|
76
|
-
* sandbox shares a port only privately, so a request needs the port's token,
|
|
109
|
+
/** Runtime's addition, for `getHost` and `getPublicHost`: a sandbox without credit shares a port only privately, so a request needs the port's token,
|
|
77
110
|
* and a host name alone cannot carry one. The message says what works. A
|
|
78
111
|
* NotSupportedError, so code that catches those catches this too. */
|
|
79
112
|
export class PublicPreviewNotAllowedError extends NotSupportedError {
|
|
80
113
|
constructor(sandboxId, port, message) {
|
|
81
114
|
const where = port === undefined ? "<port>" : String(port);
|
|
82
|
-
const alternative = `For an address that works
|
|
115
|
+
const alternative = `For an address that works without credit, use (await sandbox.runtime.previews.create(${where})).urlWithToken: ` +
|
|
83
116
|
"it carries the token, in a browser, fetch or curl. For other paths on it, send the token as the " +
|
|
84
117
|
"x-runtime-preview-token header or the runtime_preview_token query parameter. A public host needs a paid " +
|
|
85
118
|
"sandbox, which is the account owner's decision.";
|
|
86
|
-
super("A public address on a
|
|
87
|
-
`Sandbox ${sandboxId} runs
|
|
119
|
+
super("A public address on a sandbox without credit", alternative, message ??
|
|
120
|
+
`Sandbox ${sandboxId} runs without credit, where a shared port is private: every request needs the ` +
|
|
88
121
|
`port's token, and a host name alone cannot carry one. ${alternative}`);
|
|
89
122
|
this.code = "public_preview_not_allowed";
|
|
90
123
|
}
|
package/dist/e2b/filesystem.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export interface FilesystemListOpts extends FilesystemRequestOpts {
|
|
|
72
72
|
}
|
|
73
73
|
/** `sandbox.files`: E2B's filesystem module over Runtime's files API. Relative
|
|
74
74
|
* paths resolve against the home directory, as in E2B (Runtime's is
|
|
75
|
-
* /workspace). */
|
|
75
|
+
* /workspace), and come back under /home/user, as E2B gives them. */
|
|
76
76
|
export declare class Filesystem {
|
|
77
77
|
#private;
|
|
78
78
|
constructor(ctx: SandboxContext);
|