withruntime 0.8.4 → 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/COMPATIBILITY.md +145 -0
- package/DAYTONA.md +13 -7
- package/E2B.md +34 -20
- package/README.md +22 -11
- package/README.src.md +22 -11
- package/VERCEL.md +13 -3
- package/compatibility-lock.json +357 -0
- package/dist/ai-harness/index.js +2 -2
- package/dist/api-defaults.d.ts +17 -0
- package/dist/api-defaults.js +22 -0
- 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 +3 -2
- package/dist/blaxel/preview.js +16 -14
- package/dist/blaxel/process.d.ts +1 -1
- package/dist/blaxel/process.js +66 -11
- 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-words.d.ts +27 -0
- package/dist/cli-words.js +135 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +351 -85
- package/dist/client.d.ts +33 -4
- package/dist/client.js +47 -12
- package/dist/cloudflare/identity.d.ts +7 -0
- package/dist/cloudflare/identity.js +13 -0
- package/dist/cloudflare/index.d.ts +303 -0
- package/dist/cloudflare/index.js +592 -0
- package/dist/cloudflare/worker.d.ts +419 -0
- package/dist/cloudflare/worker.js +447 -0
- package/dist/codesandbox/guest-service.d.ts +31 -0
- package/dist/codesandbox/guest-service.js +490 -0
- package/dist/codesandbox/index.d.ts +386 -0
- package/dist/codesandbox/index.js +1347 -0
- package/dist/compat/core.d.ts +40 -0
- package/dist/compat/core.js +238 -0
- package/dist/compat/files.d.ts +26 -0
- package/dist/compat/files.js +133 -0
- package/dist/compat/shell.d.ts +4 -0
- package/dist/compat/shell.js +165 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +107 -38
- package/dist/daytona/context.js +2 -1
- 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/process.d.ts +1 -1
- package/dist/daytona/process.js +27 -4
- 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/code-interpreter.d.ts +4 -5
- package/dist/e2b/code-interpreter.js +54 -56
- package/dist/e2b/commands.d.ts +35 -12
- package/dist/e2b/commands.js +169 -73
- package/dist/e2b/errors.js +1 -1
- package/dist/e2b/filesystem.d.ts +8 -6
- package/dist/e2b/filesystem.js +127 -28
- 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 +6 -4
- package/dist/e2b/sandbox.js +59 -25
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +15 -2
- package/dist/file-source.d.ts +16 -0
- package/dist/file-source.js +162 -0
- package/dist/freestyle/index.d.ts +309 -0
- package/dist/freestyle/index.js +343 -0
- package/dist/index.d.ts +3 -1
- package/dist/login.js +16 -10
- package/dist/mcp.js +2 -2
- package/dist/modal/index.d.ts +205 -0
- package/dist/modal/index.js +486 -0
- package/dist/network-cli.js +112 -44
- package/dist/openai-agents/index.js +18 -71
- package/dist/page.d.ts +1 -1
- package/dist/page.js +2 -2
- package/dist/products/audit.d.ts +3 -0
- package/dist/products/index.d.ts +14 -0
- package/dist/products/index.js +12 -0
- package/dist/products/interpreter.d.ts +11 -4
- package/dist/products/interpreter.js +33 -9
- package/dist/products/jobs.js +3 -2
- package/dist/products/limits.d.ts +10 -0
- package/dist/products/observability.d.ts +44 -0
- package/dist/products/observability.js +6 -0
- package/dist/products/previews.d.ts +18 -4
- package/dist/products/previews.js +14 -5
- package/dist/products/tailscale.d.ts +57 -0
- package/dist/products/tailscale.js +17 -0
- package/dist/products/volumes.d.ts +42 -0
- package/dist/products/volumes.js +36 -0
- package/dist/products/watch.d.ts +25 -4
- package/dist/products/watch.js +132 -18
- package/dist/proxy.js +3 -1
- package/dist/runloop/index.d.ts +326 -0
- package/dist/runloop/index.js +535 -0
- package/dist/runloop/pagination.d.ts +46 -0
- package/dist/runloop/pagination.js +82 -0
- package/dist/runloop/snapshots.d.ts +52 -0
- package/dist/runloop/snapshots.js +244 -0
- package/dist/sandbox.d.ts +76 -16
- package/dist/sandbox.js +696 -135
- package/dist/snapshots.d.ts +20 -5
- package/dist/snapshots.js +11 -1
- package/dist/sprites/index.d.ts +251 -0
- package/dist/sprites/index.js +588 -0
- package/dist/tar.d.ts +10 -1
- package/dist/tar.js +311 -35
- package/dist/tools/index.js +2 -1
- package/dist/transport.d.ts +23 -8
- package/dist/transport.js +247 -127
- package/dist/types.d.ts +52 -3
- package/dist/vercel/command.d.ts +1 -1
- package/dist/vercel/command.js +15 -3
- package/dist/vercel/errors.d.ts +2 -2
- package/dist/vercel/errors.js +19 -2
- package/dist/vercel/filesystem.d.ts +21 -18
- package/dist/vercel/filesystem.js +95 -48
- package/dist/vercel/sandbox.d.ts +8 -8
- package/dist/vercel/sandbox.js +159 -64
- package/dist/vercel/snapshot.d.ts +3 -2
- package/dist/vercel/snapshot.js +9 -5
- package/package.json +46 -3
|
@@ -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.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequestOptions, Transport } from "../transport.js";
|
|
2
|
-
export type FileEventType = "create" | "write" | "remove" | "rename" | "chmod";
|
|
2
|
+
export type FileEventType = "create" | "write" | "remove" | "rename" | "chmod" | "access";
|
|
3
3
|
export type FileEvent = {
|
|
4
4
|
type: FileEventType;
|
|
5
5
|
/** Absolute path in the sandbox. */
|
|
@@ -23,7 +23,7 @@ export type WatchNotice = {
|
|
|
23
23
|
};
|
|
24
24
|
export type WatchOptions = Omit<RequestOptions, "timeoutMs"> & {
|
|
25
25
|
recursive?: boolean;
|
|
26
|
-
/** Only these event types;
|
|
26
|
+
/** Only these event types; the five mutation types by default; access is opt-in. */
|
|
27
27
|
events?: FileEventType[];
|
|
28
28
|
/** Globs relative to the path: only matching paths are reported. */
|
|
29
29
|
include?: string[];
|
|
@@ -31,7 +31,7 @@ export type WatchOptions = Omit<RequestOptions, "timeoutMs"> & {
|
|
|
31
31
|
exclude?: string[];
|
|
32
32
|
/** Events are gathered this long and delivered together. Default 100. */
|
|
33
33
|
batchMs?: number;
|
|
34
|
-
/** The watch ends by itself after running this long. Default 1 hour. */
|
|
34
|
+
/** The watch ends by itself after running this long. Default 1 hour; 0 runs until stopped or the sandbox ends. */
|
|
35
35
|
timeoutMs?: number;
|
|
36
36
|
maxWatches?: number;
|
|
37
37
|
/** A batch at a time, rather than one call per event. */
|
|
@@ -49,6 +49,16 @@ export type WatchInfo = {
|
|
|
49
49
|
processId: string;
|
|
50
50
|
state: string;
|
|
51
51
|
startedAt: number;
|
|
52
|
+
/** Its changes go to the account's webhooks as `sandbox.files.changed`. */
|
|
53
|
+
webhook?: boolean;
|
|
54
|
+
};
|
|
55
|
+
/** A watch whose changes Runtime sends to the account's webhooks. */
|
|
56
|
+
export type WebhookWatchOptions = Omit<WatchOptions, "onBatch" | "onNotice" | "onExit" | "timeoutMs"> & {
|
|
57
|
+
/** The watch ends by itself after running this long; 0 (the default here)
|
|
58
|
+
* runs until stopped or the sandbox ends. */
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
/** Its own id, 1 to 40 lowercase letters, digits or dashes. */
|
|
61
|
+
id?: string;
|
|
52
62
|
};
|
|
53
63
|
/** A running watch. Events arrive through the callbacks until `stop()`. */
|
|
54
64
|
export declare class WatchHandle {
|
|
@@ -65,7 +75,9 @@ export declare class WatchHandle {
|
|
|
65
75
|
get done(): Promise<void>;
|
|
66
76
|
/** Start delivering again from where it left off, after a pause. */
|
|
67
77
|
resume(): void;
|
|
68
|
-
/** Stop the watch in the sandbox
|
|
78
|
+
/** Stop the watch in the sandbox. What changed before the stop is still
|
|
79
|
+
* delivered: the watch reports it and ends, and this returns once that has
|
|
80
|
+
* arrived (at most 5 s). Called from inside a callback, it stops at once. */
|
|
69
81
|
stop(options?: RequestOptions): Promise<void>;
|
|
70
82
|
/** @internal */
|
|
71
83
|
begin(): this;
|
|
@@ -85,6 +97,15 @@ export declare function sandboxWatches(t: Transport, sandboxId: string): {
|
|
|
85
97
|
lostBytes: number;
|
|
86
98
|
ended: boolean;
|
|
87
99
|
}>;
|
|
100
|
+
/** Starts a watch whose changes Runtime sends to the account's webhooks
|
|
101
|
+
* as `sandbox.files.changed` events, with nothing reading it here:
|
|
102
|
+
*
|
|
103
|
+
* await sbx.files.watches.webhook("/workspace/app", { recursive: true });
|
|
104
|
+
*
|
|
105
|
+
* Runs until stopped (timeoutMs 0) unless given a timeout. */
|
|
106
|
+
webhook: (path: string, input?: WebhookWatchOptions) => Promise<WatchInfo & {
|
|
107
|
+
cursor: number;
|
|
108
|
+
}>;
|
|
88
109
|
stop: (id: string, options?: RequestOptions) => Promise<{
|
|
89
110
|
stopped: boolean;
|
|
90
111
|
}>;
|
package/dist/products/watch.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { WEBHOOK_WATCH_TIMEOUT_MS } from "../api-defaults.js";
|
|
2
|
+
import { RuntimeError } from "../errors.js";
|
|
1
3
|
/** A running watch. Events arrive through the callbacks until `stop()`. */
|
|
2
4
|
export class WatchHandle {
|
|
3
5
|
t;
|
|
@@ -8,7 +10,15 @@ export class WatchHandle {
|
|
|
8
10
|
onEvent;
|
|
9
11
|
cursor;
|
|
10
12
|
#stopped = false;
|
|
13
|
+
/** stop() was called: deliver what the watch reports until its end. */
|
|
14
|
+
#stopping = false;
|
|
15
|
+
#stopPending;
|
|
16
|
+
#remoteStop;
|
|
17
|
+
/** Callbacks under way. */
|
|
18
|
+
#delivering = 0;
|
|
19
|
+
#exitNotified = false;
|
|
11
20
|
#following;
|
|
21
|
+
#followingActive = false;
|
|
12
22
|
#abort = new AbortController();
|
|
13
23
|
constructor(t, base, id, path, cursor, options, onEvent) {
|
|
14
24
|
this.t = t;
|
|
@@ -25,24 +35,84 @@ export class WatchHandle {
|
|
|
25
35
|
}
|
|
26
36
|
/** Start delivering again from where it left off, after a pause. */
|
|
27
37
|
resume() {
|
|
28
|
-
if (this.#stopped)
|
|
38
|
+
if (this.#stopped || this.#stopping)
|
|
29
39
|
throw new Error("This watch was stopped");
|
|
40
|
+
if (this.#followingActive)
|
|
41
|
+
throw new Error("This watch is already delivering");
|
|
30
42
|
this.#abort = new AbortController();
|
|
31
43
|
this.begin();
|
|
32
44
|
}
|
|
33
|
-
/** Stop the watch in the sandbox
|
|
45
|
+
/** Stop the watch in the sandbox. What changed before the stop is still
|
|
46
|
+
* delivered: the watch reports it and ends, and this returns once that has
|
|
47
|
+
* arrived (at most 5 s). Called from inside a callback, it stops at once. */
|
|
34
48
|
async stop(options) {
|
|
35
49
|
if (this.#stopped)
|
|
36
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();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.#stopping = true;
|
|
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
|
+
}
|
|
90
|
+
// Inside a callback, #following is awaiting the caller: waiting for it
|
|
91
|
+
// here would make that callback await itself.
|
|
92
|
+
if (this.#following && this.#delivering === 0) {
|
|
93
|
+
let timer;
|
|
94
|
+
await Promise.race([
|
|
95
|
+
this.#following.catch(() => undefined),
|
|
96
|
+
new Promise((resolve) => {
|
|
97
|
+
timer = setTimeout(resolve, 5_000);
|
|
98
|
+
}),
|
|
99
|
+
]);
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
}
|
|
102
|
+
this.#finishLocalStop();
|
|
103
|
+
}
|
|
104
|
+
#finishLocalStop() {
|
|
37
105
|
this.#stopped = true;
|
|
38
106
|
this.#abort.abort();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
107
|
+
this.#notifyExit("stopped");
|
|
108
|
+
}
|
|
109
|
+
#notifyExit(reason) {
|
|
110
|
+
if (this.#exitNotified)
|
|
111
|
+
return;
|
|
112
|
+
this.#exitNotified = true;
|
|
113
|
+
this.options.onExit?.(reason);
|
|
44
114
|
}
|
|
45
|
-
async #follow() {
|
|
115
|
+
async #follow(abort) {
|
|
46
116
|
for (;;) {
|
|
47
117
|
let next = "exit";
|
|
48
118
|
try {
|
|
@@ -50,32 +120,52 @@ export class WatchHandle {
|
|
|
50
120
|
method: "GET",
|
|
51
121
|
path: `${this.base}/${encodeURIComponent(this.id)}/events`,
|
|
52
122
|
query: { cursor: this.cursor, follow: true },
|
|
53
|
-
signal:
|
|
123
|
+
signal: abort.signal,
|
|
54
124
|
timeoutMs: 150_000,
|
|
55
125
|
})) {
|
|
56
126
|
this.cursor = event.cursor ?? this.cursor;
|
|
57
127
|
if (event.k === "events") {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
128
|
+
this.#delivering++;
|
|
129
|
+
try {
|
|
130
|
+
if (this.options.onBatch)
|
|
131
|
+
await this.options.onBatch(event.events);
|
|
132
|
+
if (this.#stopped)
|
|
133
|
+
return;
|
|
134
|
+
if (this.onEvent)
|
|
135
|
+
for (const item of event.events) {
|
|
136
|
+
await this.onEvent(item);
|
|
137
|
+
if (this.#stopped)
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
this.#delivering--;
|
|
143
|
+
}
|
|
63
144
|
}
|
|
64
145
|
else if (event.k === "continue")
|
|
65
146
|
next = "continue";
|
|
66
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;
|
|
67
151
|
if (!this.#stopped)
|
|
68
|
-
this
|
|
152
|
+
this.#notifyExit(event.k === "paused" ? "paused" : event.reason);
|
|
69
153
|
return;
|
|
70
154
|
}
|
|
71
155
|
else if (event.k === "failure")
|
|
72
|
-
throw new
|
|
156
|
+
throw new RuntimeError({
|
|
157
|
+
code: event.code,
|
|
158
|
+
status: event.status ?? 0,
|
|
159
|
+
message: event.message,
|
|
160
|
+
...(event.hint ? { hint: event.hint } : {}),
|
|
161
|
+
...(event.requestId ? { requestId: event.requestId } : {}),
|
|
162
|
+
});
|
|
73
163
|
else
|
|
74
164
|
this.options.onNotice?.(event);
|
|
75
165
|
}
|
|
76
166
|
}
|
|
77
167
|
catch (error) {
|
|
78
|
-
if (this.#stopped ||
|
|
168
|
+
if (this.#stopped || abort.signal.aborted)
|
|
79
169
|
return;
|
|
80
170
|
throw error;
|
|
81
171
|
}
|
|
@@ -85,7 +175,15 @@ export class WatchHandle {
|
|
|
85
175
|
}
|
|
86
176
|
/** @internal */
|
|
87
177
|
begin() {
|
|
88
|
-
|
|
178
|
+
if (this.#followingActive)
|
|
179
|
+
throw new Error("This watch is already delivering");
|
|
180
|
+
this.#exitNotified = false;
|
|
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
|
+
});
|
|
89
187
|
// An error surfaces through `done`; nothing is left unhandled meanwhile.
|
|
90
188
|
this.#following.catch(() => undefined);
|
|
91
189
|
return this;
|
|
@@ -126,6 +224,22 @@ export function sandboxWatches(t, sandboxId) {
|
|
|
126
224
|
query: { cursor, ...(input.waitMs !== undefined ? { waitMs: input.waitMs } : {}) },
|
|
127
225
|
...options,
|
|
128
226
|
}),
|
|
227
|
+
/** Starts a watch whose changes Runtime sends to the account's webhooks
|
|
228
|
+
* as `sandbox.files.changed` events, with nothing reading it here:
|
|
229
|
+
*
|
|
230
|
+
* await sbx.files.watches.webhook("/workspace/app", { recursive: true });
|
|
231
|
+
*
|
|
232
|
+
* Runs until stopped (timeoutMs 0) unless given a timeout. */
|
|
233
|
+
webhook: (path, input = {}) => {
|
|
234
|
+
const { idempotencyKey, signal, timeoutMs, ...rest } = input;
|
|
235
|
+
return t.json({
|
|
236
|
+
method: "POST",
|
|
237
|
+
path: base,
|
|
238
|
+
body: { path, ...rest, timeoutMs: timeoutMs ?? WEBHOOK_WATCH_TIMEOUT_MS, webhook: true },
|
|
239
|
+
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
240
|
+
...(signal ? { signal } : {}),
|
|
241
|
+
});
|
|
242
|
+
},
|
|
129
243
|
stop: (id, options) => t.json({
|
|
130
244
|
method: "DELETE",
|
|
131
245
|
path: `${base}/${encodeURIComponent(id)}`,
|
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");
|