@effect-uai/microsandbox 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/MicrosandboxSandbox.d.mts +55 -0
- package/dist/MicrosandboxSandbox.d.mts.map +1 -0
- package/dist/MicrosandboxSandbox.mjs +374 -0
- package/dist/MicrosandboxSandbox.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/MicrosandboxSandbox.ts +871 -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,55 @@
|
|
|
1
|
+
import { BoundSecret, CommonCreateRequest, Sandbox, SandboxHostnameAllowlist, SandboxInstance, SandboxSecretInjection, SandboxService, SandboxSnapshots, SandboxVolumes } from "@effect-uai/core/Sandbox";
|
|
2
|
+
import * as SandboxError from "@effect-uai/core/SandboxError";
|
|
3
|
+
import * as Memory from "@effect-uai/core/Memory";
|
|
4
|
+
import { Context, Duration, Effect, Layer, Scope } from "effect";
|
|
5
|
+
|
|
6
|
+
//#region src/MicrosandboxSandbox.d.ts
|
|
7
|
+
declare namespace MicrosandboxSandbox_d_exports {
|
|
8
|
+
export { MicrosandboxBoundSecret, MicrosandboxConfig, MicrosandboxCreateRequest, MicrosandboxSandbox, MicrosandboxSandboxService, layer };
|
|
9
|
+
}
|
|
10
|
+
type MicrosandboxBoundSecret = Omit<BoundSecret, "header">;
|
|
11
|
+
type MicrosandboxCreateRequest = Omit<CommonCreateRequest, "secrets"> & {
|
|
12
|
+
readonly secrets?: ReadonlyArray<MicrosandboxBoundSecret>;
|
|
13
|
+
readonly name?: string;
|
|
14
|
+
readonly cpus?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Memory size. Accepts a byte count, a {@link Memory.Memory}, or a
|
|
17
|
+
* human string like `"1 GiB"` / `"512 MiB"`. Rounded up to whole
|
|
18
|
+
* MiB at the SDK boundary (microsandbox's builder takes MiB).
|
|
19
|
+
*/
|
|
20
|
+
readonly memory?: Memory.Input;
|
|
21
|
+
readonly workdir?: string;
|
|
22
|
+
readonly user?: string;
|
|
23
|
+
readonly maxDuration?: Duration.Input;
|
|
24
|
+
readonly idleTimeout?: Duration.Input;
|
|
25
|
+
/**
|
|
26
|
+
* Stop any existing sandbox with the same name before creating.
|
|
27
|
+
* `true` = `SIGTERM` with the SDK default grace; `{ graceMs }` =
|
|
28
|
+
* explicit grace before `SIGKILL`.
|
|
29
|
+
*/
|
|
30
|
+
readonly replace?: boolean | {
|
|
31
|
+
readonly graceMs: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Detach so the sandbox outlives the calling scope. The scope
|
|
35
|
+
* finalizer skips destroy; clean up via {@link Sandbox.destroy}.
|
|
36
|
+
*/
|
|
37
|
+
readonly detached?: boolean;
|
|
38
|
+
};
|
|
39
|
+
type MicrosandboxConfig = {
|
|
40
|
+
/** Default OCI image to use when no `image` is supplied on create. */readonly defaultImage?: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Microsandbox-narrowed service. `create` accepts the narrowed
|
|
44
|
+
* {@link MicrosandboxCreateRequest}; every other method is identical
|
|
45
|
+
* to the generic {@link SandboxService}.
|
|
46
|
+
*/
|
|
47
|
+
type MicrosandboxSandboxService = Omit<SandboxService, "create"> & {
|
|
48
|
+
readonly create: (request: MicrosandboxCreateRequest) => Effect.Effect<SandboxInstance, SandboxError.SandboxError, Scope.Scope>;
|
|
49
|
+
};
|
|
50
|
+
declare const MicrosandboxSandbox_base: Context.ServiceClass<MicrosandboxSandbox, "@betalyra/effect-uai/providers/microsandbox/MicrosandboxSandbox", MicrosandboxSandboxService>;
|
|
51
|
+
declare class MicrosandboxSandbox extends MicrosandboxSandbox_base {}
|
|
52
|
+
declare const layer: (config?: MicrosandboxConfig) => Layer.Layer<MicrosandboxSandbox | Sandbox | SandboxHostnameAllowlist | SandboxSecretInjection | SandboxSnapshots | SandboxVolumes, never, never>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { MicrosandboxBoundSecret, MicrosandboxConfig, MicrosandboxCreateRequest, MicrosandboxSandbox, MicrosandboxSandboxService, layer, MicrosandboxSandbox_d_exports as t };
|
|
55
|
+
//# sourceMappingURL=MicrosandboxSandbox.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MicrosandboxSandbox.d.mts","names":[],"sources":["../src/MicrosandboxSandbox.ts"],"mappings":";;;;;;;;;KAoEY,uBAAA,GAA0B,IAAA,CAAK,WAAA;AAAA,KAE/B,yBAAA,GAA4B,IAAA,CAAK,mBAAA;EAAA,SAClC,OAAA,GAAU,aAAA,CAAc,uBAAA;EAAA,SACxB,IAAA;EAAA,SACA,IAAA;;;;;;WAMA,MAAA,GAAS,MAAA,CAAO,KAAA;EAAA,SAChB,OAAA;EAAA,SACA,IAAA;EAAA,SACA,WAAA,GAAc,QAAA,CAAS,KAAA;EAAA,SACvB,WAAA,GAAc,QAAA,CAAS,KAAA;EAfoB;AAEtD;;;;EAFsD,SAqB3C,OAAA;IAAA,SAA+B,OAAA;EAAA;EAVtB;;;;EAAA,SAeT,QAAA;AAAA;AAAA,KAGC,kBAAA;EA1BD,+EA4BA,YAAA;AAAA;;;;;;KAQC,0BAAA,GAA6B,IAAA,CAAK,cAAA;EAAA,SACnC,MAAA,GACP,OAAA,EAAS,yBAAA,KACN,MAAA,CAAO,MAAA,CAAO,eAAA,EAAiB,YAAA,CAAa,YAAA,EAAc,KAAA,CAAM,KAAA;AAAA;AAAA,cACtE,wBAAA;cAEY,mBAAA,SAA4B,wBAAA;AAAA,cA2uB5B,KAAA,GAAS,MAAA,GAAQ,kBAAA,KAAuB,KAAA,CAAA,KAAA,CAAA,mBAAA,GAAA,OAAA,GAAA,wBAAA,GAAA,sBAAA,GAAA,gBAAA,GAAA,cAAA"}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { ExecEvent, Sandbox, SandboxHostnameAllowlist, SandboxId, SandboxSecretInjection, SandboxSnapshots, SandboxVolumes, SnapshotId, VolumeId } from "@effect-uai/core/Sandbox";
|
|
3
|
+
import * as SandboxError from "@effect-uai/core/SandboxError";
|
|
4
|
+
import * as Memory from "@effect-uai/core/Memory";
|
|
5
|
+
import { Array, Clock, Context, Duration, Effect, Filter, Layer, Match, Option, Random, Record, Redacted, Result, Schedule, Stream } from "effect";
|
|
6
|
+
import { ExecTimeoutError, NetworkPolicyBuilder, Sandbox as Sandbox$1, SandboxNotFoundError, SandboxStillRunningError, Snapshot, Volume } from "microsandbox";
|
|
7
|
+
//#region src/MicrosandboxSandbox.ts
|
|
8
|
+
var MicrosandboxSandbox_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
MicrosandboxSandbox: () => MicrosandboxSandbox,
|
|
10
|
+
layer: () => layer
|
|
11
|
+
});
|
|
12
|
+
const PROVIDER = "microsandbox";
|
|
13
|
+
var MicrosandboxSandbox = class extends Context.Service()("@betalyra/effect-uai/providers/microsandbox/MicrosandboxSandbox") {};
|
|
14
|
+
/** Random base-36 token, ~8 chars. Stand-in for `Math.random` IDs. */
|
|
15
|
+
const randomToken = Random.next.pipe(Effect.map((r) => r.toString(36).slice(2, 10)));
|
|
16
|
+
const reasonOf = (e) => e instanceof Error ? { reason: e.message } : {};
|
|
17
|
+
const mapCreateError = (e) => e instanceof SandboxStillRunningError ? new SandboxError.SandboxAlreadyExists({
|
|
18
|
+
provider: PROVIDER,
|
|
19
|
+
id: e.message
|
|
20
|
+
}) : e instanceof SandboxNotFoundError ? new SandboxError.SandboxNotFound({
|
|
21
|
+
provider: PROVIDER,
|
|
22
|
+
id: e.message
|
|
23
|
+
}) : new SandboxError.SandboxCreateFailed({
|
|
24
|
+
provider: PROVIDER,
|
|
25
|
+
raw: e,
|
|
26
|
+
...reasonOf(e)
|
|
27
|
+
});
|
|
28
|
+
const mapExecError = (e) => e instanceof ExecTimeoutError ? new SandboxError.SandboxTimeout({
|
|
29
|
+
provider: PROVIDER,
|
|
30
|
+
operation: "exec",
|
|
31
|
+
raw: e
|
|
32
|
+
}) : new SandboxError.SandboxExecFailed({
|
|
33
|
+
provider: PROVIDER,
|
|
34
|
+
raw: e,
|
|
35
|
+
...reasonOf(e)
|
|
36
|
+
});
|
|
37
|
+
const mapFsError = (e) => new SandboxError.SandboxExecFailed({
|
|
38
|
+
provider: PROVIDER,
|
|
39
|
+
raw: e,
|
|
40
|
+
...reasonOf(e)
|
|
41
|
+
});
|
|
42
|
+
const mapLookupError = (id) => (e) => e instanceof SandboxNotFoundError ? new SandboxError.SandboxNotFound({
|
|
43
|
+
provider: PROVIDER,
|
|
44
|
+
id
|
|
45
|
+
}) : mapCreateError(e);
|
|
46
|
+
const settleSchedule = Schedule.exponential("50 millis");
|
|
47
|
+
const SETTLE_RETRIES = 5;
|
|
48
|
+
/**
|
|
49
|
+
* Microsandbox's post-kill DB sync window surfaces as "still running"
|
|
50
|
+
* either via `SandboxStillRunningError` (mapped to `SandboxAlreadyExists`
|
|
51
|
+
* by {@link mapCreateError}) or, for `handle.snapshot`, a generic
|
|
52
|
+
* `MicrosandboxError` whose message contains "is not stopped" /
|
|
53
|
+
* "still running" (mapped to `SandboxCreateFailed`).
|
|
54
|
+
*/
|
|
55
|
+
const isPostKillRace = (e) => e._tag === "SandboxAlreadyExists" || e._tag === "SandboxCreateFailed" && typeof e.reason === "string" && /still running|is not stopped/i.test(e.reason);
|
|
56
|
+
const retryOnSettle = (effect) => Effect.retry(effect, {
|
|
57
|
+
schedule: settleSchedule,
|
|
58
|
+
times: SETTLE_RETRIES,
|
|
59
|
+
while: isPostKillRace
|
|
60
|
+
});
|
|
61
|
+
/** Force-kill a running sandbox. No-op if absent or already stopped. */
|
|
62
|
+
const killRunning = (id) => Effect.tryPromise({
|
|
63
|
+
try: () => Sandbox$1.get(id),
|
|
64
|
+
catch: mapLookupError(id)
|
|
65
|
+
}).pipe(Effect.flatMap((handle) => handle.status === "running" ? Effect.tryPromise({
|
|
66
|
+
try: () => handle.kill(),
|
|
67
|
+
catch: mapLookupError(id)
|
|
68
|
+
}) : Effect.void), Effect.catchTag("SandboxNotFound", () => Effect.void));
|
|
69
|
+
/** Stop + remove. Idempotent — `SandboxNotFound` is treated as success. */
|
|
70
|
+
const destroyById = (id) => killRunning(id).pipe(Effect.andThen(retryOnSettle(Effect.tryPromise({
|
|
71
|
+
try: () => Sandbox$1.remove(id),
|
|
72
|
+
catch: mapLookupError(id)
|
|
73
|
+
}))), Effect.catchTag("SandboxNotFound", () => Effect.void));
|
|
74
|
+
const noop = (b) => b;
|
|
75
|
+
const when = (value, step) => value === void 0 ? noop : step(value);
|
|
76
|
+
const imageStep = (defaultImage) => (image) => {
|
|
77
|
+
const fromDefault = defaultImage === void 0 ? Effect.fail(new SandboxError.SandboxUnsupported({
|
|
78
|
+
provider: PROVIDER,
|
|
79
|
+
capability: "image",
|
|
80
|
+
reason: "microsandbox requires an image — pass `image` on the request or set `defaultImage` on the layer config"
|
|
81
|
+
})) : Effect.succeed((b) => b.image(defaultImage));
|
|
82
|
+
if (image === void 0) return fromDefault;
|
|
83
|
+
return Match.value(image).pipe(Match.tag("Default", () => fromDefault), Match.tag("Registry", ({ ref }) => Effect.succeed((b) => b.image(ref))), Match.tag("Snapshot", ({ id }) => Effect.succeed((b) => b.fromSnapshot(id))), Match.tag("Dockerfile", () => Effect.fail(new SandboxError.SandboxUnsupported({
|
|
84
|
+
provider: PROVIDER,
|
|
85
|
+
capability: "image",
|
|
86
|
+
reason: "microsandbox accepts OCI registry refs or snapshots, not Dockerfiles"
|
|
87
|
+
}))), Match.exhaustive);
|
|
88
|
+
};
|
|
89
|
+
const buildPolicy = (hosts, cidrs) => {
|
|
90
|
+
const withHosts = Array.reduce(hosts, new NetworkPolicyBuilder().defaultDeny(), (pb, host) => pb.rule((rb) => rb.egress().allowDomain(host)));
|
|
91
|
+
return Array.reduce(cidrs, withHosts, (pb, cidr) => pb.rule((rb) => rb.egress().allow((d) => d.cidr(cidr))));
|
|
92
|
+
};
|
|
93
|
+
const networkStep = (policy) => Match.value(policy).pipe(Match.tag("Open", () => noop), Match.tag("Blocked", () => (b) => b.disableNetwork()), Match.tag("Allowlist", ({ hosts, cidrs }) => (b) => b.network((n) => n.policyFromBuilder(buildPolicy(hosts ?? [], cidrs ?? [])))), Match.exhaustive);
|
|
94
|
+
const secretStep = (secret) => (b) => b.secret((sb) => Array.reduce(secret.hosts, sb.env(secret.name).value(Redacted.value(secret.value)), (acc, host) => acc.allowHost(host)).injectHeaders(true));
|
|
95
|
+
const volumeStep = (vol) => (b) => b.volume(vol.mountPath, (m) => vol.readonly ? m.named(vol.id).readonly() : m.named(vol.id));
|
|
96
|
+
const replaceStep = (replace) => replace === true ? (b) => b.replace() : typeof replace === "object" ? (b) => b.replaceWithGrace(replace.graceMs) : noop;
|
|
97
|
+
const argv = (cmd) => typeof cmd === "string" ? Effect.succeed({
|
|
98
|
+
cmd,
|
|
99
|
+
args: []
|
|
100
|
+
}) : Option.match(Array.head(cmd), {
|
|
101
|
+
onNone: () => Effect.fail(new SandboxError.SandboxInvalidRequest({
|
|
102
|
+
provider: PROVIDER,
|
|
103
|
+
param: "cmd",
|
|
104
|
+
reason: "argv array must be non-empty"
|
|
105
|
+
})),
|
|
106
|
+
onSome: (head) => Effect.succeed({
|
|
107
|
+
cmd: head,
|
|
108
|
+
args: Array.drop(cmd, 1)
|
|
109
|
+
})
|
|
110
|
+
});
|
|
111
|
+
const toBuffer = (input) => typeof input === "string" ? Buffer.from(input, "utf-8") : Buffer.from(input);
|
|
112
|
+
/**
|
|
113
|
+
* `string` / `Uint8Array` stdin uses one-shot `stdinBytes`; a Stream
|
|
114
|
+
* uses `stdinPipe` and gets fed via `feedStdin` once the handle is open.
|
|
115
|
+
*/
|
|
116
|
+
const stdinIsStream = (stdin) => stdin !== void 0 && typeof stdin !== "string" && !(stdin instanceof Uint8Array);
|
|
117
|
+
const execConfigure = (args, request) => (b) => {
|
|
118
|
+
const withArgs = args.length === 0 ? b : b.args([...args]);
|
|
119
|
+
const withCwd = request.cwd === void 0 ? withArgs : withArgs.cwd(request.cwd);
|
|
120
|
+
const withEnv = request.env === void 0 ? withCwd : Record.reduce(request.env, withCwd, (acc, v, k) => acc.env(k, v));
|
|
121
|
+
const withTimeout = request.timeout === void 0 ? withEnv : withEnv.timeout(Duration.toMillis(Duration.fromInputUnsafe(request.timeout)));
|
|
122
|
+
if (request.stdin === void 0) return withTimeout;
|
|
123
|
+
if (stdinIsStream(request.stdin)) return withTimeout.stdinPipe();
|
|
124
|
+
return withTimeout.stdinBytes(toBuffer(request.stdin));
|
|
125
|
+
};
|
|
126
|
+
const feedStreamStdin = (handle, stream) => Effect.tryPromise({
|
|
127
|
+
try: () => handle.takeStdin(),
|
|
128
|
+
catch: mapExecError
|
|
129
|
+
}).pipe(Effect.flatMap((sink) => sink === null ? Effect.fail(new SandboxError.SandboxInvalidRequest({
|
|
130
|
+
provider: PROVIDER,
|
|
131
|
+
param: "stdin",
|
|
132
|
+
reason: "stdin sink already taken — the SDK exposes it once per spawn"
|
|
133
|
+
})) : Stream.runForEach(stream, (chunk) => Effect.tryPromise({
|
|
134
|
+
try: () => sink.write(Buffer.from(chunk)),
|
|
135
|
+
catch: mapExecError
|
|
136
|
+
})).pipe(Effect.andThen(Effect.tryPromise({
|
|
137
|
+
try: () => sink.close(),
|
|
138
|
+
catch: mapExecError
|
|
139
|
+
})))));
|
|
140
|
+
const execEventFromMsb = (event, durationMs) => Match.value(event).pipe(Match.discriminators("kind")({
|
|
141
|
+
started: () => Option.none(),
|
|
142
|
+
stdout: ({ data }) => Option.some(ExecEvent.Stdout({ chunk: data })),
|
|
143
|
+
stderr: ({ data }) => Option.some(ExecEvent.Stderr({ chunk: data })),
|
|
144
|
+
exited: ({ code }) => Option.some(ExecEvent.Complete({
|
|
145
|
+
exitCode: code,
|
|
146
|
+
durationMs
|
|
147
|
+
}))
|
|
148
|
+
}), Match.exhaustive);
|
|
149
|
+
const someEvent = Filter.fromPredicateOption((o) => o);
|
|
150
|
+
const eventStream = (handle, startedAt) => Stream.fromAsyncIterable(handle, mapExecError).pipe(Stream.mapEffect((event) => Effect.map(Clock.currentTimeMillis, (now) => execEventFromMsb(event, now - startedAt))), Stream.filterMap(someEvent));
|
|
151
|
+
const adaptInstance = (msb) => {
|
|
152
|
+
const id = SandboxId(msb.name);
|
|
153
|
+
const openExecStream = (request) => Effect.gen(function* () {
|
|
154
|
+
const { cmd, args } = yield* argv(request.cmd);
|
|
155
|
+
const startedAt = yield* Clock.currentTimeMillis;
|
|
156
|
+
const handle = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
157
|
+
try: () => msb.execStreamWith(cmd, execConfigure(args, request)),
|
|
158
|
+
catch: mapExecError
|
|
159
|
+
}), (h) => Effect.tryPromise({
|
|
160
|
+
try: () => h.kill(),
|
|
161
|
+
catch: () => void 0
|
|
162
|
+
}).pipe(Effect.ignore, Effect.andThen(Effect.promise(() => h[Symbol.asyncDispose]()))));
|
|
163
|
+
if (stdinIsStream(request.stdin)) yield* Effect.forkScoped(feedStreamStdin(handle, request.stdin));
|
|
164
|
+
return {
|
|
165
|
+
handle,
|
|
166
|
+
startedAt
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
id,
|
|
171
|
+
exec: (request) => Effect.gen(function* () {
|
|
172
|
+
const { cmd, args } = yield* argv(request.cmd);
|
|
173
|
+
const startedAt = yield* Clock.currentTimeMillis;
|
|
174
|
+
const output = yield* Effect.tryPromise({
|
|
175
|
+
try: () => msb.execWith(cmd, execConfigure(args, request)),
|
|
176
|
+
catch: mapExecError
|
|
177
|
+
});
|
|
178
|
+
const completedAt = yield* Clock.currentTimeMillis;
|
|
179
|
+
return {
|
|
180
|
+
exitCode: output.code,
|
|
181
|
+
stdout: output.stdout(),
|
|
182
|
+
stderr: output.stderr(),
|
|
183
|
+
durationMs: completedAt - startedAt
|
|
184
|
+
};
|
|
185
|
+
}),
|
|
186
|
+
execStream: (request) => Stream.unwrap(Effect.map(openExecStream(request), ({ handle, startedAt }) => eventStream(handle, startedAt))),
|
|
187
|
+
spawn: (request) => Effect.map(openExecStream(request), ({ handle, startedAt }) => ({
|
|
188
|
+
pid: 0,
|
|
189
|
+
events: eventStream(handle, startedAt),
|
|
190
|
+
kill: Effect.tryPromise({
|
|
191
|
+
try: () => handle.kill(),
|
|
192
|
+
catch: mapExecError
|
|
193
|
+
}),
|
|
194
|
+
exit: Effect.tryPromise({
|
|
195
|
+
try: () => handle.wait(),
|
|
196
|
+
catch: mapExecError
|
|
197
|
+
}).pipe(Effect.map((status) => ({ exitCode: status.code })))
|
|
198
|
+
})),
|
|
199
|
+
files: {
|
|
200
|
+
read: (path) => Effect.tryPromise({
|
|
201
|
+
try: () => msb.fs().read(path),
|
|
202
|
+
catch: mapFsError
|
|
203
|
+
}),
|
|
204
|
+
write: (path, contents) => Effect.tryPromise({
|
|
205
|
+
try: () => msb.fs().write(path, contents),
|
|
206
|
+
catch: mapFsError
|
|
207
|
+
}),
|
|
208
|
+
remove: (path) => Effect.tryPromise({
|
|
209
|
+
try: () => msb.fs().remove(path),
|
|
210
|
+
catch: mapFsError
|
|
211
|
+
}),
|
|
212
|
+
mkdir: (path) => Effect.tryPromise({
|
|
213
|
+
try: () => msb.fs().mkdir(path),
|
|
214
|
+
catch: mapFsError
|
|
215
|
+
}),
|
|
216
|
+
list: (path) => Effect.tryPromise({
|
|
217
|
+
try: () => msb.fs().list(path),
|
|
218
|
+
catch: mapFsError
|
|
219
|
+
}).pipe(Effect.map(Array.map((e) => ({
|
|
220
|
+
path: e.path,
|
|
221
|
+
kind: e.kind
|
|
222
|
+
})))),
|
|
223
|
+
exists: (path) => Effect.tryPromise({
|
|
224
|
+
try: () => msb.fs().exists(path),
|
|
225
|
+
catch: mapFsError
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
const buildService = (config) => {
|
|
231
|
+
const resolveImageStep = imageStep(config.defaultImage);
|
|
232
|
+
const acquireSandbox = (request, name) => Effect.gen(function* () {
|
|
233
|
+
const steps = [
|
|
234
|
+
yield* resolveImageStep(request.image),
|
|
235
|
+
when(request.cpus, (n) => (b) => b.cpus(n)),
|
|
236
|
+
when(request.memory, (m) => (b) => b.memory(Math.ceil(Memory.toBytes(m) / (1024 * 1024)))),
|
|
237
|
+
when(request.workdir, (p) => (b) => b.workdir(p)),
|
|
238
|
+
when(request.user, (u) => (b) => b.user(u)),
|
|
239
|
+
when(request.maxDuration, (d) => (b) => b.maxDuration(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1e3))),
|
|
240
|
+
when(request.idleTimeout, (d) => (b) => b.idleTimeout(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1e3))),
|
|
241
|
+
when(request.timeout, (t) => (b) => b.maxDuration(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(t)) / 1e3))),
|
|
242
|
+
when(request.env, (env) => (b) => b.envs(env)),
|
|
243
|
+
when(request.network, networkStep),
|
|
244
|
+
...Array.map(request.secrets ?? [], secretStep),
|
|
245
|
+
...Array.map(request.volumes ?? [], volumeStep),
|
|
246
|
+
replaceStep(request.replace)
|
|
247
|
+
];
|
|
248
|
+
const built = Array.reduce(steps, Sandbox$1.builder(name), (b, step) => step(b));
|
|
249
|
+
return yield* Effect.tryPromise({
|
|
250
|
+
try: () => request.detached ? built.createDetached() : built.create(),
|
|
251
|
+
catch: mapCreateError
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
const acquireAttached = (id) => Effect.tryPromise({
|
|
255
|
+
try: () => Sandbox$1.get(id),
|
|
256
|
+
catch: mapLookupError(id)
|
|
257
|
+
}).pipe(Effect.flatMap((handle) => Effect.tryPromise({
|
|
258
|
+
try: handle.status === "running" ? () => handle.connect() : () => handle.startDetached(),
|
|
259
|
+
catch: mapLookupError(id)
|
|
260
|
+
})));
|
|
261
|
+
return {
|
|
262
|
+
create: (request) => Effect.gen(function* () {
|
|
263
|
+
const token = yield* randomToken;
|
|
264
|
+
const name = SandboxId(request.name ?? `eff-uai-${token}`);
|
|
265
|
+
const detached = request.detached === true;
|
|
266
|
+
return adaptInstance(yield* Effect.acquireRelease(acquireSandbox(request, name), () => detached ? Effect.void : Effect.ignore(destroyById(name))));
|
|
267
|
+
}),
|
|
268
|
+
attach: (id) => Effect.acquireRelease(acquireAttached(id), () => Effect.void).pipe(Effect.map(adaptInstance)),
|
|
269
|
+
list: Effect.tryPromise({
|
|
270
|
+
try: () => Sandbox$1.list(),
|
|
271
|
+
catch: mapCreateError
|
|
272
|
+
}).pipe(Effect.map(Array.map((h) => ({
|
|
273
|
+
id: SandboxId(h.name),
|
|
274
|
+
name: h.name
|
|
275
|
+
})))),
|
|
276
|
+
destroy: destroyById,
|
|
277
|
+
snapshots: {
|
|
278
|
+
create: (from, name) => Effect.gen(function* () {
|
|
279
|
+
const token = yield* randomToken;
|
|
280
|
+
const snapName = name ?? `eff-uai-snap-${token}`;
|
|
281
|
+
yield* Effect.ignore(from.exec({ cmd: ["sync"] }));
|
|
282
|
+
yield* killRunning(from.id);
|
|
283
|
+
yield* retryOnSettle(Effect.tryPromise({
|
|
284
|
+
try: () => Sandbox$1.get(from.id),
|
|
285
|
+
catch: mapLookupError(from.id)
|
|
286
|
+
}).pipe(Effect.flatMap((handle) => Effect.tryPromise({
|
|
287
|
+
try: () => handle.snapshot(snapName),
|
|
288
|
+
catch: mapCreateError
|
|
289
|
+
}))));
|
|
290
|
+
return SnapshotId(snapName);
|
|
291
|
+
}),
|
|
292
|
+
destroy: (sid) => Effect.tryPromise({
|
|
293
|
+
try: () => Snapshot.get(sid),
|
|
294
|
+
catch: mapLookupError(sid)
|
|
295
|
+
}).pipe(Effect.flatMap((h) => Effect.tryPromise({
|
|
296
|
+
try: () => h.remove(),
|
|
297
|
+
catch: mapLookupError(sid)
|
|
298
|
+
}))),
|
|
299
|
+
list: Effect.tryPromise({
|
|
300
|
+
try: () => Snapshot.list(),
|
|
301
|
+
catch: mapCreateError
|
|
302
|
+
}).pipe(Effect.map(Array.filterMap((h) => h.name === null ? Result.failVoid : Result.succeed({
|
|
303
|
+
id: SnapshotId(h.name),
|
|
304
|
+
name: h.name
|
|
305
|
+
}))))
|
|
306
|
+
},
|
|
307
|
+
volumes: {
|
|
308
|
+
create: (name, options) => {
|
|
309
|
+
const builder = Volume.builder(name);
|
|
310
|
+
const sized = options?.quotaBytes === void 0 ? builder : builder.quota(Math.ceil(options.quotaBytes / (1024 * 1024)));
|
|
311
|
+
return Effect.tryPromise({
|
|
312
|
+
try: () => sized.create(),
|
|
313
|
+
catch: mapCreateError
|
|
314
|
+
}).pipe(Effect.map((vol) => VolumeId(vol.name)));
|
|
315
|
+
},
|
|
316
|
+
destroy: (vid) => Effect.tryPromise({
|
|
317
|
+
try: () => Volume.remove(vid),
|
|
318
|
+
catch: mapLookupError(vid)
|
|
319
|
+
}),
|
|
320
|
+
list: Effect.tryPromise({
|
|
321
|
+
try: () => Volume.list(),
|
|
322
|
+
catch: mapCreateError
|
|
323
|
+
}).pipe(Effect.map(Array.map((h) => ({
|
|
324
|
+
id: VolumeId(h.name),
|
|
325
|
+
name: h.name
|
|
326
|
+
}))))
|
|
327
|
+
},
|
|
328
|
+
ports: { expose: () => Effect.fail(new SandboxError.SandboxUnsupported({
|
|
329
|
+
provider: PROVIDER,
|
|
330
|
+
capability: "ports.expose",
|
|
331
|
+
reason: "microsandbox forwards guest ports to host ports at create time via the per-provider request; runtime port exposure is not supported"
|
|
332
|
+
})) }
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
/**
|
|
336
|
+
* Layer that registers the Microsandbox adapter against the `Sandbox`
|
|
337
|
+
* tag, with the capability markers Microsandbox actually supports.
|
|
338
|
+
*
|
|
339
|
+
* Shipped:
|
|
340
|
+
* - {@link SandboxHostnameAllowlist} — `allowDomain` rules at the
|
|
341
|
+
* policy layer (TLS-edge inspection / DNS filtering).
|
|
342
|
+
* - {@link SandboxSecretInjection} — `.secret(...)` placeholder
|
|
343
|
+
* substitution; the real value never enters the guest.
|
|
344
|
+
* - {@link SandboxSnapshots} — `Snapshot.builder(sourceSandbox)`.
|
|
345
|
+
* - {@link SandboxVolumes} — `Volume.builder(name)`.
|
|
346
|
+
*
|
|
347
|
+
* NOT shipped (calling those helpers fails at compile time):
|
|
348
|
+
* - `SandboxPauseResume` — microsandbox's `stop()` + `Sandbox.start(name)`
|
|
349
|
+
* resumes a previously stopped sandbox from disk, but RAM/process
|
|
350
|
+
* state is lost. That's different semantics from E2B-style
|
|
351
|
+
* memory-preserving pause/resume which `SandboxPauseResume` is
|
|
352
|
+
* meant to denote.
|
|
353
|
+
* - `SandboxCustomImage` — accepts OCI registry refs only.
|
|
354
|
+
* - `SandboxPortExposure` — ports are forwarded at create time via the
|
|
355
|
+
* per-provider request; runtime `exposePort` is unavailable.
|
|
356
|
+
* - `SandboxKernelSession`, `SandboxPty` — no Jupyter/REPL/PTY.
|
|
357
|
+
*/
|
|
358
|
+
const rejectCustomHeader = (secrets) => secrets === void 0 || !secrets.some((s) => s.header !== void 0) ? Effect.void : Effect.fail(new SandboxError.SandboxUnsupported({
|
|
359
|
+
provider: PROVIDER,
|
|
360
|
+
capability: "BoundSecret.header",
|
|
361
|
+
reason: "microsandbox always injects secrets as `Authorization: Bearer <value>` — custom headers aren't supported. Use the narrowed `MicrosandboxSandbox.create` surface, which omits this field."
|
|
362
|
+
}));
|
|
363
|
+
const upcastService = (s) => ({
|
|
364
|
+
...s,
|
|
365
|
+
create: (request) => rejectCustomHeader(request.secrets).pipe(Effect.andThen(s.create(request)))
|
|
366
|
+
});
|
|
367
|
+
const layer = (config = {}) => {
|
|
368
|
+
const service = buildService(config);
|
|
369
|
+
return Layer.mergeAll(Layer.succeed(MicrosandboxSandbox, 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));
|
|
370
|
+
};
|
|
371
|
+
//#endregion
|
|
372
|
+
export { MicrosandboxSandbox, layer, MicrosandboxSandbox_exports as t };
|
|
373
|
+
|
|
374
|
+
//# sourceMappingURL=MicrosandboxSandbox.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MicrosandboxSandbox.mjs","names":["MsbSandbox","Arr","CoreExecEvent","MsbSnapshot","MsbVolume","CoreSandbox"],"sources":["../src/MicrosandboxSandbox.ts"],"sourcesContent":["import {\n ExecEvent as CoreExecEvent,\n Sandbox as CoreSandbox,\n SandboxHostnameAllowlist,\n SandboxSecretInjection,\n SandboxSnapshots,\n SandboxVolumes,\n type BoundSecret,\n type CommonCreateRequest,\n type CommonExecRequest,\n type ExecResult,\n type ImageRef,\n type NetworkPolicy,\n type CommonSpawnRequest,\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 * as Memory from \"@effect-uai/core/Memory\"\nimport {\n Array as Arr,\n Clock,\n Context,\n Duration,\n Effect,\n Filter,\n Layer,\n Match,\n Option,\n Random,\n Record,\n Redacted,\n Result,\n Schedule,\n Scope,\n Stream,\n} from \"effect\"\nimport {\n type ExecHandle as MsbExecHandle,\n ExecTimeoutError,\n NetworkPolicyBuilder,\n Sandbox as MsbSandbox,\n SandboxNotFoundError,\n SandboxStillRunningError,\n Snapshot as MsbSnapshot,\n Volume as MsbVolume,\n type ExecEvent as MsbExecEvent,\n type SandboxBuilder as MsbSandboxBuilder,\n} from \"microsandbox\"\n\nconst PROVIDER = \"microsandbox\"\n\n// ---------------------------------------------------------------------------\n// Provider-narrowed request types.\n//\n// Microsandbox's secret-injection wire only supports the default\n// `Authorization: Bearer <value>` header convention — there's no\n// per-secret custom header. We omit `header` on the typed surface\n// (row B / type-level narrowing); calls via the generic `Sandbox.create`\n// surface that set `header` are rejected at runtime (row D\n// `SandboxUnsupported`) — see the upcast adapter in `layer`.\n// ---------------------------------------------------------------------------\n\nexport type MicrosandboxBoundSecret = Omit<BoundSecret, \"header\">\n\nexport type MicrosandboxCreateRequest = Omit<CommonCreateRequest, \"secrets\"> & {\n readonly secrets?: ReadonlyArray<MicrosandboxBoundSecret>\n readonly name?: string\n readonly cpus?: number\n /**\n * Memory size. Accepts a byte count, a {@link Memory.Memory}, or a\n * human string like `\"1 GiB\"` / `\"512 MiB\"`. Rounded up to whole\n * MiB at the SDK boundary (microsandbox's builder takes MiB).\n */\n readonly memory?: Memory.Input\n readonly workdir?: string\n readonly user?: string\n readonly maxDuration?: Duration.Input\n readonly idleTimeout?: Duration.Input\n /**\n * Stop any existing sandbox with the same name before creating.\n * `true` = `SIGTERM` with the SDK default grace; `{ graceMs }` =\n * explicit grace before `SIGKILL`.\n */\n readonly replace?: boolean | { readonly graceMs: number }\n /**\n * Detach so the sandbox outlives the calling scope. The scope\n * finalizer skips destroy; clean up via {@link Sandbox.destroy}.\n */\n readonly detached?: boolean\n}\n\nexport type MicrosandboxConfig = {\n /** Default OCI image to use when no `image` is supplied on create. */\n readonly defaultImage?: string\n}\n\n/**\n * Microsandbox-narrowed service. `create` accepts the narrowed\n * {@link MicrosandboxCreateRequest}; every other method is identical\n * to the generic {@link SandboxService}.\n */\nexport type MicrosandboxSandboxService = Omit<SandboxService, \"create\"> & {\n readonly create: (\n request: MicrosandboxCreateRequest,\n ) => Effect.Effect<SandboxInstance, SandboxError.SandboxError, Scope.Scope>\n}\n\nexport class MicrosandboxSandbox extends Context.Service<\n MicrosandboxSandbox,\n MicrosandboxSandboxService\n>()(\"@betalyra/effect-uai/providers/microsandbox/MicrosandboxSandbox\") {}\n\n// ---------------------------------------------------------------------------\n// Effect-native primitives\n// ---------------------------------------------------------------------------\n\n/** Random base-36 token, ~8 chars. Stand-in for `Math.random` IDs. */\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 mapCreateError = (e: unknown): SandboxError.SandboxError =>\n e instanceof SandboxStillRunningError\n ? new SandboxError.SandboxAlreadyExists({\n provider: PROVIDER,\n id: e.message,\n })\n : e instanceof SandboxNotFoundError\n ? new SandboxError.SandboxNotFound({ provider: PROVIDER, id: e.message })\n : new SandboxError.SandboxCreateFailed({\n provider: PROVIDER,\n raw: e,\n ...reasonOf(e),\n })\n\nconst mapExecError = (e: unknown): SandboxError.SandboxError =>\n e instanceof ExecTimeoutError\n ? new SandboxError.SandboxTimeout({\n provider: PROVIDER,\n operation: \"exec\",\n raw: e,\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\nconst mapLookupError =\n (id: string) =>\n (e: unknown): SandboxError.SandboxError =>\n e instanceof SandboxNotFoundError\n ? new SandboxError.SandboxNotFound({ provider: PROVIDER, id })\n : mapCreateError(e)\n\n// ---------------------------------------------------------------------------\n// Stopping & post-kill DB sync.\n//\n// Empirically (spike + SDK 0.4.6):\n// - `handle.connect()` + `live.stopAndWait()` resolves in 0ms on a\n// non-owning connection — the VM keeps running. Useless.\n// - `handle.kill()` signals the runtime to terminate, but the DB row's\n// `status` field only flips to \"stopped\" ~200ms later. Operations\n// that re-read DB status (`MsbSandbox.remove`, `handle.snapshot`)\n// reject during that window with a \"still running\" error.\n//\n// Rather than a fixed sleep (which over- and under-shoots), we kill\n// then retry the next operation on the transient race with exponential\n// backoff. Typical recovery: 1–3 retries (~50–350ms). Budget is 5\n// attempts ≈ 50+100+200+400+800 = 1.55s — plenty of margin for a\n// loaded host.\n// ---------------------------------------------------------------------------\n\nconst settleSchedule = Schedule.exponential(\"50 millis\")\nconst SETTLE_RETRIES = 5\n\n/**\n * Microsandbox's post-kill DB sync window surfaces as \"still running\"\n * either via `SandboxStillRunningError` (mapped to `SandboxAlreadyExists`\n * by {@link mapCreateError}) or, for `handle.snapshot`, a generic\n * `MicrosandboxError` whose message contains \"is not stopped\" /\n * \"still running\" (mapped to `SandboxCreateFailed`).\n */\nconst isPostKillRace = (e: SandboxError.SandboxError) =>\n e._tag === \"SandboxAlreadyExists\" ||\n (e._tag === \"SandboxCreateFailed\" &&\n typeof e.reason === \"string\" &&\n /still running|is not stopped/i.test(e.reason))\n\nconst retryOnSettle = <A, R>(\n effect: Effect.Effect<A, SandboxError.SandboxError, R>,\n): Effect.Effect<A, SandboxError.SandboxError, R> =>\n Effect.retry(effect, {\n schedule: settleSchedule,\n times: SETTLE_RETRIES,\n while: isPostKillRace,\n })\n\n/** Force-kill a running sandbox. No-op if absent or already stopped. */\nconst killRunning = (id: string) =>\n Effect.tryPromise({\n try: () => MsbSandbox.get(id),\n catch: mapLookupError(id),\n }).pipe(\n Effect.flatMap((handle) =>\n handle.status === \"running\"\n ? Effect.tryPromise({\n try: () => handle.kill(),\n catch: mapLookupError(id),\n })\n : Effect.void,\n ),\n Effect.catchTag(\"SandboxNotFound\", () => Effect.void),\n )\n\n/** Stop + remove. Idempotent — `SandboxNotFound` is treated as success. */\nconst destroyById = (id: SandboxId) =>\n killRunning(id).pipe(\n Effect.andThen(\n retryOnSettle(\n Effect.tryPromise({\n try: () => MsbSandbox.remove(id),\n catch: mapLookupError(id),\n }),\n ),\n ),\n Effect.catchTag(\"SandboxNotFound\", () => Effect.void),\n )\n\n// ---------------------------------------------------------------------------\n// Builder pipeline — a `Step` is a pure builder→builder transformation.\n// The create flow folds an array of Steps through the initial builder.\n// ---------------------------------------------------------------------------\n\ntype Step = (b: MsbSandboxBuilder) => MsbSandboxBuilder\n\nconst noop: Step = (b) => b\n\nconst when = <A>(value: A | undefined, step: (a: A) => Step): Step =>\n value === undefined ? noop : step(value)\n\n// ---------------------------------------------------------------------------\n// ImageRef → Step. Snapshots / registry honored; Default falls through\n// to the layer's `defaultImage`; Dockerfile is unsupported.\n// ---------------------------------------------------------------------------\n\nconst imageStep =\n (defaultImage: string | undefined) =>\n (image: ImageRef | undefined): Effect.Effect<Step, SandboxError.SandboxError> => {\n const fromDefault: Effect.Effect<Step, SandboxError.SandboxError> =\n defaultImage === undefined\n ? Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"image\",\n reason:\n \"microsandbox requires an image — pass `image` on the request or set `defaultImage` on the layer config\",\n }),\n )\n : Effect.succeed((b) => b.image(defaultImage))\n\n if (image === undefined) return fromDefault\n\n return Match.value(image).pipe(\n Match.tag(\"Default\", () => fromDefault),\n Match.tag(\n \"Registry\",\n ({ ref }): Effect.Effect<Step, SandboxError.SandboxError> =>\n Effect.succeed((b) => b.image(ref)),\n ),\n Match.tag(\n \"Snapshot\",\n ({ id }): Effect.Effect<Step, SandboxError.SandboxError> =>\n Effect.succeed((b) => b.fromSnapshot(id)),\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\",\n reason: \"microsandbox accepts OCI registry refs or snapshots, not Dockerfiles\",\n }),\n ),\n ),\n Match.exhaustive,\n )\n }\n\n// ---------------------------------------------------------------------------\n// NetworkPolicy → Step. CIDRs and hostnames both honored.\n// ---------------------------------------------------------------------------\n\nconst buildPolicy = (hosts: ReadonlyArray<string>, cidrs: ReadonlyArray<string>) => {\n const withHosts = Arr.reduce(hosts, new NetworkPolicyBuilder().defaultDeny(), (pb, host) =>\n pb.rule((rb) => rb.egress().allowDomain(host)),\n )\n return Arr.reduce(cidrs, withHosts, (pb, cidr) =>\n pb.rule((rb) => rb.egress().allow((d) => d.cidr(cidr))),\n )\n}\n\nconst networkStep = (policy: NetworkPolicy): Step =>\n Match.value(policy).pipe(\n Match.tag(\"Open\", (): Step => noop),\n Match.tag(\"Blocked\", (): Step => (b) => b.disableNetwork()),\n Match.tag(\n \"Allowlist\",\n ({ hosts, cidrs }): Step =>\n (b) =>\n b.network((n) => n.policyFromBuilder(buildPolicy(hosts ?? [], cidrs ?? []))),\n ),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// Secret / volume / replace Steps. Each item folds into its own Step;\n// the create pipeline reduces them through the builder.\n// ---------------------------------------------------------------------------\n\nconst secretStep =\n (secret: {\n readonly name: string\n readonly value: Redacted.Redacted<string>\n readonly hosts: ReadonlyArray<string>\n }): Step =>\n (b) =>\n b.secret((sb) =>\n Arr.reduce(\n secret.hosts,\n sb.env(secret.name).value(Redacted.value(secret.value)),\n (acc, host) => acc.allowHost(host),\n ).injectHeaders(true),\n )\n\nconst volumeStep =\n (vol: { readonly id: VolumeId; readonly mountPath: string; readonly readonly?: boolean }): Step =>\n (b) =>\n b.volume(vol.mountPath, (m) => (vol.readonly ? m.named(vol.id).readonly() : m.named(vol.id)))\n\nconst replaceStep = (replace: MicrosandboxCreateRequest[\"replace\"]): Step =>\n replace === true\n ? (b) => b.replace()\n : typeof replace === \"object\"\n ? (b) => b.replaceWithGrace(replace.graceMs)\n : noop\n\n// ---------------------------------------------------------------------------\n// argv splitter — string is run via shell semantics by the SDK, array\n// is direct argv. Empty argv is a typed error.\n// ---------------------------------------------------------------------------\n\nconst argv = (\n cmd: CommonExecRequest[\"cmd\"],\n): Effect.Effect<\n { readonly cmd: string; readonly args: ReadonlyArray<string> },\n SandboxError.SandboxError\n> =>\n typeof cmd === \"string\"\n ? Effect.succeed({ cmd, args: [] as ReadonlyArray<string> })\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({ cmd: head, args: Arr.drop(cmd, 1) }),\n })\n\n// ---------------------------------------------------------------------------\n// CommonExecRequest → ExecOptionsBuilder configurator.\n// ---------------------------------------------------------------------------\n\ntype ExecConfigure = Parameters<MsbSandbox[\"execWith\"]>[1]\n\nconst toBuffer = (input: Uint8Array | string) =>\n typeof input === \"string\" ? Buffer.from(input, \"utf-8\") : Buffer.from(input)\n\n/**\n * `string` / `Uint8Array` stdin uses one-shot `stdinBytes`; a Stream\n * uses `stdinPipe` and gets fed via `feedStdin` once the handle is open.\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\nconst execConfigure =\n (args: ReadonlyArray<string>, request: CommonSpawnRequest): ExecConfigure =>\n (b) => {\n const withArgs = args.length === 0 ? b : b.args([...args])\n const withCwd = request.cwd === undefined ? withArgs : withArgs.cwd(request.cwd)\n const withEnv =\n request.env === undefined\n ? withCwd\n : Record.reduce(request.env, withCwd, (acc, v, k) => acc.env(k, v))\n const withTimeout =\n request.timeout === undefined\n ? withEnv\n : withEnv.timeout(Duration.toMillis(Duration.fromInputUnsafe(request.timeout)))\n if (request.stdin === undefined) return withTimeout\n if (stdinIsStream(request.stdin)) return withTimeout.stdinPipe()\n return withTimeout.stdinBytes(toBuffer(request.stdin))\n }\n\n// ---------------------------------------------------------------------------\n// Stream stdin → SDK `ExecSink`. Takes the writer once, pumps each\n// chunk via `sink.write(Buffer)`, closes on stream completion. Errors\n// from the SDK side (sink already taken / closed / process exited\n// mid-stream) are mapped through {@link mapExecError}. Caller forks\n// this against a `Scope` so the pump dies with the spawn.\n// ---------------------------------------------------------------------------\n\nconst feedStreamStdin = (\n handle: MsbExecHandle,\n stream: Stream.Stream<Uint8Array, never, never>,\n): Effect.Effect<void, SandboxError.SandboxError> =>\n Effect.tryPromise({\n try: () => handle.takeStdin(),\n catch: mapExecError,\n }).pipe(\n Effect.flatMap((sink) =>\n sink === null\n ? Effect.fail(\n new SandboxError.SandboxInvalidRequest({\n provider: PROVIDER,\n param: \"stdin\",\n reason: \"stdin sink already taken — the SDK exposes it once per spawn\",\n }),\n )\n : Stream.runForEach(stream, (chunk) =>\n Effect.tryPromise({\n try: () => sink.write(Buffer.from(chunk)),\n catch: mapExecError,\n }),\n ).pipe(\n Effect.andThen(\n Effect.tryPromise({\n try: () => sink.close(),\n catch: mapExecError,\n }),\n ),\n ),\n ),\n )\n\n// ---------------------------------------------------------------------------\n// MsbExecEvent → Option<CoreExecEvent>. `started` carries the pid but\n// downstream observers don't need it — drop via `Option.none()`.\n// Duration is precomputed by the caller (Clock-based).\n// ---------------------------------------------------------------------------\n\nconst execEventFromMsb = (event: MsbExecEvent, durationMs: number): Option.Option<CoreExecEvent> =>\n Match.value(event).pipe(\n Match.discriminators(\"kind\")({\n started: () => Option.none<CoreExecEvent>(),\n stdout: ({ data }) => Option.some(CoreExecEvent.Stdout({ chunk: data })),\n stderr: ({ data }) => Option.some(CoreExecEvent.Stderr({ chunk: data })),\n exited: ({ code }) => Option.some(CoreExecEvent.Complete({ exitCode: code, durationMs })),\n }),\n Match.exhaustive,\n )\n\nconst someEvent = Filter.fromPredicateOption((o: Option.Option<CoreExecEvent>) => o)\n\nconst eventStream = (handle: AsyncIterable<MsbExecEvent>, startedAt: number) =>\n Stream.fromAsyncIterable(handle, mapExecError).pipe(\n Stream.mapEffect((event) =>\n Effect.map(Clock.currentTimeMillis, (now) => execEventFromMsb(event, now - startedAt)),\n ),\n Stream.filterMap(someEvent),\n )\n\n// ---------------------------------------------------------------------------\n// Live SandboxInstance adapter.\n// ---------------------------------------------------------------------------\n\nconst adaptInstance = (msb: MsbSandbox): SandboxInstance => {\n const id = SandboxId(msb.name)\n\n const openExecStream = (request: CommonSpawnRequest) =>\n Effect.gen(function* () {\n const { cmd, args } = yield* argv(request.cmd)\n const startedAt = yield* Clock.currentTimeMillis\n // `asyncDispose` alone drops the relay connection but does NOT\n // terminate the guest process — sleep / dev servers / watchers\n // happily keep running. Explicitly kill first; both calls are\n // safe to invoke on an already-exited handle.\n const handle = yield* Effect.acquireRelease(\n Effect.tryPromise({\n try: () => msb.execStreamWith(cmd, execConfigure(args, request)),\n catch: mapExecError,\n }),\n (h) =>\n Effect.tryPromise({\n try: () => h.kill(),\n catch: () => undefined,\n }).pipe(Effect.ignore, Effect.andThen(Effect.promise(() => h[Symbol.asyncDispose]()))),\n )\n // Stream stdin: fork a fiber that drains the user's stream into\n // the SDK's `ExecSink`, closing on completion. Scoped to the\n // caller's scope so it dies if the spawn is interrupted.\n if (stdinIsStream(request.stdin)) {\n yield* Effect.forkScoped(feedStreamStdin(handle, request.stdin))\n }\n return { handle, startedAt }\n })\n\n return {\n id,\n\n exec: (request) =>\n Effect.gen(function* () {\n const { cmd, args } = yield* argv(request.cmd)\n const startedAt = yield* Clock.currentTimeMillis\n const output = yield* Effect.tryPromise({\n try: () => msb.execWith(cmd, execConfigure(args, request)),\n catch: mapExecError,\n })\n const completedAt = yield* Clock.currentTimeMillis\n return {\n exitCode: output.code,\n stdout: output.stdout(),\n stderr: output.stderr(),\n durationMs: completedAt - startedAt,\n } satisfies ExecResult\n }),\n\n execStream: (request) =>\n Stream.unwrap(\n Effect.map(openExecStream(request), ({ handle, startedAt }) =>\n eventStream(handle, startedAt),\n ),\n ),\n\n spawn: (request) =>\n Effect.map(\n openExecStream(request),\n ({ handle, startedAt }): ProcessHandle => ({\n // Microsandbox surfaces pid via the \"started\" event; the\n // handle exposes no sync getter. Surface 0 until it does.\n pid: 0,\n events: eventStream(handle, startedAt),\n kill: Effect.tryPromise({\n try: () => handle.kill(),\n catch: mapExecError,\n }),\n exit: Effect.tryPromise({\n try: () => handle.wait(),\n catch: mapExecError,\n }).pipe(Effect.map((status) => ({ exitCode: status.code }))),\n }),\n ),\n\n files: {\n read: (path) =>\n Effect.tryPromise({\n try: () => msb.fs().read(path),\n catch: mapFsError,\n }),\n write: (path, contents) =>\n Effect.tryPromise({\n try: () => msb.fs().write(path, contents),\n catch: mapFsError,\n }),\n remove: (path) =>\n Effect.tryPromise({\n try: () => msb.fs().remove(path),\n catch: mapFsError,\n }),\n mkdir: (path) =>\n Effect.tryPromise({\n try: () => msb.fs().mkdir(path),\n catch: mapFsError,\n }),\n list: (path) =>\n Effect.tryPromise({\n try: () => msb.fs().list(path),\n catch: mapFsError,\n }).pipe(Effect.map(Arr.map((e) => ({ path: e.path, kind: e.kind })))),\n exists: (path) =>\n Effect.tryPromise({\n try: () => msb.fs().exists(path),\n catch: mapFsError,\n }),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Service factory\n// ---------------------------------------------------------------------------\n\nconst buildService = (config: MicrosandboxConfig): MicrosandboxSandboxService => {\n const resolveImageStep = imageStep(config.defaultImage)\n\n const acquireSandbox = (request: MicrosandboxCreateRequest, name: string) =>\n Effect.gen(function* () {\n const img = yield* resolveImageStep(request.image)\n\n const steps: ReadonlyArray<Step> = [\n img,\n when(request.cpus, (n) => (b) => b.cpus(n)),\n when(request.memory, (m) => (b) => b.memory(Math.ceil(Memory.toBytes(m) / (1024 * 1024)))),\n when(request.workdir, (p) => (b) => b.workdir(p)),\n when(request.user, (u) => (b) => b.user(u)),\n when(\n request.maxDuration,\n (d) => (b) =>\n b.maxDuration(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1000)),\n ),\n when(\n request.idleTimeout,\n (d) => (b) =>\n b.idleTimeout(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1000)),\n ),\n when(\n request.timeout,\n (t) => (b) =>\n b.maxDuration(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(t)) / 1000)),\n ),\n when(request.env, (env) => (b) => b.envs(env)),\n when(request.network, networkStep),\n ...Arr.map(request.secrets ?? [], secretStep),\n ...Arr.map(request.volumes ?? [], volumeStep),\n replaceStep(request.replace),\n ]\n\n const built = Arr.reduce(steps, MsbSandbox.builder(name), (b, step) => step(b))\n return yield* Effect.tryPromise({\n try: () => (request.detached ? built.createDetached() : built.create()),\n catch: mapCreateError,\n })\n })\n\n // Attach picks the right resume path: `connect()` for an already-\n // running (e.g. detached) sandbox, `startDetached()` for a stopped\n // one. Plain `MsbSandbox.start(id)` rejects on running sandboxes,\n // which breaks the detached-survives-scope-close flow.\n const acquireAttached = (id: SandboxId) =>\n Effect.tryPromise({\n try: () => MsbSandbox.get(id),\n catch: mapLookupError(id),\n }).pipe(\n Effect.flatMap((handle) =>\n Effect.tryPromise({\n try: handle.status === \"running\" ? () => handle.connect() : () => handle.startDetached(),\n catch: mapLookupError(id),\n }),\n ),\n )\n\n return {\n create: (request) =>\n Effect.gen(function* () {\n const token = yield* randomToken\n const name = SandboxId(request.name ?? `eff-uai-${token}`)\n const detached = request.detached === true\n // Detached sandboxes outlive the scope — finalizer skips\n // teardown and the user calls `destroy(id)` explicitly.\n const msb = yield* Effect.acquireRelease(acquireSandbox(request, name), () =>\n detached ? Effect.void : Effect.ignore(destroyById(name)),\n )\n return adaptInstance(msb)\n }),\n\n attach: (id) =>\n Effect.acquireRelease(\n acquireAttached(id),\n // Detach on scope close — do NOT stop.\n () => Effect.void,\n ).pipe(Effect.map(adaptInstance)),\n\n list: Effect.tryPromise({\n try: () => MsbSandbox.list(),\n catch: mapCreateError,\n }).pipe(Effect.map(Arr.map((h): SandboxRef => ({ id: SandboxId(h.name), name: h.name })))),\n\n destroy: destroyById,\n\n snapshots: {\n // Microsandbox requires the source sandbox to be stopped before\n // snapshotting. We must also flush root-FS writes BEFORE stop:\n // SDK 0.4.6's stop path races the guest poweroff and `fs().write`\n // data sitting in page cache never reaches the upper.ext4. Fix\n // landed upstream as microsandbox#746 but isn't released — we\n // call `sync` via exec first. Drop this `flush` step once the\n // SDK is bumped past the fix.\n //\n // `SandboxHandle.snapshot(name)` registers the artifact in the\n // DB index (visible to `Snapshot.list` / `msb snapshot list` /\n // `fromSnapshot(name)`); `Snapshot.builder` skips that, so we\n // go through the handle. The handle's `inner.status` is read at\n // fetch time, so we re-`get` AFTER the stop has settled.\n create: (from, name) =>\n Effect.gen(function* () {\n const token = yield* randomToken\n const snapName = name ?? `eff-uai-snap-${token}`\n yield* Effect.ignore(from.exec({ cmd: [\"sync\"] }))\n yield* killRunning(from.id)\n // Re-`get` per attempt: `SandboxHandle.status` is snapshotted\n // at fetch time, so a stale handle would keep failing even\n // after the DB lands the \"stopped\" status.\n yield* retryOnSettle(\n Effect.tryPromise({\n try: () => MsbSandbox.get(from.id),\n catch: mapLookupError(from.id),\n }).pipe(\n Effect.flatMap((handle) =>\n Effect.tryPromise({\n try: () => handle.snapshot(snapName),\n catch: mapCreateError,\n }),\n ),\n ),\n )\n return SnapshotId(snapName)\n }),\n destroy: (sid) =>\n Effect.tryPromise({\n try: () => MsbSnapshot.get(sid),\n catch: mapLookupError(sid),\n }).pipe(\n Effect.flatMap((h) =>\n Effect.tryPromise({\n try: () => h.remove(),\n catch: mapLookupError(sid),\n }),\n ),\n ),\n list: Effect.tryPromise({\n try: () => MsbSnapshot.list(),\n catch: mapCreateError,\n }).pipe(\n Effect.map(\n Arr.filterMap((h) =>\n h.name === null\n ? Result.failVoid\n : Result.succeed({ id: SnapshotId(h.name), name: h.name }),\n ),\n ),\n ),\n },\n\n volumes: {\n create: (name, options) => {\n const builder = MsbVolume.builder(name)\n const sized =\n options?.quotaBytes === undefined\n ? builder\n : builder.quota(Math.ceil(options.quotaBytes / (1024 * 1024)))\n return Effect.tryPromise({\n try: () => sized.create(),\n catch: mapCreateError,\n }).pipe(Effect.map((vol) => VolumeId(vol.name)))\n },\n destroy: (vid) =>\n Effect.tryPromise({\n try: () => MsbVolume.remove(vid),\n catch: mapLookupError(vid),\n }),\n list: Effect.tryPromise({\n try: () => MsbVolume.list(),\n catch: mapCreateError,\n }).pipe(Effect.map(Arr.map((h) => ({ id: VolumeId(h.name), name: h.name })))),\n },\n\n ports: {\n // Defensive stub. The Layer doesn't ship `SandboxPortExposure`,\n // so the free helper `Sandbox.exposePort` is a compile error\n // against this provider. This stub only fires if a caller\n // bypassed the marker and reached into `s.ports.expose` directly.\n expose: () =>\n Effect.fail(\n new SandboxError.SandboxUnsupported({\n provider: PROVIDER,\n capability: \"ports.expose\",\n reason:\n \"microsandbox forwards guest ports to host ports at create time via the per-provider request; runtime port exposure is not supported\",\n }),\n ),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * Layer that registers the Microsandbox adapter against the `Sandbox`\n * tag, with the capability markers Microsandbox actually supports.\n *\n * Shipped:\n * - {@link SandboxHostnameAllowlist} — `allowDomain` rules at the\n * policy layer (TLS-edge inspection / DNS filtering).\n * - {@link SandboxSecretInjection} — `.secret(...)` placeholder\n * substitution; the real value never enters the guest.\n * - {@link SandboxSnapshots} — `Snapshot.builder(sourceSandbox)`.\n * - {@link SandboxVolumes} — `Volume.builder(name)`.\n *\n * NOT shipped (calling those helpers fails at compile time):\n * - `SandboxPauseResume` — microsandbox's `stop()` + `Sandbox.start(name)`\n * resumes a previously stopped sandbox from disk, but RAM/process\n * state is lost. That's different semantics from E2B-style\n * memory-preserving pause/resume which `SandboxPauseResume` is\n * meant to denote.\n * - `SandboxCustomImage` — accepts OCI registry refs only.\n * - `SandboxPortExposure` — ports are forwarded at create time via the\n * per-provider request; runtime `exposePort` is unavailable.\n * - `SandboxKernelSession`, `SandboxPty` — no Jupyter/REPL/PTY.\n */\n// Row-D guard: reject `BoundSecret.header` when callers route through\n// the generic `Sandbox.create` surface (the typed `MicrosandboxSandbox`\n// surface omits the field at the type level — row B). Microsandbox's\n// secret API doesn't accept arbitrary headers; silently dropping would\n// produce wrong outbound auth, so we fail loudly.\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 \"microsandbox always injects secrets as `Authorization: Bearer <value>` — custom headers aren't supported. Use the narrowed `MicrosandboxSandbox.create` surface, which omits this field.\",\n }),\n )\n\nconst upcastService = (s: MicrosandboxSandboxService): SandboxService => ({\n ...s,\n create: (request) =>\n rejectCustomHeader(request.secrets).pipe(\n Effect.andThen(s.create(request as MicrosandboxCreateRequest)),\n ),\n})\n\nexport const layer = (config: MicrosandboxConfig = {}) => {\n const service = buildService(config)\n return Layer.mergeAll(\n Layer.succeed(MicrosandboxSandbox, 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 )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effect-uai/microsandbox",
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "Microsandbox local microVM sandbox provider for @effect-uai/core.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agents",
|
|
7
|
+
"ai",
|
|
8
|
+
"effect",
|
|
9
|
+
"microsandbox",
|
|
10
|
+
"microvm",
|
|
11
|
+
"sandbox"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/betalyra/effect-uai/tree/main/packages/providers/microsandbox",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/betalyra/effect-uai/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Betalyra",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/betalyra/effect-uai",
|
|
22
|
+
"directory": "packages/providers/microsandbox"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"src",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.mjs",
|
|
32
|
+
"types": "./dist/index.d.mts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.mts",
|
|
36
|
+
"import": "./dist/index.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./MicrosandboxSandbox": {
|
|
39
|
+
"types": "./dist/MicrosandboxSandbox.d.mts",
|
|
40
|
+
"import": "./dist/MicrosandboxSandbox.mjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"effect": "4.0.0-beta.57",
|
|
48
|
+
"microsandbox": "^0.4.6",
|
|
49
|
+
"typescript": "^6.0.3",
|
|
50
|
+
"@effect-uai/core": "0.5.2"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@effect-uai/core": ">=0.5.0 <1",
|
|
54
|
+
"effect": "4.0.0-beta.57",
|
|
55
|
+
"microsandbox": "^0.4.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsdown",
|
|
59
|
+
"typecheck": "tsc --noEmit"
|
|
60
|
+
}
|
|
61
|
+
}
|