withruntime 0.9.0 → 0.10.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 +13 -7
- package/E2B.md +12 -8
- package/README.md +15 -7
- package/README.src.md +15 -7
- package/VERCEL.md +13 -3
- package/compatibility-lock.json +84 -8
- 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 +189 -54
- package/dist/client.d.ts +29 -3
- package/dist/client.js +40 -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/credentials.d.ts +6 -1
- package/dist/credentials.js +107 -38
- package/dist/daytona/daytona.js +11 -4
- package/dist/daytona/filesystem.d.ts +17 -4
- package/dist/daytona/filesystem.js +121 -38
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/sandbox.d.ts +1 -1
- package/dist/daytona/sandbox.js +16 -7
- package/dist/e2b/client.js +1 -1
- package/dist/e2b/commands.d.ts +30 -8
- package/dist/e2b/commands.js +142 -54
- package/dist/e2b/index.d.ts +1 -0
- package/dist/e2b/index.js +1 -0
- package/dist/e2b/pty.d.ts +24 -0
- package/dist/e2b/pty.js +80 -0
- package/dist/e2b/sandbox.d.ts +2 -1
- package/dist/e2b/sandbox.js +49 -15
- 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 +2 -1
- package/dist/login.js +16 -10
- package/dist/modal/index.js +2 -1
- package/dist/network-cli.js +111 -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/index.d.ts +12 -0
- package/dist/products/index.js +10 -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/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 +37 -8
- package/dist/sandbox.js +432 -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/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/sandbox.d.ts +8 -8
- package/dist/vercel/sandbox.js +154 -59
- package/dist/vercel/snapshot.d.ts +3 -2
- package/dist/vercel/snapshot.js +9 -5
- package/package.json +2 -1
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() {
|
package/dist/products/index.d.ts
CHANGED
|
@@ -40,6 +40,16 @@ export declare const clientExtensions: {
|
|
|
40
40
|
tunnel: typeof tunnel;
|
|
41
41
|
network: typeof network;
|
|
42
42
|
};
|
|
43
|
+
/** Canonical CLI product names share the existing factory instance. Account
|
|
44
|
+
* commands such as secrets, events and billing retain their current names. */
|
|
45
|
+
export declare const clientProductAliases: {
|
|
46
|
+
readonly image: "images";
|
|
47
|
+
readonly volume: "volumes";
|
|
48
|
+
readonly job: "jobs";
|
|
49
|
+
readonly domain: "domains";
|
|
50
|
+
readonly port: "ports";
|
|
51
|
+
readonly address: "addresses";
|
|
52
|
+
};
|
|
43
53
|
export declare const sandboxExtensions: {
|
|
44
54
|
interpreter: typeof sandboxInterpreter;
|
|
45
55
|
previews: typeof sandboxPreviews;
|
|
@@ -52,6 +62,8 @@ export declare const sandboxExtensions: {
|
|
|
52
62
|
};
|
|
53
63
|
export type ClientExtensions = {
|
|
54
64
|
readonly [K in keyof typeof clientExtensions]: ReturnType<(typeof clientExtensions)[K]>;
|
|
65
|
+
} & {
|
|
66
|
+
readonly [K in keyof typeof clientProductAliases]: ReturnType<(typeof clientExtensions)[(typeof clientProductAliases)[K]]>;
|
|
55
67
|
};
|
|
56
68
|
export type SandboxExtensions = {
|
|
57
69
|
readonly [K in keyof typeof sandboxExtensions]: ReturnType<(typeof sandboxExtensions)[K]>;
|
package/dist/products/index.js
CHANGED
|
@@ -53,6 +53,16 @@ export const clientExtensions = {
|
|
|
53
53
|
tunnel,
|
|
54
54
|
network,
|
|
55
55
|
};
|
|
56
|
+
/** Canonical CLI product names share the existing factory instance. Account
|
|
57
|
+
* commands such as secrets, events and billing retain their current names. */
|
|
58
|
+
export const clientProductAliases = {
|
|
59
|
+
image: "images",
|
|
60
|
+
volume: "volumes",
|
|
61
|
+
job: "jobs",
|
|
62
|
+
domain: "domains",
|
|
63
|
+
port: "ports",
|
|
64
|
+
address: "addresses",
|
|
65
|
+
};
|
|
56
66
|
export const sandboxExtensions = {
|
|
57
67
|
interpreter: sandboxInterpreter,
|
|
58
68
|
previews: sandboxPreviews,
|
|
@@ -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;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** Top-level cross-product settled usage, selected by UTC settlement timestamps
|
|
2
|
+
* in [since, until). Pending holds are excluded. Follow nextCursor using the
|
|
3
|
+
* same range; each page checks current permissions. Pages are live reads. */
|
|
4
|
+
export function usageExport(t) {
|
|
5
|
+
return (query, options = {}) => t.json({ method: "GET", path: "/v1/usage/export", query, ...options });
|
|
6
|
+
}
|
|
1
7
|
/** `sbx.metrics()`: this sandbox's measured CPU and memory. */
|
|
2
8
|
export function sandboxMetrics(t, sandbox) {
|
|
3
9
|
return (options = {}) => {
|
|
@@ -29,6 +29,9 @@ export type CreatePreview = {
|
|
|
29
29
|
visibility?: "private" | "public";
|
|
30
30
|
/** How long the returned token lasts, 60 s to 7 days (default 1 day). */
|
|
31
31
|
ttlSeconds?: number;
|
|
32
|
+
/** An absolute ISO private-token deadline, a minute to a week ahead.
|
|
33
|
+
* Used exactly without TTL; with TTL, the earlier expiry wins. */
|
|
34
|
+
expiresAt?: string;
|
|
32
35
|
/** The sites that may show it in an iframe, as origins
|
|
33
36
|
* (`https://app.example.com`, `https://*.example.com`, `http://localhost:3000`),
|
|
34
37
|
* up to 16. Any other site's iframe is refused. Omitted: a shared port keeps
|
|
@@ -47,8 +50,11 @@ export declare function sandboxPreviews(t: Transport, sandbox: Sandbox): {
|
|
|
47
50
|
create: (port: number, input?: CreatePreview, options?: RequestOptions) => Promise<Preview>;
|
|
48
51
|
/** Every shared port, each private one with a fresh token. */
|
|
49
52
|
list: (options?: RequestOptions) => Promise<Preview[]>;
|
|
50
|
-
/** One preview, with a fresh token
|
|
51
|
-
|
|
53
|
+
/** One preview, with a fresh private token. `expiresAt` bounds its expiry
|
|
54
|
+
* to an absolute ISO deadline; with no TTL, that deadline is used exactly. */
|
|
55
|
+
get: (port: number, ttlSeconds?: number, { expiresAt, ...options }?: RequestOptions & {
|
|
56
|
+
expiresAt?: string;
|
|
57
|
+
}) => Promise<Preview>;
|
|
52
58
|
/** Refuses every token issued for this port so far and returns a new one. */
|
|
53
59
|
rotate: (port: number, options?: RequestOptions) => Promise<Preview>;
|
|
54
60
|
/** Stops sharing `port`. Its open connections are closed before this returns. */
|
|
@@ -12,11 +12,19 @@ export function sandboxPreviews(t, sandbox) {
|
|
|
12
12
|
create: (port, input = {}, options) => t.json({ method: "POST", path: base(), body: { port, ...input }, ...options }),
|
|
13
13
|
/** Every shared port, each private one with a fresh token. */
|
|
14
14
|
list: async (options) => (await t.json({ method: "GET", path: base(), ...options })).data,
|
|
15
|
-
/** One preview, with a fresh token
|
|
16
|
-
|
|
15
|
+
/** One preview, with a fresh private token. `expiresAt` bounds its expiry
|
|
16
|
+
* to an absolute ISO deadline; with no TTL, that deadline is used exactly. */
|
|
17
|
+
get: (port, ttlSeconds, { expiresAt, ...options } = {}) => t.json({
|
|
17
18
|
method: "GET",
|
|
18
19
|
path: `${base()}/${port}`,
|
|
19
|
-
...(ttlSeconds
|
|
20
|
+
...(ttlSeconds !== undefined || expiresAt !== undefined
|
|
21
|
+
? {
|
|
22
|
+
query: {
|
|
23
|
+
...(ttlSeconds === undefined ? {} : { ttlSeconds }),
|
|
24
|
+
...(expiresAt === undefined ? {} : { expiresAt }),
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
: {}),
|
|
20
28
|
...options,
|
|
21
29
|
}),
|
|
22
30
|
/** Refuses every token issued for this port so far and returns a new one. */
|
|
@@ -10,6 +10,16 @@ export type Volume = {
|
|
|
10
10
|
labels: Record<string, string>;
|
|
11
11
|
region: string;
|
|
12
12
|
sizeMiB: number;
|
|
13
|
+
/** Fixed at creation; shared disks support concurrent writers. */
|
|
14
|
+
shared?: boolean;
|
|
15
|
+
resize?: {
|
|
16
|
+
id: string;
|
|
17
|
+
operationId: string;
|
|
18
|
+
sizeMiB: number;
|
|
19
|
+
state: "pending" | "running" | "completed" | "failed";
|
|
20
|
+
generation: number;
|
|
21
|
+
error: string | null;
|
|
22
|
+
} | null;
|
|
13
23
|
filesystem: "ext4";
|
|
14
24
|
usedMiB: number | null;
|
|
15
25
|
measuredAt: string | null;
|
|
@@ -24,6 +34,8 @@ export type Volume = {
|
|
|
24
34
|
/** The backup this volume was restored from, if any. */
|
|
25
35
|
restoredFrom: string | null;
|
|
26
36
|
attachments: {
|
|
37
|
+
id?: string;
|
|
38
|
+
state?: VolumeAttachment["state"];
|
|
27
39
|
sandboxId: string;
|
|
28
40
|
mode: "rw" | "snapshot";
|
|
29
41
|
path: string;
|
|
@@ -34,6 +46,8 @@ export type Volume = {
|
|
|
34
46
|
readyAt: string | null;
|
|
35
47
|
};
|
|
36
48
|
export type CreateVolume = {
|
|
49
|
+
/** Available where shared disks are enabled. Fixed at creation. */
|
|
50
|
+
shared?: boolean;
|
|
37
51
|
/** Required unless restoring: a restore is the backup's size. */
|
|
38
52
|
sizeMiB?: number;
|
|
39
53
|
/** A ready backup to restore into the new volume, on any host in its region. */
|
|
@@ -42,6 +56,18 @@ export type CreateVolume = {
|
|
|
42
56
|
labels?: Record<string, string>;
|
|
43
57
|
region?: string;
|
|
44
58
|
};
|
|
59
|
+
export type VolumeAttachment = {
|
|
60
|
+
id: string;
|
|
61
|
+
volumeId: string;
|
|
62
|
+
sandboxId: string;
|
|
63
|
+
path: string;
|
|
64
|
+
mode: "rw";
|
|
65
|
+
state: "attaching" | "active" | "detaching" | "detached";
|
|
66
|
+
generation: number;
|
|
67
|
+
error: string | null;
|
|
68
|
+
attachedAt: string;
|
|
69
|
+
changedAt: string;
|
|
70
|
+
};
|
|
45
71
|
export type VolumeBackupState = "pending" | "ready" | "failed" | "deleting" | "deleted";
|
|
46
72
|
/** A point-in-time copy of a volume, kept off its host. */
|
|
47
73
|
export type VolumeBackup = {
|
|
@@ -78,6 +104,22 @@ export declare function volumes(t: Transport): {
|
|
|
78
104
|
limit?: number;
|
|
79
105
|
cursor?: string;
|
|
80
106
|
}, options?: RequestOptions) => Promise<Page<Volume>>;
|
|
107
|
+
/** Grow a detached ordinary disk; wait up to 10 s for the operation. */
|
|
108
|
+
resize: (id: string, input: {
|
|
109
|
+
sizeMiB: number;
|
|
110
|
+
}, options?: RequestOptions & {
|
|
111
|
+
wait?: number;
|
|
112
|
+
}) => Promise<Volume>;
|
|
113
|
+
attach: (id: string, input: {
|
|
114
|
+
sandboxId: string;
|
|
115
|
+
path: string;
|
|
116
|
+
}, options?: RequestOptions & {
|
|
117
|
+
wait?: number;
|
|
118
|
+
}) => Promise<VolumeAttachment>;
|
|
119
|
+
detach: (id: string, attachmentId: string, options?: RequestOptions & {
|
|
120
|
+
wait?: number;
|
|
121
|
+
}) => Promise<VolumeAttachment>;
|
|
122
|
+
getAttachment: (id: string, attachmentId: string, options?: RequestOptions) => Promise<VolumeAttachment>;
|
|
81
123
|
delete: (id: string, options?: RequestOptions) => Promise<Volume>;
|
|
82
124
|
/** Back a volume up now, off its host, and wait (up to a minute, or
|
|
83
125
|
* `wait` seconds) for it to be ready. */
|
package/dist/products/volumes.js
CHANGED
|
@@ -29,6 +29,42 @@ export function volumes(t) {
|
|
|
29
29
|
create: (input, options) => t.json({ method: "POST", path: "/v1/volumes", body: input, wait: 10, ...options }),
|
|
30
30
|
get,
|
|
31
31
|
list,
|
|
32
|
+
/** Grow a detached ordinary disk; wait up to 10 s for the operation. */
|
|
33
|
+
resize: (id, input, options = {}) => {
|
|
34
|
+
const { wait = 10, ...rest } = options;
|
|
35
|
+
return t.json({
|
|
36
|
+
method: "POST",
|
|
37
|
+
path: `/v1/volumes/${enc(id)}:resize`,
|
|
38
|
+
body: input,
|
|
39
|
+
wait,
|
|
40
|
+
...rest,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
attach: (id, input, options = {}) => {
|
|
44
|
+
const { wait = 10, ...rest } = options;
|
|
45
|
+
return t.json({
|
|
46
|
+
method: "POST",
|
|
47
|
+
path: `/v1/volumes/${enc(id)}:attach`,
|
|
48
|
+
body: input,
|
|
49
|
+
wait,
|
|
50
|
+
...rest,
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
detach: (id, attachmentId, options = {}) => {
|
|
54
|
+
const { wait = 10, ...rest } = options;
|
|
55
|
+
return t.json({
|
|
56
|
+
method: "POST",
|
|
57
|
+
path: `/v1/volumes/${enc(id)}/attachments/${enc(attachmentId)}:detach`,
|
|
58
|
+
body: {},
|
|
59
|
+
wait,
|
|
60
|
+
...rest,
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
getAttachment: (id, attachmentId, options) => t.json({
|
|
64
|
+
method: "GET",
|
|
65
|
+
path: `/v1/volumes/${enc(id)}/attachments/${enc(attachmentId)}`,
|
|
66
|
+
...options,
|
|
67
|
+
}),
|
|
32
68
|
delete: (id, options) => t.json({
|
|
33
69
|
method: "POST",
|
|
34
70
|
path: `/v1/volumes/${enc(id)}:delete`,
|
package/dist/products/watch.js
CHANGED
|
@@ -12,10 +12,13 @@ export class WatchHandle {
|
|
|
12
12
|
#stopped = false;
|
|
13
13
|
/** stop() was called: deliver what the watch reports until its end. */
|
|
14
14
|
#stopping = false;
|
|
15
|
+
#stopPending;
|
|
16
|
+
#remoteStop;
|
|
15
17
|
/** Callbacks under way. */
|
|
16
18
|
#delivering = 0;
|
|
17
19
|
#exitNotified = false;
|
|
18
20
|
#following;
|
|
21
|
+
#followingActive = false;
|
|
19
22
|
#abort = new AbortController();
|
|
20
23
|
constructor(t, base, id, path, cursor, options, onEvent) {
|
|
21
24
|
this.t = t;
|
|
@@ -34,6 +37,8 @@ export class WatchHandle {
|
|
|
34
37
|
resume() {
|
|
35
38
|
if (this.#stopped || this.#stopping)
|
|
36
39
|
throw new Error("This watch was stopped");
|
|
40
|
+
if (this.#followingActive)
|
|
41
|
+
throw new Error("This watch is already delivering");
|
|
37
42
|
this.#abort = new AbortController();
|
|
38
43
|
this.begin();
|
|
39
44
|
}
|
|
@@ -41,12 +46,47 @@ export class WatchHandle {
|
|
|
41
46
|
* delivered: the watch reports it and ends, and this returns once that has
|
|
42
47
|
* arrived (at most 5 s). Called from inside a callback, it stops at once. */
|
|
43
48
|
async stop(options) {
|
|
44
|
-
if (this.#stopped
|
|
49
|
+
if (this.#stopped)
|
|
50
|
+
return;
|
|
51
|
+
if (this.#stopping) {
|
|
52
|
+
// Even a callback must observe whether the remote delete succeeded.
|
|
53
|
+
// It cannot join the drain, which is waiting for that callback to finish.
|
|
54
|
+
await this.#remoteStop;
|
|
55
|
+
if (this.#delivering === 0)
|
|
56
|
+
await this.#stopPending;
|
|
57
|
+
else
|
|
58
|
+
this.#finishLocalStop();
|
|
45
59
|
return;
|
|
60
|
+
}
|
|
46
61
|
this.#stopping = true;
|
|
47
|
-
await this.t
|
|
48
|
-
|
|
49
|
-
.
|
|
62
|
+
this.#remoteStop = (async () => await this.t.json({
|
|
63
|
+
method: "DELETE",
|
|
64
|
+
path: `${this.base}/${encodeURIComponent(this.id)}`,
|
|
65
|
+
...options,
|
|
66
|
+
}))();
|
|
67
|
+
const pending = this.#finishStop(this.#remoteStop);
|
|
68
|
+
this.#stopPending = pending;
|
|
69
|
+
try {
|
|
70
|
+
await pending;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
if (this.#stopPending === pending) {
|
|
74
|
+
this.#stopPending = undefined;
|
|
75
|
+
this.#remoteStop = undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async #finishStop(remote) {
|
|
80
|
+
try {
|
|
81
|
+
await remote;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
// A failed delete does not prove the remote watch stopped. Close local
|
|
85
|
+
// delivery, but let the caller observe the failure and retry the delete.
|
|
86
|
+
this.#abort.abort();
|
|
87
|
+
this.#stopping = false;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
50
90
|
// Inside a callback, #following is awaiting the caller: waiting for it
|
|
51
91
|
// here would make that callback await itself.
|
|
52
92
|
if (this.#following && this.#delivering === 0) {
|
|
@@ -59,6 +99,9 @@ export class WatchHandle {
|
|
|
59
99
|
]);
|
|
60
100
|
clearTimeout(timer);
|
|
61
101
|
}
|
|
102
|
+
this.#finishLocalStop();
|
|
103
|
+
}
|
|
104
|
+
#finishLocalStop() {
|
|
62
105
|
this.#stopped = true;
|
|
63
106
|
this.#abort.abort();
|
|
64
107
|
this.#notifyExit("stopped");
|
|
@@ -69,7 +112,7 @@ export class WatchHandle {
|
|
|
69
112
|
this.#exitNotified = true;
|
|
70
113
|
this.options.onExit?.(reason);
|
|
71
114
|
}
|
|
72
|
-
async #follow() {
|
|
115
|
+
async #follow(abort) {
|
|
73
116
|
for (;;) {
|
|
74
117
|
let next = "exit";
|
|
75
118
|
try {
|
|
@@ -77,7 +120,7 @@ export class WatchHandle {
|
|
|
77
120
|
method: "GET",
|
|
78
121
|
path: `${this.base}/${encodeURIComponent(this.id)}/events`,
|
|
79
122
|
query: { cursor: this.cursor, follow: true },
|
|
80
|
-
signal:
|
|
123
|
+
signal: abort.signal,
|
|
81
124
|
timeoutMs: 150_000,
|
|
82
125
|
})) {
|
|
83
126
|
this.cursor = event.cursor ?? this.cursor;
|
|
@@ -102,6 +145,9 @@ export class WatchHandle {
|
|
|
102
145
|
else if (event.k === "continue")
|
|
103
146
|
next = "continue";
|
|
104
147
|
else if (event.k === "end" || event.k === "paused") {
|
|
148
|
+
// A paused callback may resume immediately. Retire this reader
|
|
149
|
+
// before invoking it, without letting its finalizer retire the new one.
|
|
150
|
+
this.#followingActive = false;
|
|
105
151
|
if (!this.#stopped)
|
|
106
152
|
this.#notifyExit(event.k === "paused" ? "paused" : event.reason);
|
|
107
153
|
return;
|
|
@@ -119,7 +165,7 @@ export class WatchHandle {
|
|
|
119
165
|
}
|
|
120
166
|
}
|
|
121
167
|
catch (error) {
|
|
122
|
-
if (this.#stopped ||
|
|
168
|
+
if (this.#stopped || abort.signal.aborted)
|
|
123
169
|
return;
|
|
124
170
|
throw error;
|
|
125
171
|
}
|
|
@@ -129,8 +175,15 @@ export class WatchHandle {
|
|
|
129
175
|
}
|
|
130
176
|
/** @internal */
|
|
131
177
|
begin() {
|
|
178
|
+
if (this.#followingActive)
|
|
179
|
+
throw new Error("This watch is already delivering");
|
|
132
180
|
this.#exitNotified = false;
|
|
133
|
-
this.#
|
|
181
|
+
this.#followingActive = true;
|
|
182
|
+
const abort = this.#abort;
|
|
183
|
+
this.#following = this.#follow(abort).finally(() => {
|
|
184
|
+
if (this.#abort === abort)
|
|
185
|
+
this.#followingActive = false;
|
|
186
|
+
});
|
|
134
187
|
// An error surfaces through `done`; nothing is left unhandled meanwhile.
|
|
135
188
|
this.#following.catch(() => undefined);
|
|
136
189
|
return this;
|
package/dist/proxy.js
CHANGED
|
@@ -72,7 +72,9 @@ export function routeFor(target, env = processEnv()) {
|
|
|
72
72
|
parsed = new URL(proxy);
|
|
73
73
|
}
|
|
74
74
|
catch {
|
|
75
|
-
|
|
75
|
+
// A malformed address cannot be parsed reliably enough to remove its
|
|
76
|
+
// credentials. Keep the variable name, never its untrusted value.
|
|
77
|
+
throw invalidProxy(found.name, "invalid address", "is not an address");
|
|
76
78
|
}
|
|
77
79
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
78
80
|
throw invalidProxy(found.name, `${parsed.protocol}//${parsed.host}`, "is not an HTTP proxy");
|
package/dist/runloop/index.d.ts
CHANGED
|
@@ -152,7 +152,7 @@ export declare class Devboxes {
|
|
|
152
152
|
resource_size_request: "CUSTOM_SIZE";
|
|
153
153
|
};
|
|
154
154
|
}>;
|
|
155
|
-
retrieve(id: string): Promise<{
|
|
155
|
+
retrieve(id: string, options?: RequestOptions): Promise<{
|
|
156
156
|
id: string;
|
|
157
157
|
name: string | null;
|
|
158
158
|
metadata: {
|
|
@@ -232,7 +232,7 @@ export declare class Devboxes {
|
|
|
232
232
|
list(query?: DevboxListParams, options?: RequestOptions): PagePromise<DevboxView, DevboxViewsCursorIDPage<DevboxView>>;
|
|
233
233
|
list(options?: RequestOptions): PagePromise<DevboxView, DevboxViewsCursorIDPage<DevboxView>>;
|
|
234
234
|
private listPage;
|
|
235
|
-
suspend(id: string): Promise<{
|
|
235
|
+
suspend(id: string, options?: RequestOptions): Promise<{
|
|
236
236
|
id: string;
|
|
237
237
|
name: string | null;
|
|
238
238
|
metadata: {
|
|
@@ -251,26 +251,7 @@ export declare class Devboxes {
|
|
|
251
251
|
resource_size_request: "CUSTOM_SIZE";
|
|
252
252
|
};
|
|
253
253
|
}>;
|
|
254
|
-
resume(id: string): Promise<{
|
|
255
|
-
id: string;
|
|
256
|
-
name: string | null;
|
|
257
|
-
metadata: {
|
|
258
|
-
[k: string]: string;
|
|
259
|
-
};
|
|
260
|
-
status: string;
|
|
261
|
-
create_time_ms: number;
|
|
262
|
-
end_time_ms: number | null;
|
|
263
|
-
capabilities: never[];
|
|
264
|
-
state_transitions: never[];
|
|
265
|
-
launch_parameters: {
|
|
266
|
-
custom_cpu_cores: number;
|
|
267
|
-
custom_gb_memory: number;
|
|
268
|
-
custom_disk_size: number;
|
|
269
|
-
keep_alive_time_seconds: number;
|
|
270
|
-
resource_size_request: "CUSTOM_SIZE";
|
|
271
|
-
};
|
|
272
|
-
}>;
|
|
273
|
-
shutdown(id: string): Promise<{
|
|
254
|
+
resume(id: string, options?: RequestOptions): Promise<{
|
|
274
255
|
id: string;
|
|
275
256
|
name: string | null;
|
|
276
257
|
metadata: {
|
|
@@ -289,16 +270,19 @@ export declare class Devboxes {
|
|
|
289
270
|
resource_size_request: "CUSTOM_SIZE";
|
|
290
271
|
};
|
|
291
272
|
}>;
|
|
273
|
+
shutdown(id: string, params?: {
|
|
274
|
+
force?: string;
|
|
275
|
+
} | RequestOptions, options?: RequestOptions): Promise<DevboxView>;
|
|
292
276
|
executeSync(id: string, body: DevboxExecuteParams, options?: RequestOptions): Promise<DevboxExecutionDetailView>;
|
|
293
277
|
executeAsync(id: string, body: DevboxExecuteParams, options?: RequestOptions): Promise<DevboxAsyncExecutionDetailView>;
|
|
294
278
|
executeAndAwaitCompletion(id: string, body: DevboxExecuteParams, options?: RequestOptions): Promise<DevboxAsyncExecutionDetailView>;
|
|
295
279
|
readFileContents(id: string, body: {
|
|
296
280
|
file_path: string;
|
|
297
|
-
}): Promise<string>;
|
|
281
|
+
}, options?: RequestOptions): Promise<string>;
|
|
298
282
|
writeFileContents(id: string, body: {
|
|
299
283
|
file_path: string;
|
|
300
284
|
contents: string;
|
|
301
|
-
}): Promise<DevboxExecutionDetailView>;
|
|
285
|
+
}, options?: RequestOptions): Promise<DevboxExecutionDetailView>;
|
|
302
286
|
snapshotDisk(id: string, body?: DevboxSnapshotDiskParams, options?: RequestOptions): Promise<DevboxSnapshotView>;
|
|
303
287
|
snapshotDisk(id: string, options?: RequestOptions): Promise<DevboxSnapshotView>;
|
|
304
288
|
snapshotDiskAsync(_id: string, _body?: DevboxSnapshotDiskParams | RequestOptions, _options?: RequestOptions): Promise<never>;
|
|
@@ -311,15 +295,17 @@ export declare class Executions {
|
|
|
311
295
|
constructor(runtime: Runtime);
|
|
312
296
|
executeSync(id: string, body: DevboxExecuteParams, options?: RequestOptions): Promise<DevboxExecutionDetailView>;
|
|
313
297
|
executeAsync(id: string, body: DevboxExecuteParams, options?: RequestOptions): Promise<DevboxAsyncExecutionDetailView>;
|
|
314
|
-
retrieve(id: string, execution: string
|
|
298
|
+
retrieve(id: string, execution: string, query?: {
|
|
299
|
+
last_n?: string;
|
|
300
|
+
} | RequestOptions, options?: RequestOptions): Promise<DevboxAsyncExecutionDetailView>;
|
|
315
301
|
awaitCompleted(id: string, execution: string, options?: RequestOptions): Promise<DevboxAsyncExecutionDetailView>;
|
|
316
302
|
kill(id: string, execution: string, body?: {
|
|
317
303
|
kill_process_group?: boolean | null;
|
|
318
|
-
}): Promise<DevboxAsyncExecutionDetailView>;
|
|
304
|
+
} | RequestOptions, options?: RequestOptions): Promise<DevboxAsyncExecutionDetailView>;
|
|
319
305
|
sendStdIn(id: string, execution: string, body?: {
|
|
320
306
|
text?: string | null;
|
|
321
307
|
signal?: "EOF" | "INTERRUPT" | null;
|
|
322
|
-
}): Promise<{
|
|
308
|
+
} | RequestOptions, options?: RequestOptions): Promise<{
|
|
323
309
|
devbox_id: string;
|
|
324
310
|
execution_id: string;
|
|
325
311
|
success: boolean;
|