@effect-uai/deno 0.5.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/LICENSE +21 -0
- package/dist/DenoSandbox.d.mts +76 -0
- package/dist/DenoSandbox.d.mts.map +1 -0
- package/dist/DenoSandbox.mjs +438 -0
- package/dist/DenoSandbox.mjs.map +1 -0
- package/dist/chunk-CfYAbeIz.mjs +13 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/package.json +61 -0
- package/src/DenoSandbox.ts +939 -0
- package/src/index.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Betalyra Sociedade Unipessoal Lda.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as Memory from "@effect-uai/core/Memory";
|
|
2
|
+
import { BoundSecret, CommonCreateRequest, Sandbox, SandboxHostnameAllowlist, SandboxInstance, SandboxPortExposure, SandboxSecretInjection, SandboxService, SandboxSnapshots, SandboxVolumes, SnapshotId, VolumeId } from "@effect-uai/core/Sandbox";
|
|
3
|
+
import * as SandboxError from "@effect-uai/core/SandboxError";
|
|
4
|
+
import { Context, Effect, Layer, Redacted, Scope } from "effect";
|
|
5
|
+
import { ApiError, ConnectionClosedError, ConnectionEstablishmentError, InvalidMemoryError, InvalidTimeoutError, InvalidTokenError, MissingTokenError, SandboxCommandError, SandboxKillError } from "@deno/sandbox";
|
|
6
|
+
|
|
7
|
+
//#region src/DenoSandbox.d.ts
|
|
8
|
+
declare namespace DenoSandbox_d_exports {
|
|
9
|
+
export { ApiError, ConnectionClosedError, ConnectionEstablishmentError, DenoRegion, DenoSandbox, DenoSandboxBoundSecret, DenoSandboxConfig, DenoSandboxCreateRequest, DenoSandboxService, InvalidMemoryError, InvalidTimeoutError, InvalidTokenError, MissingTokenError, SandboxCommandError, SandboxKillError, layer };
|
|
10
|
+
}
|
|
11
|
+
type DenoSandboxBoundSecret = Omit<BoundSecret, "header">;
|
|
12
|
+
type DenoRegion = "ord" | "ams";
|
|
13
|
+
type DenoSandboxCreateRequest = Omit<CommonCreateRequest, "secrets"> & {
|
|
14
|
+
readonly secrets?: ReadonlyArray<DenoSandboxBoundSecret>;
|
|
15
|
+
readonly region?: DenoRegion;
|
|
16
|
+
/**
|
|
17
|
+
* Memory size for the sandbox. Accepts a byte count, a {@link Memory.Memory},
|
|
18
|
+
* or a human string like `"1280 MiB"` / `"1 GiB"`. Deno's accepted
|
|
19
|
+
* range at GA is 768 MiB – 4 GiB (default 1280 MiB).
|
|
20
|
+
*/
|
|
21
|
+
readonly memory?: Memory.Input;
|
|
22
|
+
readonly labels?: Readonly<Record<string, string>>; /** Auto-expose this port at boot; the live sandbox's `url` reaches it. */
|
|
23
|
+
readonly port?: number;
|
|
24
|
+
};
|
|
25
|
+
type DenoSandboxConfig = {
|
|
26
|
+
/** Overrides `DENO_DEPLOY_TOKEN`. */readonly token?: Redacted.Redacted<string>; /** Overrides `DENO_DEPLOY_ORG`. Required for personal `ddp_` tokens. */
|
|
27
|
+
readonly org?: string; /** Overrides `DENO_DEPLOY_ENDPOINT`. */
|
|
28
|
+
readonly apiEndpoint?: string; /** Default region when create requests omit one. */
|
|
29
|
+
readonly defaultRegion?: DenoRegion;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Deno-narrowed service. `create` accepts the narrowed
|
|
33
|
+
* {@link DenoSandboxCreateRequest}; every other method is identical to
|
|
34
|
+
* the generic {@link SandboxService} except for the per-provider
|
|
35
|
+
* `snapshotVolume` escape hatch — Deno snapshots are derived from
|
|
36
|
+
* volumes, not from live sandboxes, so the generic
|
|
37
|
+
* `Sandbox.snapshots.create(fromInstance)` doesn't fit and fails with
|
|
38
|
+
* `SandboxUnsupported`.
|
|
39
|
+
*/
|
|
40
|
+
type DenoSandboxService = Omit<SandboxService, "create"> & {
|
|
41
|
+
readonly create: (request: DenoSandboxCreateRequest) => Effect.Effect<SandboxInstance, SandboxError.SandboxError, Scope.Scope>;
|
|
42
|
+
/**
|
|
43
|
+
* Snapshot an existing volume by id/slug. Deno's snapshot model is
|
|
44
|
+
* volume-derived, so the generic `Sandbox.snapshots.create` doesn't
|
|
45
|
+
* apply — use this escape hatch instead. The volume must not be
|
|
46
|
+
* attached to any running sandbox at the time of snapshot.
|
|
47
|
+
*/
|
|
48
|
+
readonly snapshotVolume: (volumeId: VolumeId, slug?: string) => Effect.Effect<SnapshotId, SandboxError.SandboxError>;
|
|
49
|
+
};
|
|
50
|
+
declare const DenoSandbox_base: Context.ServiceClass<DenoSandbox, "@betalyra/effect-uai/providers/deno/DenoSandbox", DenoSandboxService>;
|
|
51
|
+
declare class DenoSandbox extends DenoSandbox_base {}
|
|
52
|
+
/**
|
|
53
|
+
* Layer registering the Deno Sandbox adapter against `Sandbox`, with
|
|
54
|
+
* the capability markers Deno actually supports.
|
|
55
|
+
*
|
|
56
|
+
* Shipped:
|
|
57
|
+
* - {@link SandboxHostnameAllowlist} — `allowNet` accepts hostnames + wildcards.
|
|
58
|
+
* - {@link SandboxSecretInjection} — `secrets.<name>.value` is injected
|
|
59
|
+
* only on outbound HTTPS to bound hosts; in-VM env shows a placeholder.
|
|
60
|
+
* - {@link SandboxSnapshots} — read-side helpers (`list`, `destroy`)
|
|
61
|
+
* work. `snapshots.create(fromInstance)` fails with
|
|
62
|
+
* `SandboxUnsupported`; use the per-provider
|
|
63
|
+
* `DenoSandbox.snapshotVolume` escape hatch for the volume-derived
|
|
64
|
+
* snapshot workflow.
|
|
65
|
+
* - {@link SandboxVolumes} — `client.volumes.*` directly.
|
|
66
|
+
* - {@link SandboxPortExposure} — `sandbox.exposeHttp({ port })`.
|
|
67
|
+
*
|
|
68
|
+
* NOT shipped (calling those helpers fails at compile time):
|
|
69
|
+
* - `SandboxCustomImage` — no Dockerfile / no OCI registry refs.
|
|
70
|
+
* - `SandboxPauseResume` — Deno doesn't offer in-place memory-preserving pause.
|
|
71
|
+
* - `SandboxKernelSession`, `SandboxPty` — no Jupyter / no PTY.
|
|
72
|
+
*/
|
|
73
|
+
declare const layer: (config?: DenoSandboxConfig) => Layer.Layer<DenoSandbox | Sandbox | SandboxHostnameAllowlist | SandboxSecretInjection | SandboxSnapshots | SandboxVolumes | SandboxPortExposure, never, never>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { ApiError, ConnectionClosedError, ConnectionEstablishmentError, DenoRegion, DenoSandbox, DenoSandboxBoundSecret, DenoSandboxConfig, DenoSandboxCreateRequest, DenoSandboxService, InvalidMemoryError, InvalidTimeoutError, InvalidTokenError, MissingTokenError, SandboxCommandError, SandboxKillError, layer, DenoSandbox_d_exports as t };
|
|
76
|
+
//# sourceMappingURL=DenoSandbox.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DenoSandbox.d.mts","names":[],"sources":["../src/DenoSandbox.ts"],"mappings":";;;;;;;;;;KAoEY,sBAAA,GAAyB,IAAA,CAAK,WAAA;AAAA,KAE9B,UAAA;AAAA,KAEA,wBAAA,GAA2B,IAAA,CAAK,mBAAA;EAAA,SACjC,OAAA,GAAU,aAAA,CAAc,sBAAA;EAAA,SACxB,MAAA,GAAS,UAAA;;;;;;WAMT,MAAA,GAAS,MAAA,CAAO,KAAA;EAAA,SAChB,MAAA,GAAS,QAAA,CAAS,MAAA;WAElB,IAAA;AAAA;AAAA,KAGC,iBAAA;gDAED,KAAA,GAAQ,QAAA,CAAS,QAAA;WAEjB,GAAA;WAEA,WAAA,WAxBuB;EAAA,SA0BvB,aAAA,GAAgB,UAAA;AAAA;;AAxB3B;;;;;AAEA;;;KAkCY,kBAAA,GAAqB,IAAA,CAAK,cAAA;EAAA,SAC3B,MAAA,GACP,OAAA,EAAS,wBAAA,KACN,MAAA,CAAO,MAAA,CAAO,eAAA,EAAiB,YAAA,CAAa,YAAA,EAAc,KAAA,CAAM,KAAA;EApCpC;;;;;;EAAA,SA2CxB,cAAA,GACP,QAAA,EAAU,QAAA,EACV,IAAA,cACG,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,YAAA,CAAa,YAAA;AAAA;AAAA,cAC7C,gBAAA;cAEY,WAAA,SAAoB,gBAAA;;;;;;;;;;;;;;AApCjC;;;;;;;;cA2zBa,KAAA,GAAS,MAAA,GAAQ,iBAAA,KAAsB,KAAA,CAAA,KAAA,CAAA,WAAA,GAAA,OAAA,GAAA,wBAAA,GAAA,sBAAA,GAAA,gBAAA,GAAA,cAAA,GAAA,mBAAA"}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import * as Memory from "@effect-uai/core/Memory";
|
|
3
|
+
import { ExecEvent, Sandbox, SandboxHostnameAllowlist, SandboxId, SandboxPortExposure, SandboxSecretInjection, SandboxSnapshots, SandboxVolumes, SnapshotId, VolumeId } from "@effect-uai/core/Sandbox";
|
|
4
|
+
import * as SandboxError from "@effect-uai/core/SandboxError";
|
|
5
|
+
import { Array, Clock, Context, Duration, Effect, Layer, Match, Option, Random, Redacted, Schedule, Stream } from "effect";
|
|
6
|
+
import { ApiError, Client, ConnectionClosedError, ConnectionEstablishmentError, InvalidMemoryError, InvalidTimeoutError, InvalidTokenError, MissingTokenError, Sandbox as Sandbox$1, SandboxCommandError, SandboxKillError } from "@deno/sandbox";
|
|
7
|
+
//#region src/DenoSandbox.ts
|
|
8
|
+
var DenoSandbox_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
ApiError: () => ApiError,
|
|
10
|
+
ConnectionClosedError: () => ConnectionClosedError,
|
|
11
|
+
ConnectionEstablishmentError: () => ConnectionEstablishmentError,
|
|
12
|
+
DenoSandbox: () => DenoSandbox,
|
|
13
|
+
InvalidMemoryError: () => InvalidMemoryError,
|
|
14
|
+
InvalidTimeoutError: () => InvalidTimeoutError,
|
|
15
|
+
InvalidTokenError: () => InvalidTokenError,
|
|
16
|
+
MissingTokenError: () => MissingTokenError,
|
|
17
|
+
SandboxCommandError: () => SandboxCommandError,
|
|
18
|
+
SandboxKillError: () => SandboxKillError,
|
|
19
|
+
layer: () => layer
|
|
20
|
+
});
|
|
21
|
+
const PROVIDER = "deno";
|
|
22
|
+
var DenoSandbox = class extends Context.Service()("@betalyra/effect-uai/providers/deno/DenoSandbox") {};
|
|
23
|
+
/** Random base-36 token, ~8 chars. */
|
|
24
|
+
const randomToken = Random.next.pipe(Effect.map((r) => r.toString(36).slice(2, 10)));
|
|
25
|
+
const reasonOf = (e) => e instanceof Error ? { reason: e.message } : {};
|
|
26
|
+
const isAuthError = (e) => e instanceof MissingTokenError || e instanceof InvalidTokenError || e instanceof ApiError && (e.status === 401 || e.status === 403) || e instanceof ConnectionEstablishmentError && (e.status === 401 || e.status === 403);
|
|
27
|
+
const isQuotaError = (e) => e instanceof ApiError && e.status === 429 || e instanceof ConnectionEstablishmentError && e.status === 429;
|
|
28
|
+
/**
|
|
29
|
+
* "Sandbox is no longer reachable" — covers both the 404 (never existed)
|
|
30
|
+
* and the 400 + `SANDBOX_ALREADY_TERMINATED` case the API returns when
|
|
31
|
+
* you `connect` to a sandbox after its `kill`. Both map to
|
|
32
|
+
* `SandboxNotFound` so `destroy` and friends stay idempotent.
|
|
33
|
+
*/
|
|
34
|
+
const isNotFoundError = (e) => e instanceof ApiError && (e.status === 404 || e.code === "SANDBOX_ALREADY_TERMINATED") || e instanceof ConnectionEstablishmentError && (e.status === 404 || e.code === "SANDBOX_ALREADY_TERMINATED");
|
|
35
|
+
const mapCreateError = (e) => isAuthError(e) ? new SandboxError.SandboxAuthFailed({
|
|
36
|
+
provider: PROVIDER,
|
|
37
|
+
subtype: "auth",
|
|
38
|
+
raw: e
|
|
39
|
+
}) : isQuotaError(e) ? new SandboxError.SandboxQuotaExceeded({
|
|
40
|
+
provider: PROVIDER,
|
|
41
|
+
raw: e,
|
|
42
|
+
...reasonOf(e)
|
|
43
|
+
}) : e instanceof InvalidTimeoutError || e instanceof InvalidMemoryError ? new SandboxError.SandboxInvalidRequest({
|
|
44
|
+
provider: PROVIDER,
|
|
45
|
+
raw: e,
|
|
46
|
+
...reasonOf(e)
|
|
47
|
+
}) : new SandboxError.SandboxCreateFailed({
|
|
48
|
+
provider: PROVIDER,
|
|
49
|
+
raw: e,
|
|
50
|
+
...reasonOf(e)
|
|
51
|
+
});
|
|
52
|
+
const mapLookupError = (id) => (e) => isNotFoundError(e) ? new SandboxError.SandboxNotFound({
|
|
53
|
+
provider: PROVIDER,
|
|
54
|
+
id
|
|
55
|
+
}) : mapCreateError(e);
|
|
56
|
+
const mapExecError = (e) => e instanceof SandboxCommandError ? new SandboxError.SandboxExecFailed({
|
|
57
|
+
provider: PROVIDER,
|
|
58
|
+
raw: e,
|
|
59
|
+
reason: e.message
|
|
60
|
+
}) : e instanceof ConnectionClosedError ? new SandboxError.SandboxExecFailed({
|
|
61
|
+
provider: PROVIDER,
|
|
62
|
+
raw: e,
|
|
63
|
+
reason: e.reason ?? "connection closed"
|
|
64
|
+
}) : new SandboxError.SandboxExecFailed({
|
|
65
|
+
provider: PROVIDER,
|
|
66
|
+
raw: e,
|
|
67
|
+
...reasonOf(e)
|
|
68
|
+
});
|
|
69
|
+
const mapFsError = (e) => new SandboxError.SandboxExecFailed({
|
|
70
|
+
provider: PROVIDER,
|
|
71
|
+
raw: e,
|
|
72
|
+
...reasonOf(e)
|
|
73
|
+
});
|
|
74
|
+
const isTransientWsHandshake = (e) => (e._tag === "SandboxExecFailed" || e._tag === "SandboxCreateFailed") && e.raw instanceof ConnectionEstablishmentError && e.raw.code === "SANDBOX_WEBSOCKET_HANDSHAKE_ERROR";
|
|
75
|
+
const wsHandshakeSchedule = Schedule.exponential("500 millis");
|
|
76
|
+
const WS_HANDSHAKE_RETRIES = 3;
|
|
77
|
+
const retryOnTransientWs = (effect) => Effect.retry(effect, {
|
|
78
|
+
schedule: wsHandshakeSchedule,
|
|
79
|
+
times: WS_HANDSHAKE_RETRIES,
|
|
80
|
+
while: isTransientWsHandshake
|
|
81
|
+
});
|
|
82
|
+
const noop = (p) => p;
|
|
83
|
+
const when = (value, step) => value === void 0 ? noop : step(value);
|
|
84
|
+
const set = (key, value) => (p) => ({
|
|
85
|
+
...p,
|
|
86
|
+
[key]: value
|
|
87
|
+
});
|
|
88
|
+
const toDenoSeconds = (d) => {
|
|
89
|
+
return `${Math.max(1, Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1e3))}s`;
|
|
90
|
+
};
|
|
91
|
+
const imageStep = (image) => {
|
|
92
|
+
if (image === void 0) return Effect.succeed(noop);
|
|
93
|
+
return Match.value(image).pipe(Match.tag("Default", () => Effect.succeed(noop)), Match.tag("Snapshot", ({ id }) => Effect.succeed(set("root", id))), Match.tag("Registry", () => Effect.fail(new SandboxError.SandboxUnsupported({
|
|
94
|
+
provider: PROVIDER,
|
|
95
|
+
capability: "image.registry",
|
|
96
|
+
reason: "deno sandboxes do not accept OCI registry refs — boot from a snapshot instead"
|
|
97
|
+
}))), Match.tag("Dockerfile", () => Effect.fail(new SandboxError.SandboxUnsupported({
|
|
98
|
+
provider: PROVIDER,
|
|
99
|
+
capability: "image.dockerfile",
|
|
100
|
+
reason: "deno sandboxes do not accept Dockerfiles — install software into a bootable volume then snapshot it"
|
|
101
|
+
}))), Match.exhaustive);
|
|
102
|
+
};
|
|
103
|
+
const networkStep = (policy) => Match.value(policy).pipe(Match.tag("Open", () => Effect.succeed(noop)), Match.tag("Blocked", () => Effect.succeed(set("allowNet", []))), Match.tag("Allowlist", ({ hosts, cidrs }) => cidrs !== void 0 && cidrs.length > 0 ? Effect.fail(new SandboxError.SandboxUnsupported({
|
|
104
|
+
provider: PROVIDER,
|
|
105
|
+
capability: "network.cidrs",
|
|
106
|
+
reason: "deno's allowNet accepts hostnames or literal IPs, not CIDR ranges — use hosts instead"
|
|
107
|
+
})) : Effect.succeed(set("allowNet", [...hosts ?? []]))), Match.exhaustive);
|
|
108
|
+
const volumesStep = (volumes) => {
|
|
109
|
+
if (volumes === void 0 || volumes.length === 0) return Effect.succeed(noop);
|
|
110
|
+
if (volumes.some((v) => v.readonly === true)) return Effect.fail(new SandboxError.SandboxUnsupported({
|
|
111
|
+
provider: PROVIDER,
|
|
112
|
+
capability: "volumes.readonly",
|
|
113
|
+
reason: "deno mounts volumes read-write — read-only mounts are not supported"
|
|
114
|
+
}));
|
|
115
|
+
const map = Array.reduce(volumes, {}, (acc, v) => {
|
|
116
|
+
acc[v.mountPath] = v.id;
|
|
117
|
+
return acc;
|
|
118
|
+
});
|
|
119
|
+
return Effect.succeed(set("volumes", map));
|
|
120
|
+
};
|
|
121
|
+
const secretsStep = (secrets) => {
|
|
122
|
+
return set("secrets", Array.reduce(secrets, {}, (acc, s) => {
|
|
123
|
+
acc[s.name] = {
|
|
124
|
+
hosts: [...s.hosts],
|
|
125
|
+
value: Redacted.value(s.value)
|
|
126
|
+
};
|
|
127
|
+
return acc;
|
|
128
|
+
}));
|
|
129
|
+
};
|
|
130
|
+
const baseClientOptions = (config) => ({
|
|
131
|
+
...config.token === void 0 ? {} : { token: Redacted.value(config.token) },
|
|
132
|
+
...config.org === void 0 ? {} : { org: config.org },
|
|
133
|
+
...config.apiEndpoint === void 0 ? {} : { apiEndpoint: config.apiEndpoint }
|
|
134
|
+
});
|
|
135
|
+
const argv = (cmd) => typeof cmd === "string" ? Effect.succeed({
|
|
136
|
+
command: "bash",
|
|
137
|
+
args: ["-c", cmd]
|
|
138
|
+
}) : Option.match(Array.head(cmd), {
|
|
139
|
+
onNone: () => Effect.fail(new SandboxError.SandboxInvalidRequest({
|
|
140
|
+
provider: PROVIDER,
|
|
141
|
+
param: "cmd",
|
|
142
|
+
reason: "argv array must be non-empty"
|
|
143
|
+
})),
|
|
144
|
+
onSome: (head) => Effect.succeed({
|
|
145
|
+
command: head,
|
|
146
|
+
args: Array.drop(cmd, 1)
|
|
147
|
+
})
|
|
148
|
+
});
|
|
149
|
+
const acquireAbortController = Effect.acquireRelease(Effect.sync(() => new AbortController()), (ac) => Effect.sync(() => ac.abort()));
|
|
150
|
+
const stdinIsStream = (stdin) => stdin !== void 0 && typeof stdin !== "string" && !(stdin instanceof Uint8Array);
|
|
151
|
+
/**
|
|
152
|
+
* Copy into a fresh `Uint8Array<ArrayBuffer>`. The Deno SDK strictly
|
|
153
|
+
* types its bytes argument as `Uint8Array<ArrayBuffer>` (not the
|
|
154
|
+
* `ArrayBufferLike` upper bound), so `TextEncoder`-produced bytes and
|
|
155
|
+
* caller-supplied Uint8Arrays need to be copied to a guaranteed-
|
|
156
|
+
* `ArrayBuffer`-backed buffer to satisfy the type.
|
|
157
|
+
*/
|
|
158
|
+
const toBytes = (input) => {
|
|
159
|
+
const src = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
160
|
+
const out = new Uint8Array(new ArrayBuffer(src.byteLength));
|
|
161
|
+
out.set(src);
|
|
162
|
+
return out;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Acquire the child's stdin writer, run `use(writer)`, then close the
|
|
166
|
+
* writer on release (regardless of success / failure / interrupt).
|
|
167
|
+
* Returns `Effect.void` if the child wasn't spawned with `stdin: "piped"`.
|
|
168
|
+
*/
|
|
169
|
+
const withStdinWriter = (child, use) => {
|
|
170
|
+
const stdin = child.stdin;
|
|
171
|
+
if (stdin === null) return Effect.void;
|
|
172
|
+
return Effect.acquireUseRelease(Effect.sync(() => stdin.getWriter()), use, (writer) => Effect.promise(() => writer.close().catch(() => void 0)).pipe(Effect.ignore));
|
|
173
|
+
};
|
|
174
|
+
const writeStdinBytes = (child, bytes) => withStdinWriter(child, (writer) => Effect.tryPromise({
|
|
175
|
+
try: () => writer.write(bytes),
|
|
176
|
+
catch: mapExecError
|
|
177
|
+
})).pipe(Effect.asVoid);
|
|
178
|
+
const feedStreamStdin = (child, source) => withStdinWriter(child, (writer) => Stream.runForEach(source, (chunk) => Effect.tryPromise({
|
|
179
|
+
try: () => writer.write(chunk),
|
|
180
|
+
catch: mapExecError
|
|
181
|
+
}))).pipe(Effect.asVoid);
|
|
182
|
+
const streamFromReadable = (readable) => Stream.fromReadableStream({
|
|
183
|
+
evaluate: () => readable,
|
|
184
|
+
onError: mapExecError
|
|
185
|
+
});
|
|
186
|
+
const eventStream = (child, startedAt) => {
|
|
187
|
+
const stdout = child.stdout === null ? Stream.empty : Stream.map(streamFromReadable(child.stdout), (chunk) => ExecEvent.Stdout({ chunk }));
|
|
188
|
+
const stderr = child.stderr === null ? Stream.empty : Stream.map(streamFromReadable(child.stderr), (chunk) => ExecEvent.Stderr({ chunk }));
|
|
189
|
+
const complete = Stream.unwrap(Effect.gen(function* () {
|
|
190
|
+
const status = yield* Effect.tryPromise({
|
|
191
|
+
try: () => child.status,
|
|
192
|
+
catch: mapExecError
|
|
193
|
+
});
|
|
194
|
+
const now = yield* Clock.currentTimeMillis;
|
|
195
|
+
return Stream.succeed(ExecEvent.Complete({
|
|
196
|
+
exitCode: status.code,
|
|
197
|
+
durationMs: now - startedAt
|
|
198
|
+
}));
|
|
199
|
+
}));
|
|
200
|
+
return Stream.concat(Stream.merge(stdout, stderr), complete);
|
|
201
|
+
};
|
|
202
|
+
const withExecTimeout = (timeout, effect) => timeout === void 0 ? effect : Effect.race(effect, Effect.sleep(timeout).pipe(Effect.andThen(Effect.fail(new SandboxError.SandboxTimeout({
|
|
203
|
+
provider: PROVIDER,
|
|
204
|
+
operation: "exec",
|
|
205
|
+
budget: Duration.fromInputUnsafe(timeout)
|
|
206
|
+
})))));
|
|
207
|
+
const adaptInstance = (sdk, portRegistry) => {
|
|
208
|
+
const id = SandboxId(sdk.id);
|
|
209
|
+
const openProcess = (request) => Effect.gen(function* () {
|
|
210
|
+
const { command, args } = yield* argv(request.cmd);
|
|
211
|
+
const ac = yield* acquireAbortController;
|
|
212
|
+
const stdinMode = request.stdin === void 0 ? "null" : "piped";
|
|
213
|
+
const startedAt = yield* Clock.currentTimeMillis;
|
|
214
|
+
const child = yield* Effect.acquireRelease(retryOnTransientWs(Effect.tryPromise({
|
|
215
|
+
try: () => sdk.spawn(command, {
|
|
216
|
+
args: [...args],
|
|
217
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
218
|
+
...request.env === void 0 ? {} : { env: { ...request.env } },
|
|
219
|
+
stdin: stdinMode,
|
|
220
|
+
stdout: "piped",
|
|
221
|
+
stderr: "piped",
|
|
222
|
+
signal: ac.signal
|
|
223
|
+
}),
|
|
224
|
+
catch: mapExecError
|
|
225
|
+
})), (c) => Effect.promise(() => c.kill("SIGKILL").catch(() => void 0)).pipe(Effect.ignore));
|
|
226
|
+
if (request.stdin !== void 0) if (stdinIsStream(request.stdin)) yield* Effect.forkScoped(feedStreamStdin(child, request.stdin));
|
|
227
|
+
else yield* writeStdinBytes(child, toBytes(request.stdin));
|
|
228
|
+
return {
|
|
229
|
+
child,
|
|
230
|
+
startedAt
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
const instance = {
|
|
234
|
+
id,
|
|
235
|
+
exec: (request) => withExecTimeout(request.timeout, Effect.scoped(Effect.gen(function* () {
|
|
236
|
+
const { child, startedAt } = yield* openProcess(request);
|
|
237
|
+
const output = yield* Effect.tryPromise({
|
|
238
|
+
try: () => child.output(),
|
|
239
|
+
catch: mapExecError
|
|
240
|
+
});
|
|
241
|
+
const completedAt = yield* Clock.currentTimeMillis;
|
|
242
|
+
return {
|
|
243
|
+
exitCode: output.status.code,
|
|
244
|
+
stdout: output.stdoutText ?? "",
|
|
245
|
+
stderr: output.stderrText ?? "",
|
|
246
|
+
durationMs: completedAt - startedAt
|
|
247
|
+
};
|
|
248
|
+
}))),
|
|
249
|
+
execStream: (request) => Stream.unwrap(Effect.map(openProcess(request), ({ child, startedAt }) => eventStream(child, startedAt))),
|
|
250
|
+
spawn: (request) => Effect.map(openProcess(request), ({ child, startedAt }) => ({
|
|
251
|
+
pid: child.pid,
|
|
252
|
+
events: eventStream(child, startedAt),
|
|
253
|
+
kill: Effect.tryPromise({
|
|
254
|
+
try: () => child.kill("SIGTERM"),
|
|
255
|
+
catch: mapExecError
|
|
256
|
+
}),
|
|
257
|
+
exit: Effect.tryPromise({
|
|
258
|
+
try: () => child.status,
|
|
259
|
+
catch: mapExecError
|
|
260
|
+
}).pipe(Effect.map((status) => ({ exitCode: status.code })))
|
|
261
|
+
})),
|
|
262
|
+
files: {
|
|
263
|
+
read: (path) => Effect.tryPromise({
|
|
264
|
+
try: () => sdk.fs.readFile(path),
|
|
265
|
+
catch: mapFsError
|
|
266
|
+
}),
|
|
267
|
+
write: (path, contents) => Effect.tryPromise({
|
|
268
|
+
try: () => sdk.fs.writeFile(path, toBytes(contents)),
|
|
269
|
+
catch: mapFsError
|
|
270
|
+
}),
|
|
271
|
+
remove: (path) => Effect.tryPromise({
|
|
272
|
+
try: () => sdk.fs.remove(path),
|
|
273
|
+
catch: mapFsError
|
|
274
|
+
}),
|
|
275
|
+
mkdir: (path) => Effect.tryPromise({
|
|
276
|
+
try: () => sdk.fs.mkdir(path, { recursive: true }),
|
|
277
|
+
catch: mapFsError
|
|
278
|
+
}),
|
|
279
|
+
list: (path) => Stream.fromAsyncIterable(sdk.fs.readDir(path), mapFsError).pipe(Stream.map((e) => ({
|
|
280
|
+
path: e.name,
|
|
281
|
+
kind: e.isFile ? "file" : e.isDirectory ? "directory" : e.isSymlink ? "symlink" : "other"
|
|
282
|
+
})), Stream.runCollect),
|
|
283
|
+
exists: (path) => Effect.tryPromise({
|
|
284
|
+
try: () => sdk.fs.stat(path),
|
|
285
|
+
catch: mapFsError
|
|
286
|
+
}).pipe(Effect.as(true), Effect.catchCause(() => Effect.succeed(false)))
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
portRegistry.set(instance, sdk);
|
|
290
|
+
return instance;
|
|
291
|
+
};
|
|
292
|
+
const buildService = (config) => {
|
|
293
|
+
const clientOpts = baseClientOptions(config);
|
|
294
|
+
const client = new Client(clientOpts);
|
|
295
|
+
const portRegistry = /* @__PURE__ */ new WeakMap();
|
|
296
|
+
const buildOptions = (request) => Effect.gen(function* () {
|
|
297
|
+
const img = yield* imageStep(request.image);
|
|
298
|
+
const net = yield* request.network === void 0 ? Effect.succeed(noop) : networkStep(request.network);
|
|
299
|
+
const vols = yield* volumesStep(request.volumes);
|
|
300
|
+
const steps = [
|
|
301
|
+
img,
|
|
302
|
+
when(request.region ?? config.defaultRegion, (r) => set("region", r)),
|
|
303
|
+
when(request.env, (env) => set("env", { ...env })),
|
|
304
|
+
when(request.timeout, (t) => set("timeout", toDenoSeconds(t))),
|
|
305
|
+
when(request.memory, (m) => set("memory", Memory.toBytes(m))),
|
|
306
|
+
when(request.labels, (l) => set("labels", { ...l })),
|
|
307
|
+
net,
|
|
308
|
+
vols,
|
|
309
|
+
when(request.secrets, (s) => s.length === 0 ? noop : secretsStep(s)),
|
|
310
|
+
when(request.port, (p) => set("port", p))
|
|
311
|
+
];
|
|
312
|
+
const overlay = Array.reduce(steps, {}, (acc, step) => step(acc));
|
|
313
|
+
return {
|
|
314
|
+
...clientOpts,
|
|
315
|
+
...overlay
|
|
316
|
+
};
|
|
317
|
+
});
|
|
318
|
+
const acquireSdk = (request) => Effect.flatMap(buildOptions(request), (opts) => Effect.tryPromise({
|
|
319
|
+
try: () => Sandbox$1.create(opts),
|
|
320
|
+
catch: mapCreateError
|
|
321
|
+
}));
|
|
322
|
+
const releaseSdk = (sdk) => Effect.promise(() => sdk.kill().catch(() => void 0)).pipe(Effect.ignore);
|
|
323
|
+
const releaseAttached = (sdk) => Effect.promise(() => sdk.close().catch(() => void 0)).pipe(Effect.ignore);
|
|
324
|
+
const destroyById = (id) => Effect.tryPromise({
|
|
325
|
+
try: () => Sandbox$1.connect(id, clientOpts),
|
|
326
|
+
catch: mapLookupError(id)
|
|
327
|
+
}).pipe(Effect.flatMap((sdk) => Effect.tryPromise({
|
|
328
|
+
try: () => sdk.kill(),
|
|
329
|
+
catch: mapLookupError(id)
|
|
330
|
+
})), Effect.catchTag("SandboxNotFound", () => Effect.void));
|
|
331
|
+
return {
|
|
332
|
+
create: (request) => Effect.acquireRelease(acquireSdk(request), releaseSdk).pipe(Effect.map((sdk) => adaptInstance(sdk, portRegistry))),
|
|
333
|
+
attach: (id) => Effect.acquireRelease(Effect.tryPromise({
|
|
334
|
+
try: () => Sandbox$1.connect(id, clientOpts),
|
|
335
|
+
catch: mapLookupError(id)
|
|
336
|
+
}), releaseAttached).pipe(Effect.map((sdk) => adaptInstance(sdk, portRegistry))),
|
|
337
|
+
list: Effect.tryPromise({
|
|
338
|
+
try: () => client.sandboxes.list(),
|
|
339
|
+
catch: mapCreateError
|
|
340
|
+
}).pipe(Effect.map(Array.map((m) => ({ id: SandboxId(m.id) })))),
|
|
341
|
+
destroy: destroyById,
|
|
342
|
+
snapshotVolume: (volumeId, slug) => Effect.gen(function* () {
|
|
343
|
+
const snapSlug = slug ?? `eff-uai-snap-${yield* randomToken}`;
|
|
344
|
+
return SnapshotId((yield* Effect.tryPromise({
|
|
345
|
+
try: () => client.volumes.snapshot(volumeId, { slug: snapSlug }),
|
|
346
|
+
catch: mapCreateError
|
|
347
|
+
})).slug);
|
|
348
|
+
}),
|
|
349
|
+
snapshots: {
|
|
350
|
+
create: () => Effect.fail(new SandboxError.SandboxUnsupported({
|
|
351
|
+
provider: PROVIDER,
|
|
352
|
+
capability: "snapshots.create",
|
|
353
|
+
reason: "deno snapshots are derived from volumes, not from running sandboxes — use DenoSandbox.snapshotVolume(volumeId, slug?) instead"
|
|
354
|
+
})),
|
|
355
|
+
destroy: (sid) => Effect.tryPromise({
|
|
356
|
+
try: () => client.snapshots.delete(sid),
|
|
357
|
+
catch: mapLookupError(sid)
|
|
358
|
+
}),
|
|
359
|
+
list: Stream.unwrap(Effect.tryPromise({
|
|
360
|
+
try: () => client.snapshots.list(),
|
|
361
|
+
catch: mapCreateError
|
|
362
|
+
}).pipe(Effect.map((page) => Stream.fromAsyncIterable(page, mapCreateError)))).pipe(Stream.map((snap) => ({
|
|
363
|
+
id: SnapshotId(snap.id),
|
|
364
|
+
name: snap.slug
|
|
365
|
+
})), Stream.runCollect)
|
|
366
|
+
},
|
|
367
|
+
volumes: {
|
|
368
|
+
create: (name, options) => Effect.tryPromise({
|
|
369
|
+
try: () => client.volumes.create({
|
|
370
|
+
slug: name,
|
|
371
|
+
region: config.defaultRegion ?? "ord",
|
|
372
|
+
capacity: options?.quotaBytes ?? "1GB"
|
|
373
|
+
}),
|
|
374
|
+
catch: mapCreateError
|
|
375
|
+
}).pipe(Effect.map((vol) => VolumeId(vol.slug))),
|
|
376
|
+
destroy: (vid) => Effect.tryPromise({
|
|
377
|
+
try: () => client.volumes.delete(vid),
|
|
378
|
+
catch: mapLookupError(vid)
|
|
379
|
+
}),
|
|
380
|
+
list: Stream.unwrap(Effect.tryPromise({
|
|
381
|
+
try: () => client.volumes.list(),
|
|
382
|
+
catch: mapCreateError
|
|
383
|
+
}).pipe(Effect.map((page) => Stream.fromAsyncIterable(page, mapCreateError)))).pipe(Stream.map((vol) => ({
|
|
384
|
+
id: VolumeId(vol.slug),
|
|
385
|
+
name: vol.slug
|
|
386
|
+
})), Stream.runCollect)
|
|
387
|
+
},
|
|
388
|
+
ports: { expose: (instance, port) => Effect.gen(function* () {
|
|
389
|
+
const sdk = portRegistry.get(instance);
|
|
390
|
+
if (sdk === void 0) return yield* Effect.fail(new SandboxError.SandboxNotFound({
|
|
391
|
+
provider: PROVIDER,
|
|
392
|
+
id: instance.id
|
|
393
|
+
}));
|
|
394
|
+
return { url: yield* Effect.tryPromise({
|
|
395
|
+
try: () => sdk.exposeHttp({ port }),
|
|
396
|
+
catch: mapExecError
|
|
397
|
+
}) };
|
|
398
|
+
}) }
|
|
399
|
+
};
|
|
400
|
+
};
|
|
401
|
+
const rejectCustomHeader = (secrets) => secrets === void 0 || !secrets.some((s) => s.header !== void 0) ? Effect.void : Effect.fail(new SandboxError.SandboxUnsupported({
|
|
402
|
+
provider: PROVIDER,
|
|
403
|
+
capability: "BoundSecret.header",
|
|
404
|
+
reason: "deno always injects secrets as `Authorization: Bearer <value>` — custom headers aren't supported. Use the narrowed `DenoSandbox.create` surface, which omits this field."
|
|
405
|
+
}));
|
|
406
|
+
const upcastService = (s) => ({
|
|
407
|
+
...s,
|
|
408
|
+
create: (request) => rejectCustomHeader(request.secrets).pipe(Effect.andThen(s.create(request)))
|
|
409
|
+
});
|
|
410
|
+
/**
|
|
411
|
+
* Layer registering the Deno Sandbox adapter against `Sandbox`, with
|
|
412
|
+
* the capability markers Deno actually supports.
|
|
413
|
+
*
|
|
414
|
+
* Shipped:
|
|
415
|
+
* - {@link SandboxHostnameAllowlist} — `allowNet` accepts hostnames + wildcards.
|
|
416
|
+
* - {@link SandboxSecretInjection} — `secrets.<name>.value` is injected
|
|
417
|
+
* only on outbound HTTPS to bound hosts; in-VM env shows a placeholder.
|
|
418
|
+
* - {@link SandboxSnapshots} — read-side helpers (`list`, `destroy`)
|
|
419
|
+
* work. `snapshots.create(fromInstance)` fails with
|
|
420
|
+
* `SandboxUnsupported`; use the per-provider
|
|
421
|
+
* `DenoSandbox.snapshotVolume` escape hatch for the volume-derived
|
|
422
|
+
* snapshot workflow.
|
|
423
|
+
* - {@link SandboxVolumes} — `client.volumes.*` directly.
|
|
424
|
+
* - {@link SandboxPortExposure} — `sandbox.exposeHttp({ port })`.
|
|
425
|
+
*
|
|
426
|
+
* NOT shipped (calling those helpers fails at compile time):
|
|
427
|
+
* - `SandboxCustomImage` — no Dockerfile / no OCI registry refs.
|
|
428
|
+
* - `SandboxPauseResume` — Deno doesn't offer in-place memory-preserving pause.
|
|
429
|
+
* - `SandboxKernelSession`, `SandboxPty` — no Jupyter / no PTY.
|
|
430
|
+
*/
|
|
431
|
+
const layer = (config = {}) => {
|
|
432
|
+
const service = buildService(config);
|
|
433
|
+
return Layer.mergeAll(Layer.succeed(DenoSandbox, service), Layer.succeed(Sandbox, upcastService(service)), Layer.succeed(SandboxHostnameAllowlist, void 0), Layer.succeed(SandboxSecretInjection, void 0), Layer.succeed(SandboxSnapshots, void 0), Layer.succeed(SandboxVolumes, void 0), Layer.succeed(SandboxPortExposure, void 0));
|
|
434
|
+
};
|
|
435
|
+
//#endregion
|
|
436
|
+
export { ApiError, ConnectionClosedError, ConnectionEstablishmentError, DenoSandbox, InvalidMemoryError, InvalidTimeoutError, InvalidTokenError, MissingTokenError, SandboxCommandError, SandboxKillError, layer, DenoSandbox_exports as t };
|
|
437
|
+
|
|
438
|
+
//# sourceMappingURL=DenoSandbox.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DenoSandbox.mjs","names":["Arr","CoreExecEvent","DenoSdkSandbox","CoreSandbox"],"sources":["../src/DenoSandbox.ts"],"sourcesContent":["import * as Memory from \"@effect-uai/core/Memory\"\nimport {\n ExecEvent as CoreExecEvent,\n Sandbox as CoreSandbox,\n SandboxHostnameAllowlist,\n SandboxPortExposure,\n SandboxSecretInjection,\n SandboxSnapshots,\n SandboxVolumes,\n type BoundSecret,\n type CommonCreateRequest,\n type CommonExecRequest,\n type CommonSpawnRequest,\n type ExecResult,\n type ImageRef,\n type NetworkPolicy,\n type ProcessHandle,\n SandboxId,\n type SandboxInstance,\n type SandboxRef,\n type SandboxService,\n SnapshotId,\n VolumeId,\n} from \"@effect-uai/core/Sandbox\"\nimport * as SandboxError from \"@effect-uai/core/SandboxError\"\nimport {\n Array as Arr,\n Clock,\n Context,\n Duration,\n Effect,\n Layer,\n Match,\n Option,\n Random,\n Redacted,\n Schedule,\n Scope,\n Stream,\n} from \"effect\"\nimport {\n ApiError,\n Client,\n ConnectionClosedError,\n ConnectionEstablishmentError,\n InvalidMemoryError,\n InvalidTimeoutError,\n InvalidTokenError,\n MissingTokenError,\n Sandbox as DenoSdkSandbox,\n SandboxCommandError,\n SandboxKillError,\n type ChildProcess as DenoChildProcess,\n type SandboxOptions,\n type SecretConfig,\n} from \"@deno/sandbox\"\n\nconst PROVIDER = \"deno\"\n\n// ---------------------------------------------------------------------------\n// Provider-narrowed request types.\n//\n// Deno's `secrets: Record<name, SecretConfig>` shape has no per-secret\n// custom header — substitution is always \"Authorization: Bearer <value>\".\n// We omit `header` on the typed surface (row B) and reject at the upcast\n// adapter (row D) if a caller routes through the generic `Sandbox.create`.\n// ---------------------------------------------------------------------------\n\nexport type DenoSandboxBoundSecret = Omit<BoundSecret, \"header\">\n\nexport type DenoRegion = \"ord\" | \"ams\"\n\nexport type DenoSandboxCreateRequest = Omit<CommonCreateRequest, \"secrets\"> & {\n readonly secrets?: ReadonlyArray<DenoSandboxBoundSecret>\n readonly region?: DenoRegion\n /**\n * Memory size for the sandbox. Accepts a byte count, a {@link Memory.Memory},\n * or a human string like `\"1280 MiB\"` / `\"1 GiB\"`. Deno's accepted\n * range at GA is 768 MiB – 4 GiB (default 1280 MiB).\n */\n readonly memory?: Memory.Input\n readonly labels?: Readonly<Record<string, string>>\n /** Auto-expose this port at boot; the live sandbox's `url` reaches it. */\n readonly port?: number\n}\n\nexport type DenoSandboxConfig = {\n /** Overrides `DENO_DEPLOY_TOKEN`. */\n readonly token?: Redacted.Redacted<string>\n /** Overrides `DENO_DEPLOY_ORG`. Required for personal `ddp_` tokens. */\n readonly org?: string\n /** Overrides `DENO_DEPLOY_ENDPOINT`. */\n readonly apiEndpoint?: string\n /** Default region when create requests omit one. */\n readonly defaultRegion?: DenoRegion\n}\n\n/**\n * Deno-narrowed service. `create` accepts the narrowed\n * {@link DenoSandboxCreateRequest}; every other method is identical to\n * the generic {@link SandboxService} except for the per-provider\n * `snapshotVolume` escape hatch — Deno snapshots are derived from\n * volumes, not from live sandboxes, so the generic\n * `Sandbox.snapshots.create(fromInstance)` doesn't fit and fails with\n * `SandboxUnsupported`.\n */\nexport type DenoSandboxService = Omit<SandboxService, \"create\"> & {\n readonly create: (\n request: DenoSandboxCreateRequest,\n ) => Effect.Effect<SandboxInstance, SandboxError.SandboxError, Scope.Scope>\n /**\n * Snapshot an existing volume by id/slug. Deno's snapshot model is\n * volume-derived, so the generic `Sandbox.snapshots.create` doesn't\n * apply — use this escape hatch instead. The volume must not be\n * attached to any running sandbox at the time of snapshot.\n */\n readonly snapshotVolume: (\n volumeId: VolumeId,\n slug?: string,\n ) => Effect.Effect<SnapshotId, SandboxError.SandboxError>\n}\n\nexport class DenoSandbox extends Context.Service<DenoSandbox, DenoSandboxService>()(\n \"@betalyra/effect-uai/providers/deno/DenoSandbox\",\n) {}\n\n// ---------------------------------------------------------------------------\n// Effect-native primitives\n// ---------------------------------------------------------------------------\n\n/** Random base-36 token, ~8 chars. */\nconst randomToken = Random.next.pipe(Effect.map((r) => r.toString(36).slice(2, 10)))\n\n// ---------------------------------------------------------------------------\n// Error mapping\n// ---------------------------------------------------------------------------\n\nconst reasonOf = (e: unknown) => (e instanceof Error ? { reason: e.message } : {})\n\nconst isAuthError = (e: unknown): boolean =>\n e instanceof MissingTokenError ||\n e instanceof InvalidTokenError ||\n (e instanceof ApiError && (e.status === 401 || e.status === 403)) ||\n (e instanceof ConnectionEstablishmentError && (e.status === 401 || e.status === 403))\n\nconst isQuotaError = (e: unknown): boolean =>\n (e instanceof ApiError && e.status === 429) ||\n (e instanceof ConnectionEstablishmentError && e.status === 429)\n\n/**\n * \"Sandbox is no longer reachable\" — covers both the 404 (never existed)\n * and the 400 + `SANDBOX_ALREADY_TERMINATED` case the API returns when\n * you `connect` to a sandbox after its `kill`. Both map to\n * `SandboxNotFound` so `destroy` and friends stay idempotent.\n */\nconst isNotFoundError = (e: unknown): boolean =>\n (e instanceof ApiError && (e.status === 404 || e.code === \"SANDBOX_ALREADY_TERMINATED\")) ||\n (e instanceof ConnectionEstablishmentError &&\n (e.status === 404 || e.code === \"SANDBOX_ALREADY_TERMINATED\"))\n\nconst mapCreateError = (e: unknown): SandboxError.SandboxError =>\n isAuthError(e)\n ? new SandboxError.SandboxAuthFailed({ provider: PROVIDER, subtype: \"auth\", raw: e })\n : isQuotaError(e)\n ? new SandboxError.SandboxQuotaExceeded({\n provider: PROVIDER,\n raw: e,\n ...reasonOf(e),\n })\n : e instanceof InvalidTimeoutError || e instanceof InvalidMemoryError\n ? new SandboxError.SandboxInvalidRequest({\n provider: PROVIDER,\n raw: e,\n ...reasonOf(e),\n })\n : new SandboxError.SandboxCreateFailed({\n provider: PROVIDER,\n raw: e,\n ...reasonOf(e),\n })\n\nconst mapLookupError =\n (id: string) =>\n (e: unknown): SandboxError.SandboxError =>\n isNotFoundError(e)\n ? new SandboxError.SandboxNotFound({ provider: PROVIDER, id })\n : mapCreateError(e)\n\nconst mapExecError = (e: unknown): SandboxError.SandboxError =>\n e instanceof SandboxCommandError\n ? new SandboxError.SandboxExecFailed({\n provider: PROVIDER,\n raw: e,\n reason: e.message,\n })\n : e instanceof ConnectionClosedError\n ? new SandboxError.SandboxExecFailed({\n provider: PROVIDER,\n raw: e,\n reason: e.reason ?? \"connection closed\",\n })\n : new SandboxError.SandboxExecFailed({\n provider: PROVIDER,\n raw: e,\n ...reasonOf(e),\n })\n\nconst mapFsError = (e: unknown): SandboxError.SandboxError =>\n new SandboxError.SandboxExecFailed({\n provider: PROVIDER,\n raw: e,\n ...reasonOf(e),\n })\n\n// ---------------------------------------------------------------------------\n// Transient WebSocket handshake failures.\n//\n// Empirically (SDK 0.13.2): `sdk.spawn` opens a per-process WebSocket\n// to the sandbox-side relay. The Deploy edge intermittently returns\n// `500 SANDBOX_WEBSOCKET_HANDSHAKE_ERROR` on this handshake; the next\n// attempt against the same sandbox typically succeeds. Retry narrowly\n// on that specific code rather than blanketing all 5xx — most are not\n// transient.\n//\n// Budget: 3 attempts ≈ 500+1000+2000 = 3.5s on top of the original\n// call, well under the 120s test timeout.\n// ---------------------------------------------------------------------------\n\nconst isTransientWsHandshake = (e: SandboxError.SandboxError) =>\n (e._tag === \"SandboxExecFailed\" || e._tag === \"SandboxCreateFailed\") &&\n e.raw instanceof ConnectionEstablishmentError &&\n e.raw.code === \"SANDBOX_WEBSOCKET_HANDSHAKE_ERROR\"\n\nconst wsHandshakeSchedule = Schedule.exponential(\"500 millis\")\nconst WS_HANDSHAKE_RETRIES = 3\n\nconst retryOnTransientWs = <A, R>(\n effect: Effect.Effect<A, SandboxError.SandboxError, R>,\n): Effect.Effect<A, SandboxError.SandboxError, R> =>\n Effect.retry(effect, {\n schedule: wsHandshakeSchedule,\n times: WS_HANDSHAKE_RETRIES,\n while: isTransientWsHandshake,\n })\n\n// ---------------------------------------------------------------------------\n// Builder pipeline — a `Step` mutates `SandboxOptions`. Deno's create\n// API is a plain options object (no builder fluent API), so each Step\n// returns a partial overlay; the create flow folds them into the base.\n// Keeping the same shape as microsandbox's `Step` pattern means optional\n// fields read uniformly.\n// ---------------------------------------------------------------------------\n\ntype OptionsPatch = Partial<SandboxOptions>\ntype Step = (patch: OptionsPatch) => OptionsPatch\n\nconst noop: Step = (p) => p\n\nconst when = <A>(value: A | undefined, step: (a: A) => Step): Step =>\n value === undefined ? noop : step(value)\n\nconst set =\n <K extends keyof SandboxOptions>(key: K, value: SandboxOptions[K]): Step =>\n (p) => ({ ...p, [key]: value })\n\n// ---------------------------------------------------------------------------\n// Duration → Deno timeout format (\"30s\"). Deno's `create.timeout`\n// accepts `\"session\" | \"${number}s\" | \"${number}m\"`; we always emit\n// `<seconds>s` since seconds is the most precise unit honored.\n// ---------------------------------------------------------------------------\n\nconst toDenoSeconds = (d: Duration.Input): `${number}s` => {\n const secs = Math.max(1, Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1000))\n return `${secs}s`\n}\n\n// ---------------------------------------------------------------------------\n// ImageRef → Step. Default omits `root`; Snapshot sets it; Registry /\n// Dockerfile are unsupported and fail at decode.\n// ---------------------------------------------------------------------------\n\nconst imageStep = (image: ImageRef | undefined): Effect.Effect<Step, SandboxError.SandboxError> => {\n if (image === undefined) return Effect.succeed(noop)\n return Match.value(image).pipe(\n Match.tag(\n \"Default\",\n (): Effect.Effect<Step, SandboxError.SandboxError> => Effect.succeed(noop),\n ),\n Match.tag(\n \"Snapshot\",\n ({ id }): Effect.Effect<Step, SandboxError.SandboxError> => Effect.succeed(set(\"root\", id)),\n ),\n Match.tag(\n \"Registry\",\n (): Effect.Effect<Step, SandboxError.SandboxError> =>\n Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"image.registry\",\n reason: \"deno sandboxes do not accept OCI registry refs — boot from a snapshot instead\",\n }),\n ),\n ),\n Match.tag(\n \"Dockerfile\",\n (): Effect.Effect<Step, SandboxError.SandboxError> =>\n Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"image.dockerfile\",\n reason:\n \"deno sandboxes do not accept Dockerfiles — install software into a bootable volume then snapshot it\",\n }),\n ),\n ),\n Match.exhaustive,\n )\n}\n\n// ---------------------------------------------------------------------------\n// NetworkPolicy → Step. Deno accepts hostnames + literal IPs but no\n// CIDR ranges; reject `cidrs` rather than silently dropping. `Blocked`\n// encodes as empty allowlist; `Open` omits the field.\n// ---------------------------------------------------------------------------\n\nconst networkStep = (policy: NetworkPolicy): Effect.Effect<Step, SandboxError.SandboxError> =>\n Match.value(policy).pipe(\n Match.tag(\"Open\", (): Effect.Effect<Step, SandboxError.SandboxError> => Effect.succeed(noop)),\n Match.tag(\n \"Blocked\",\n (): Effect.Effect<Step, SandboxError.SandboxError> => Effect.succeed(set(\"allowNet\", [])),\n ),\n Match.tag(\n \"Allowlist\",\n ({ hosts, cidrs }): Effect.Effect<Step, SandboxError.SandboxError> =>\n cidrs !== undefined && cidrs.length > 0\n ? Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"network.cidrs\",\n reason:\n \"deno's allowNet accepts hostnames or literal IPs, not CIDR ranges — use hosts instead\",\n }),\n )\n : Effect.succeed(set(\"allowNet\", [...(hosts ?? [])])),\n ),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// Volumes → Step. Deno mounts read-write only; reject `readonly: true`.\n// ---------------------------------------------------------------------------\n\nconst volumesStep = (\n volumes: CommonCreateRequest[\"volumes\"],\n): Effect.Effect<Step, SandboxError.SandboxError> => {\n if (volumes === undefined || volumes.length === 0) return Effect.succeed(noop)\n if (volumes.some((v) => v.readonly === true)) {\n return Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"volumes.readonly\",\n reason: \"deno mounts volumes read-write — read-only mounts are not supported\",\n }),\n )\n }\n const map = Arr.reduce(volumes, {} as Record<string, string>, (acc, v) => {\n acc[v.mountPath] = v.id\n return acc\n })\n return Effect.succeed(set(\"volumes\", map))\n}\n\n// ---------------------------------------------------------------------------\n// Secrets → Step. Stays sync — no decode-time failure modes (custom\n// header is rejected upstream in the upcast).\n// ---------------------------------------------------------------------------\n\nconst secretsStep = (secrets: ReadonlyArray<DenoSandboxBoundSecret>): Step => {\n const record = Arr.reduce(secrets, {} as Record<string, SecretConfig>, (acc, s) => {\n acc[s.name] = { hosts: [...s.hosts], value: Redacted.value(s.value) }\n return acc\n })\n return set(\"secrets\", record)\n}\n\n// ---------------------------------------------------------------------------\n// Base client options used for both client construction and per-call\n// overrides. `token` is a Redacted — we unwrap at the SDK boundary.\n// ---------------------------------------------------------------------------\n\nconst baseClientOptions = (config: DenoSandboxConfig): SandboxOptions => ({\n ...(config.token === undefined ? {} : { token: Redacted.value(config.token) }),\n ...(config.org === undefined ? {} : { org: config.org }),\n ...(config.apiEndpoint === undefined ? {} : { apiEndpoint: config.apiEndpoint }),\n})\n\n// ---------------------------------------------------------------------------\n// argv splitter. Deno's `spawn(command, { args })` is direct argv only —\n// for `cmd: string` (shell semantics) we route through `bash -c`.\n// ---------------------------------------------------------------------------\n\nconst argv = (\n cmd: CommonExecRequest[\"cmd\"],\n): Effect.Effect<\n { readonly command: string; readonly args: ReadonlyArray<string> },\n SandboxError.SandboxError\n> =>\n typeof cmd === \"string\"\n ? Effect.succeed({ command: \"bash\", args: [\"-c\", cmd] })\n : Option.match(Arr.head(cmd), {\n onNone: () =>\n Effect.fail(\n new SandboxError.SandboxInvalidRequest({\n provider: PROVIDER,\n param: \"cmd\",\n reason: \"argv array must be non-empty\",\n }),\n ),\n onSome: (head) => Effect.succeed({ command: head, args: Arr.drop(cmd, 1) }),\n })\n\n// ---------------------------------------------------------------------------\n// AbortController scoped to the caller. On scope close we abort, which\n// signals the SDK to terminate the in-flight call. This is the kill-\n// switch the SDK responds to.\n// ---------------------------------------------------------------------------\n\nconst acquireAbortController: Effect.Effect<AbortController, never, Scope.Scope> =\n Effect.acquireRelease(\n Effect.sync(() => new AbortController()),\n (ac) => Effect.sync(() => ac.abort()),\n )\n\n// ---------------------------------------------------------------------------\n// stdin helpers\n// ---------------------------------------------------------------------------\n\nconst stdinIsStream = (\n stdin: CommonSpawnRequest[\"stdin\"],\n): stdin is Stream.Stream<Uint8Array, never, never> =>\n stdin !== undefined && typeof stdin !== \"string\" && !(stdin instanceof Uint8Array)\n\n/**\n * Copy into a fresh `Uint8Array<ArrayBuffer>`. The Deno SDK strictly\n * types its bytes argument as `Uint8Array<ArrayBuffer>` (not the\n * `ArrayBufferLike` upper bound), so `TextEncoder`-produced bytes and\n * caller-supplied Uint8Arrays need to be copied to a guaranteed-\n * `ArrayBuffer`-backed buffer to satisfy the type.\n */\nconst toBytes = (input: Uint8Array | string): Uint8Array<ArrayBuffer> => {\n const src = typeof input === \"string\" ? new TextEncoder().encode(input) : input\n const out = new Uint8Array(new ArrayBuffer(src.byteLength))\n out.set(src)\n return out\n}\n\n/**\n * Acquire the child's stdin writer, run `use(writer)`, then close the\n * writer on release (regardless of success / failure / interrupt).\n * Returns `Effect.void` if the child wasn't spawned with `stdin: \"piped\"`.\n */\nconst withStdinWriter = <A, E, R>(\n child: DenoChildProcess,\n use: (writer: WritableStreamDefaultWriter<Uint8Array>) => Effect.Effect<A, E, R>,\n): Effect.Effect<void | A, E | SandboxError.SandboxError, R> => {\n const stdin = child.stdin\n if (stdin === null) return Effect.void\n return Effect.acquireUseRelease(\n Effect.sync(() => stdin.getWriter()),\n use,\n (writer) => Effect.promise(() => writer.close().catch(() => undefined)).pipe(Effect.ignore),\n )\n}\n\nconst writeStdinBytes = (\n child: DenoChildProcess,\n bytes: Uint8Array,\n): Effect.Effect<void, SandboxError.SandboxError> =>\n withStdinWriter(child, (writer) =>\n Effect.tryPromise({ try: () => writer.write(bytes), catch: mapExecError }),\n ).pipe(Effect.asVoid)\n\nconst feedStreamStdin = (\n child: DenoChildProcess,\n source: Stream.Stream<Uint8Array, never, never>,\n): Effect.Effect<void, SandboxError.SandboxError> =>\n withStdinWriter(child, (writer) =>\n Stream.runForEach(source, (chunk) =>\n Effect.tryPromise({ try: () => writer.write(chunk), catch: mapExecError }),\n ),\n ).pipe(Effect.asVoid)\n\n// ---------------------------------------------------------------------------\n// ReadableStream<Uint8Array> → Stream<Uint8Array>.\n// ---------------------------------------------------------------------------\n\nconst streamFromReadable = (\n readable: ReadableStream<Uint8Array>,\n): Stream.Stream<Uint8Array, SandboxError.SandboxError> =>\n Stream.fromReadableStream({\n evaluate: () => readable,\n onError: mapExecError,\n })\n\n// ---------------------------------------------------------------------------\n// Merge stdout + stderr into a tagged ExecEvent stream terminated by a\n// single Complete event derived from `child.status`.\n// ---------------------------------------------------------------------------\n\nconst eventStream = (\n child: DenoChildProcess,\n startedAt: number,\n): Stream.Stream<CoreExecEvent, SandboxError.SandboxError> => {\n const stdout: Stream.Stream<CoreExecEvent, SandboxError.SandboxError> =\n child.stdout === null\n ? Stream.empty\n : Stream.map(streamFromReadable(child.stdout), (chunk) => CoreExecEvent.Stdout({ chunk }))\n const stderr: Stream.Stream<CoreExecEvent, SandboxError.SandboxError> =\n child.stderr === null\n ? Stream.empty\n : Stream.map(streamFromReadable(child.stderr), (chunk) => CoreExecEvent.Stderr({ chunk }))\n const complete: Stream.Stream<CoreExecEvent, SandboxError.SandboxError> = Stream.unwrap(\n Effect.gen(function* () {\n const status = yield* Effect.tryPromise({ try: () => child.status, catch: mapExecError })\n const now = yield* Clock.currentTimeMillis\n return Stream.succeed(\n CoreExecEvent.Complete({ exitCode: status.code, durationMs: now - startedAt }),\n )\n }),\n )\n return Stream.concat(Stream.merge(stdout, stderr), complete)\n}\n\n// ---------------------------------------------------------------------------\n// Per-exec timeout. Races against `Effect.sleep` so the parent scope\n// finalizers (which kill the child via `acquireRelease`) fire on breach.\n// We don't lean on `Effect.timeout` because it returns `Option`-shaped\n// fallbacks in v4; the race-based shape lets us keep the SandboxError\n// channel directly.\n// ---------------------------------------------------------------------------\n\nconst withExecTimeout = <A, R>(\n timeout: Duration.Input | undefined,\n effect: Effect.Effect<A, SandboxError.SandboxError, R>,\n): Effect.Effect<A, SandboxError.SandboxError, R> =>\n timeout === undefined\n ? effect\n : Effect.race(\n effect,\n Effect.sleep(timeout).pipe(\n Effect.andThen(\n Effect.fail(\n new SandboxError.SandboxTimeout({\n provider: PROVIDER,\n operation: \"exec\",\n budget: Duration.fromInputUnsafe(timeout),\n }),\n ),\n ),\n ),\n )\n\n// ---------------------------------------------------------------------------\n// Live SandboxInstance adapter. The `WeakMap<SandboxInstance, sdk>`\n// passed in is the back-reference used by the port-exposure sub-API\n// to recover the SDK handle from an opaque `SandboxInstance`.\n// ---------------------------------------------------------------------------\n\nconst adaptInstance = (\n sdk: DenoSdkSandbox,\n portRegistry: WeakMap<SandboxInstance, DenoSdkSandbox>,\n): SandboxInstance => {\n const id = SandboxId(sdk.id)\n\n const openProcess = (request: CommonSpawnRequest) =>\n Effect.gen(function* () {\n const { command, args } = yield* argv(request.cmd)\n const ac = yield* acquireAbortController\n const stdinMode: \"piped\" | \"null\" = request.stdin === undefined ? \"null\" : \"piped\"\n const startedAt = yield* Clock.currentTimeMillis\n const child = yield* Effect.acquireRelease(\n retryOnTransientWs(\n Effect.tryPromise({\n try: () =>\n sdk.spawn(command, {\n args: [...args],\n ...(request.cwd === undefined ? {} : { cwd: request.cwd }),\n ...(request.env === undefined ? {} : { env: { ...request.env } }),\n stdin: stdinMode,\n stdout: \"piped\",\n stderr: \"piped\",\n signal: ac.signal,\n }),\n catch: mapExecError,\n }),\n ),\n (c) => Effect.promise(() => c.kill(\"SIGKILL\").catch(() => undefined)).pipe(Effect.ignore),\n )\n if (request.stdin !== undefined) {\n if (stdinIsStream(request.stdin)) {\n yield* Effect.forkScoped(feedStreamStdin(child, request.stdin))\n } else {\n yield* writeStdinBytes(child, toBytes(request.stdin))\n }\n }\n return { child, startedAt }\n })\n\n const instance: SandboxInstance = {\n id,\n\n exec: (request) =>\n withExecTimeout(\n request.timeout,\n Effect.scoped(\n Effect.gen(function* () {\n const { child, startedAt } = yield* openProcess(request)\n const output = yield* Effect.tryPromise({\n try: () => child.output(),\n catch: mapExecError,\n })\n const completedAt = yield* Clock.currentTimeMillis\n return {\n exitCode: output.status.code,\n stdout: output.stdoutText ?? \"\",\n stderr: output.stderrText ?? \"\",\n durationMs: completedAt - startedAt,\n } satisfies ExecResult\n }),\n ),\n ),\n\n execStream: (request) =>\n Stream.unwrap(\n Effect.map(openProcess(request), ({ child, startedAt }) => eventStream(child, startedAt)),\n ),\n\n spawn: (request) =>\n Effect.map(\n openProcess(request),\n ({ child, startedAt }): ProcessHandle => ({\n pid: child.pid,\n events: eventStream(child, startedAt),\n kill: Effect.tryPromise({\n try: () => child.kill(\"SIGTERM\"),\n catch: mapExecError,\n }),\n exit: Effect.tryPromise({\n try: () => child.status,\n catch: mapExecError,\n }).pipe(Effect.map((status) => ({ exitCode: status.code }))),\n }),\n ),\n\n files: {\n read: (path) => Effect.tryPromise({ try: () => sdk.fs.readFile(path), catch: mapFsError }),\n write: (path, contents) =>\n Effect.tryPromise({\n try: () => sdk.fs.writeFile(path, toBytes(contents)),\n catch: mapFsError,\n }),\n remove: (path) => Effect.tryPromise({ try: () => sdk.fs.remove(path), catch: mapFsError }),\n mkdir: (path) =>\n Effect.tryPromise({\n try: () => sdk.fs.mkdir(path, { recursive: true }),\n catch: mapFsError,\n }),\n list: (path) =>\n Stream.fromAsyncIterable(sdk.fs.readDir(path), mapFsError).pipe(\n Stream.map((e) => ({\n path: e.name,\n kind: e.isFile\n ? (\"file\" as const)\n : e.isDirectory\n ? (\"directory\" as const)\n : e.isSymlink\n ? (\"symlink\" as const)\n : (\"other\" as const),\n })),\n Stream.runCollect,\n ),\n exists: (path) =>\n Effect.tryPromise({\n try: () => sdk.fs.stat(path),\n catch: mapFsError,\n }).pipe(\n Effect.as(true),\n Effect.catchCause(() => Effect.succeed(false)),\n ),\n },\n }\n\n portRegistry.set(instance, sdk)\n return instance\n}\n\n// ---------------------------------------------------------------------------\n// Service factory\n// ---------------------------------------------------------------------------\n\nconst buildService = (config: DenoSandboxConfig): DenoSandboxService => {\n const clientOpts = baseClientOptions(config)\n const client = new Client(clientOpts)\n const portRegistry = new WeakMap<SandboxInstance, DenoSdkSandbox>()\n\n const buildOptions = (\n request: DenoSandboxCreateRequest,\n ): Effect.Effect<SandboxOptions, SandboxError.SandboxError> =>\n Effect.gen(function* () {\n const img = yield* imageStep(request.image)\n const net = yield* request.network === undefined\n ? Effect.succeed(noop)\n : networkStep(request.network)\n const vols = yield* volumesStep(request.volumes)\n\n const steps: ReadonlyArray<Step> = [\n img,\n when(request.region ?? config.defaultRegion, (r) => set(\"region\", r)),\n when(request.env, (env) => set(\"env\", { ...env })),\n when(request.timeout, (t) => set(\"timeout\", toDenoSeconds(t))),\n when(request.memory, (m) => set(\"memory\", Memory.toBytes(m))),\n when(request.labels, (l) => set(\"labels\", { ...l })),\n net,\n vols,\n when(request.secrets, (s) => (s.length === 0 ? noop : secretsStep(s))),\n when(request.port, (p) => set(\"port\", p)),\n ]\n const overlay = Arr.reduce(steps, {} as OptionsPatch, (acc, step) => step(acc))\n return { ...clientOpts, ...overlay }\n })\n\n const acquireSdk = (request: DenoSandboxCreateRequest) =>\n Effect.flatMap(buildOptions(request), (opts) =>\n Effect.tryPromise({ try: () => DenoSdkSandbox.create(opts), catch: mapCreateError }),\n )\n\n const releaseSdk = (sdk: DenoSdkSandbox) =>\n Effect.promise(() => sdk.kill().catch(() => undefined)).pipe(Effect.ignore)\n\n const releaseAttached = (sdk: DenoSdkSandbox) =>\n Effect.promise(() => sdk.close().catch(() => undefined)).pipe(Effect.ignore)\n\n const destroyById = (id: SandboxId) =>\n Effect.tryPromise({\n try: () => DenoSdkSandbox.connect(id, clientOpts),\n catch: mapLookupError(id),\n }).pipe(\n Effect.flatMap((sdk) =>\n Effect.tryPromise({ try: () => sdk.kill(), catch: mapLookupError(id) }),\n ),\n Effect.catchTag(\"SandboxNotFound\", () => Effect.void),\n )\n\n return {\n create: (request) =>\n Effect.acquireRelease(acquireSdk(request), releaseSdk).pipe(\n Effect.map((sdk) => adaptInstance(sdk, portRegistry)),\n ),\n\n attach: (id) =>\n Effect.acquireRelease(\n Effect.tryPromise({\n try: () => DenoSdkSandbox.connect(id, clientOpts),\n catch: mapLookupError(id),\n }),\n releaseAttached,\n ).pipe(Effect.map((sdk) => adaptInstance(sdk, portRegistry))),\n\n list: Effect.tryPromise({\n try: () => client.sandboxes.list(),\n catch: mapCreateError,\n }).pipe(Effect.map(Arr.map((m): SandboxRef => ({ id: SandboxId(m.id) })))),\n\n destroy: destroyById,\n\n snapshotVolume: (volumeId, slug) =>\n Effect.gen(function* () {\n const snapSlug = slug ?? `eff-uai-snap-${yield* randomToken}`\n const snap = yield* Effect.tryPromise({\n try: () => client.volumes.snapshot(volumeId, { slug: snapSlug }),\n catch: mapCreateError,\n })\n return SnapshotId(snap.slug)\n }),\n\n snapshots: {\n create: () =>\n Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"snapshots.create\",\n reason:\n \"deno snapshots are derived from volumes, not from running sandboxes — use DenoSandbox.snapshotVolume(volumeId, slug?) instead\",\n }),\n ),\n destroy: (sid) =>\n Effect.tryPromise({\n try: () => client.snapshots.delete(sid),\n catch: mapLookupError(sid),\n }),\n list: Stream.unwrap(\n Effect.tryPromise({\n try: () => client.snapshots.list(),\n catch: mapCreateError,\n }).pipe(Effect.map((page) => Stream.fromAsyncIterable(page, mapCreateError))),\n ).pipe(\n Stream.map((snap) => ({ id: SnapshotId(snap.id), name: snap.slug })),\n Stream.runCollect,\n ),\n },\n\n volumes: {\n create: (name, options) =>\n Effect.tryPromise({\n try: () =>\n client.volumes.create({\n slug: name,\n // Volumes are only available in `ord` at the moment.\n // Callers needing a different region can pre-create via\n // the SDK directly.\n region: config.defaultRegion ?? \"ord\",\n capacity: options?.quotaBytes ?? \"1GB\",\n }),\n catch: mapCreateError,\n }).pipe(Effect.map((vol) => VolumeId(vol.slug))),\n destroy: (vid) =>\n Effect.tryPromise({\n try: () => client.volumes.delete(vid),\n catch: mapLookupError(vid),\n }),\n list: Stream.unwrap(\n Effect.tryPromise({\n try: () => client.volumes.list(),\n catch: mapCreateError,\n }).pipe(Effect.map((page) => Stream.fromAsyncIterable(page, mapCreateError))),\n ).pipe(\n Stream.map((vol) => ({ id: VolumeId(vol.slug), name: vol.slug })),\n Stream.runCollect,\n ),\n },\n\n ports: {\n expose: (instance, port) =>\n Effect.gen(function* () {\n const sdk = portRegistry.get(instance)\n if (sdk === undefined) {\n return yield* Effect.fail(\n new SandboxError.SandboxNotFound({ provider: PROVIDER, id: instance.id }),\n )\n }\n const url = yield* Effect.tryPromise({\n try: () => sdk.exposeHttp({ port }),\n catch: mapExecError,\n })\n return { url }\n }),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cross-provider upcast — reject custom header on BoundSecret since\n// Deno doesn't support per-secret headers.\n// ---------------------------------------------------------------------------\n\nconst rejectCustomHeader = (\n secrets: CommonCreateRequest[\"secrets\"],\n): Effect.Effect<void, SandboxError.SandboxError> =>\n secrets === undefined || !secrets.some((s) => s.header !== undefined)\n ? Effect.void\n : Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"BoundSecret.header\",\n reason:\n \"deno always injects secrets as `Authorization: Bearer <value>` — custom headers aren't supported. Use the narrowed `DenoSandbox.create` surface, which omits this field.\",\n }),\n )\n\nconst upcastService = (s: DenoSandboxService): SandboxService => ({\n ...s,\n create: (request) =>\n rejectCustomHeader(request.secrets).pipe(\n Effect.andThen(s.create(request as DenoSandboxCreateRequest)),\n ),\n})\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * Layer registering the Deno Sandbox adapter against `Sandbox`, with\n * the capability markers Deno actually supports.\n *\n * Shipped:\n * - {@link SandboxHostnameAllowlist} — `allowNet` accepts hostnames + wildcards.\n * - {@link SandboxSecretInjection} — `secrets.<name>.value` is injected\n * only on outbound HTTPS to bound hosts; in-VM env shows a placeholder.\n * - {@link SandboxSnapshots} — read-side helpers (`list`, `destroy`)\n * work. `snapshots.create(fromInstance)` fails with\n * `SandboxUnsupported`; use the per-provider\n * `DenoSandbox.snapshotVolume` escape hatch for the volume-derived\n * snapshot workflow.\n * - {@link SandboxVolumes} — `client.volumes.*` directly.\n * - {@link SandboxPortExposure} — `sandbox.exposeHttp({ port })`.\n *\n * NOT shipped (calling those helpers fails at compile time):\n * - `SandboxCustomImage` — no Dockerfile / no OCI registry refs.\n * - `SandboxPauseResume` — Deno doesn't offer in-place memory-preserving pause.\n * - `SandboxKernelSession`, `SandboxPty` — no Jupyter / no PTY.\n */\nexport const layer = (config: DenoSandboxConfig = {}) => {\n const service = buildService(config)\n return Layer.mergeAll(\n Layer.succeed(DenoSandbox, service),\n Layer.succeed(CoreSandbox, upcastService(service)),\n Layer.succeed(SandboxHostnameAllowlist, undefined),\n Layer.succeed(SandboxSecretInjection, undefined),\n Layer.succeed(SandboxSnapshots, undefined),\n Layer.succeed(SandboxVolumes, undefined),\n Layer.succeed(SandboxPortExposure, undefined),\n )\n}\n\n// Re-export SDK error classes so consumers don't need a direct\n// dependency on `@deno/sandbox` to do error-shape-narrowed handling.\nexport {\n ApiError,\n ConnectionClosedError,\n ConnectionEstablishmentError,\n InvalidMemoryError,\n InvalidTimeoutError,\n InvalidTokenError,\n MissingTokenError,\n SandboxCommandError,\n SandboxKillError,\n}\n"],"mappings":"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"}
|