@effect-uai/microsandbox 0.11.0 → 0.12.1
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/dist/MicrosandboxSandbox.d.mts +2 -3
- package/dist/MicrosandboxSandbox.d.mts.map +1 -1
- package/dist/MicrosandboxSandbox.mjs +23 -13
- package/dist/MicrosandboxSandbox.mjs.map +1 -1
- package/package.json +7 -7
- package/src/MicrosandboxSandbox.ts +52 -45
- /package/dist/{chunk-CfYAbeIz.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
|
@@ -2,8 +2,6 @@ import { BoundSecret, CommonCreateRequest, Sandbox, SandboxHostnameAllowlist, Sa
|
|
|
2
2
|
import * as SandboxError from "@effect-uai/core/SandboxError";
|
|
3
3
|
import * as Memory from "@effect-uai/core/Memory";
|
|
4
4
|
import { Context, Duration, Effect, Layer, Scope } from "effect";
|
|
5
|
-
|
|
6
|
-
//#region src/MicrosandboxSandbox.d.ts
|
|
7
5
|
declare namespace MicrosandboxSandbox_d_exports {
|
|
8
6
|
export { MicrosandboxBoundSecret, MicrosandboxConfig, MicrosandboxCreateRequest, MicrosandboxSandbox, MicrosandboxSandboxService, layer };
|
|
9
7
|
}
|
|
@@ -37,7 +35,8 @@ type MicrosandboxCreateRequest = Omit<CommonCreateRequest, "secrets"> & {
|
|
|
37
35
|
readonly detached?: boolean;
|
|
38
36
|
};
|
|
39
37
|
type MicrosandboxConfig = {
|
|
40
|
-
/** Default OCI image to use when no `image` is supplied on create. */
|
|
38
|
+
/** Default OCI image to use when no `image` is supplied on create. */
|
|
39
|
+
readonly defaultImage?: string;
|
|
41
40
|
};
|
|
42
41
|
/**
|
|
43
42
|
* Microsandbox-narrowed service. `create` accepts the narrowed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MicrosandboxSandbox.d.mts","names":[],"sources":["../src/MicrosandboxSandbox.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"MicrosandboxSandbox.d.mts","names":[],"sources":["../src/MicrosandboxSandbox.ts"],"mappings":";;;;;;;KAoEY,0BAA0B,KAAK;KAE/B,4BAA4B,KAAK;WAClC,UAAU,cAAc;WACxB;WACA;;;;;;WAMA,SAAS,OAAO;WAChB;WACA;WACA,cAAc,SAAS;WACvB,cAAc,SAAS;;;;;;WAMvB;aAA+B;;;;;;WAK/B;;KAGC;;WAED;;;;;;;KAQC,6BAA6B,KAAK;WACnC,SACP,SAAS,8BACN,OAAO,OAAO,iBAAiB,aAAa,cAAc,MAAM;;;cAG1D,4BAA4B;cAkvB5B,QAAK,SAAY,uBAAkB,MAAA,MAAA,sBAAA,UAAA,2BAAA,yBAAA,mBAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "./
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { ExecEvent, Sandbox, SandboxHostnameAllowlist, SandboxId, SandboxSecretInjection, SandboxSnapshots, SandboxVolumes, SnapshotId, VolumeId } from "@effect-uai/core/Sandbox";
|
|
3
3
|
import * as SandboxError from "@effect-uai/core/SandboxError";
|
|
4
4
|
import * as Memory from "@effect-uai/core/Memory";
|
|
@@ -93,7 +93,7 @@ const buildPolicy = (hosts, cidrs) => {
|
|
|
93
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
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
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.
|
|
96
|
+
const replaceStep = (replace) => replace === true ? (b) => b.replace() : typeof replace === "object" ? (b) => b.replaceWithTimeout(replace.graceMs) : noop;
|
|
97
97
|
const argv = (cmd) => typeof cmd === "string" ? Effect.succeed({
|
|
98
98
|
cmd,
|
|
99
99
|
args: []
|
|
@@ -233,7 +233,7 @@ const buildService = (config) => {
|
|
|
233
233
|
const steps = [
|
|
234
234
|
yield* resolveImageStep(request.image),
|
|
235
235
|
when(request.cpus, (n) => (b) => b.cpus(n)),
|
|
236
|
-
when(request.memory, (m) => (b) => b.memory(Math.ceil(Memory.toBytes(m) /
|
|
236
|
+
when(request.memory, (m) => (b) => b.memory(Math.ceil(Memory.toBytes(m) / 1048576))),
|
|
237
237
|
when(request.workdir, (p) => (b) => b.workdir(p)),
|
|
238
238
|
when(request.user, (u) => (b) => b.user(u)),
|
|
239
239
|
when(request.maxDuration, (d) => (b) => b.maxDuration(Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(d)) / 1e3))),
|
|
@@ -247,7 +247,7 @@ const buildService = (config) => {
|
|
|
247
247
|
];
|
|
248
248
|
const built = Array.reduce(steps, Sandbox$1.builder(name), (b, step) => step(b));
|
|
249
249
|
return yield* Effect.tryPromise({
|
|
250
|
-
try: () => request.detached ? built.
|
|
250
|
+
try: () => (request.detached ? built.detached(true) : built).create(),
|
|
251
251
|
catch: mapCreateError
|
|
252
252
|
});
|
|
253
253
|
});
|
|
@@ -263,16 +263,26 @@ const buildService = (config) => {
|
|
|
263
263
|
const token = yield* randomToken;
|
|
264
264
|
const name = SandboxId(request.name ?? `eff-uai-${token}`);
|
|
265
265
|
const detached = request.detached === true;
|
|
266
|
-
|
|
266
|
+
const msb = yield* Effect.acquireRelease(acquireSandbox(request, name), () => detached ? Effect.void : Effect.ignore(destroyById(name)));
|
|
267
|
+
return adaptInstance(msb);
|
|
267
268
|
}),
|
|
268
269
|
attach: (id) => Effect.acquireRelease(acquireAttached(id), () => Effect.void).pipe(Effect.map(adaptInstance)),
|
|
269
|
-
list: Effect.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
270
|
+
list: Effect.gen(function* () {
|
|
271
|
+
const refs = [];
|
|
272
|
+
let cursor;
|
|
273
|
+
while (true) {
|
|
274
|
+
const page = yield* Effect.tryPromise({
|
|
275
|
+
try: () => cursor === void 0 ? Sandbox$1.list() : Sandbox$1.listWith((l) => l.cursor(cursor)),
|
|
276
|
+
catch: mapCreateError
|
|
277
|
+
});
|
|
278
|
+
refs.push(...Array.map(page.sandboxes, (h) => ({
|
|
279
|
+
id: SandboxId(h.name),
|
|
280
|
+
name: h.name
|
|
281
|
+
})));
|
|
282
|
+
if (page.nextCursor === void 0) return refs;
|
|
283
|
+
cursor = page.nextCursor;
|
|
284
|
+
}
|
|
285
|
+
}),
|
|
276
286
|
destroy: destroyById,
|
|
277
287
|
snapshots: {
|
|
278
288
|
create: (from, name) => Effect.gen(function* () {
|
|
@@ -307,7 +317,7 @@ const buildService = (config) => {
|
|
|
307
317
|
volumes: {
|
|
308
318
|
create: (name, options) => {
|
|
309
319
|
const builder = Volume.builder(name);
|
|
310
|
-
const sized = options?.quotaBytes === void 0 ? builder : builder.quota(Math.ceil(options.quotaBytes /
|
|
320
|
+
const sized = options?.quotaBytes === void 0 ? builder : builder.quota(Math.ceil(options.quotaBytes / 1048576));
|
|
311
321
|
return Effect.tryPromise({
|
|
312
322
|
try: () => sized.create(),
|
|
313
323
|
catch: mapCreateError
|
|
@@ -1 +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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"}
|
|
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; not re-verified on 0.6.x):\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(\"Registry\", ({ ref }): Effect.Effect<Step, SandboxError.SandboxError> =>\n Effect.succeed((b) => b.image(ref)),\n ),\n Match.tag(\"Snapshot\", ({ id }): Effect.Effect<Step, SandboxError.SandboxError> =>\n Effect.succeed((b) => b.fromSnapshot(id)),\n ),\n Match.tag(\"Dockerfile\", (): 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.replaceWithTimeout(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(openExecStream(request), ({ 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 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.detached(true) : 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()` is paginated from 0.6 on; walk the cursor so the\n // service contract stays \"every visible sandbox\".\n list: Effect.gen(function* () {\n const refs: Array<SandboxRef> = []\n let cursor: string | undefined\n while (true) {\n const page = yield* Effect.tryPromise({\n try: () =>\n cursor === undefined\n ? MsbSandbox.list()\n : MsbSandbox.listWith((l) => l.cursor(cursor as string)),\n catch: mapCreateError,\n })\n refs.push(\n ...Arr.map(page.sandboxes, (h): SandboxRef => ({ id: SandboxId(h.name), name: h.name })),\n )\n if (page.nextCursor === undefined) return refs\n cursor = page.nextCursor\n }\n }),\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, so we\n // call `sync` via exec first. The upstream fix (microsandbox#746)\n // should be in 0.6.x; drop this `flush` step once a live snapshot\n // run confirms it.\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/microsandbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Microsandbox local microVM sandbox provider for @effect-uai/core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -44,15 +44,15 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"effect": "4.0.0-
|
|
48
|
-
"microsandbox": "^0.
|
|
49
|
-
"typescript": "^
|
|
50
|
-
"@effect-uai/core": "0.
|
|
47
|
+
"effect": "4.0.0-rc.111",
|
|
48
|
+
"microsandbox": "^0.6.12",
|
|
49
|
+
"typescript": "^7.0.2",
|
|
50
|
+
"@effect-uai/core": "0.12.1"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@effect-uai/core": ">=0.5.0 <1",
|
|
54
|
-
"effect": ">=4.0.0-
|
|
55
|
-
"microsandbox": "^0.
|
|
54
|
+
"effect": ">=4.0.0-rc.111 <5.0.0",
|
|
55
|
+
"microsandbox": "^0.6.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsdown",
|
|
@@ -173,7 +173,7 @@ const mapLookupError =
|
|
|
173
173
|
// ---------------------------------------------------------------------------
|
|
174
174
|
// Stopping & post-kill DB sync.
|
|
175
175
|
//
|
|
176
|
-
// Empirically (spike + SDK 0.4.6):
|
|
176
|
+
// Empirically (spike + SDK 0.4.6; not re-verified on 0.6.x):
|
|
177
177
|
// - `handle.connect()` + `live.stopAndWait()` resolves in 0ms on a
|
|
178
178
|
// non-owning connection — the VM keeps running. Useless.
|
|
179
179
|
// - `handle.kill()` signals the runtime to terminate, but the DB row's
|
|
@@ -280,26 +280,20 @@ const imageStep =
|
|
|
280
280
|
|
|
281
281
|
return Match.value(image).pipe(
|
|
282
282
|
Match.tag("Default", () => fromDefault),
|
|
283
|
-
Match.tag(
|
|
284
|
-
|
|
285
|
-
({ ref }): Effect.Effect<Step, SandboxError.SandboxError> =>
|
|
286
|
-
Effect.succeed((b) => b.image(ref)),
|
|
283
|
+
Match.tag("Registry", ({ ref }): Effect.Effect<Step, SandboxError.SandboxError> =>
|
|
284
|
+
Effect.succeed((b) => b.image(ref)),
|
|
287
285
|
),
|
|
288
|
-
Match.tag(
|
|
289
|
-
|
|
290
|
-
({ id }): Effect.Effect<Step, SandboxError.SandboxError> =>
|
|
291
|
-
Effect.succeed((b) => b.fromSnapshot(id)),
|
|
286
|
+
Match.tag("Snapshot", ({ id }): Effect.Effect<Step, SandboxError.SandboxError> =>
|
|
287
|
+
Effect.succeed((b) => b.fromSnapshot(id)),
|
|
292
288
|
),
|
|
293
|
-
Match.tag(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}),
|
|
302
|
-
),
|
|
289
|
+
Match.tag("Dockerfile", (): Effect.Effect<Step, SandboxError.SandboxError> =>
|
|
290
|
+
Effect.fail(
|
|
291
|
+
new SandboxError.SandboxUnsupported({
|
|
292
|
+
provider: PROVIDER,
|
|
293
|
+
capability: "image",
|
|
294
|
+
reason: "microsandbox accepts OCI registry refs or snapshots, not Dockerfiles",
|
|
295
|
+
}),
|
|
296
|
+
),
|
|
303
297
|
),
|
|
304
298
|
Match.exhaustive,
|
|
305
299
|
)
|
|
@@ -360,7 +354,7 @@ const replaceStep = (replace: MicrosandboxCreateRequest["replace"]): Step =>
|
|
|
360
354
|
replace === true
|
|
361
355
|
? (b) => b.replace()
|
|
362
356
|
: typeof replace === "object"
|
|
363
|
-
? (b) => b.
|
|
357
|
+
? (b) => b.replaceWithTimeout(replace.graceMs)
|
|
364
358
|
: noop
|
|
365
359
|
|
|
366
360
|
// ---------------------------------------------------------------------------
|
|
@@ -555,23 +549,20 @@ const adaptInstance = (msb: MsbSandbox): SandboxInstance => {
|
|
|
555
549
|
),
|
|
556
550
|
|
|
557
551
|
spawn: (request) =>
|
|
558
|
-
Effect.map(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
try: () => handle.kill(),
|
|
567
|
-
catch: mapExecError,
|
|
568
|
-
}),
|
|
569
|
-
exit: Effect.tryPromise({
|
|
570
|
-
try: () => handle.wait(),
|
|
571
|
-
catch: mapExecError,
|
|
572
|
-
}).pipe(Effect.map((status) => ({ exitCode: status.code }))),
|
|
552
|
+
Effect.map(openExecStream(request), ({ handle, startedAt }): ProcessHandle => ({
|
|
553
|
+
// Microsandbox surfaces pid via the "started" event; the
|
|
554
|
+
// handle exposes no sync getter. Surface 0 until it does.
|
|
555
|
+
pid: 0,
|
|
556
|
+
events: eventStream(handle, startedAt),
|
|
557
|
+
kill: Effect.tryPromise({
|
|
558
|
+
try: () => handle.kill(),
|
|
559
|
+
catch: mapExecError,
|
|
573
560
|
}),
|
|
574
|
-
|
|
561
|
+
exit: Effect.tryPromise({
|
|
562
|
+
try: () => handle.wait(),
|
|
563
|
+
catch: mapExecError,
|
|
564
|
+
}).pipe(Effect.map((status) => ({ exitCode: status.code }))),
|
|
565
|
+
})),
|
|
575
566
|
|
|
576
567
|
files: {
|
|
577
568
|
read: (path) =>
|
|
@@ -649,7 +640,7 @@ const buildService = (config: MicrosandboxConfig): MicrosandboxSandboxService =>
|
|
|
649
640
|
|
|
650
641
|
const built = Arr.reduce(steps, MsbSandbox.builder(name), (b, step) => step(b))
|
|
651
642
|
return yield* Effect.tryPromise({
|
|
652
|
-
try: () => (request.detached ? built.
|
|
643
|
+
try: () => (request.detached ? built.detached(true) : built).create(),
|
|
653
644
|
catch: mapCreateError,
|
|
654
645
|
})
|
|
655
646
|
})
|
|
@@ -692,10 +683,26 @@ const buildService = (config: MicrosandboxConfig): MicrosandboxSandboxService =>
|
|
|
692
683
|
() => Effect.void,
|
|
693
684
|
).pipe(Effect.map(adaptInstance)),
|
|
694
685
|
|
|
695
|
-
list
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
686
|
+
// `list()` is paginated from 0.6 on; walk the cursor so the
|
|
687
|
+
// service contract stays "every visible sandbox".
|
|
688
|
+
list: Effect.gen(function* () {
|
|
689
|
+
const refs: Array<SandboxRef> = []
|
|
690
|
+
let cursor: string | undefined
|
|
691
|
+
while (true) {
|
|
692
|
+
const page = yield* Effect.tryPromise({
|
|
693
|
+
try: () =>
|
|
694
|
+
cursor === undefined
|
|
695
|
+
? MsbSandbox.list()
|
|
696
|
+
: MsbSandbox.listWith((l) => l.cursor(cursor as string)),
|
|
697
|
+
catch: mapCreateError,
|
|
698
|
+
})
|
|
699
|
+
refs.push(
|
|
700
|
+
...Arr.map(page.sandboxes, (h): SandboxRef => ({ id: SandboxId(h.name), name: h.name })),
|
|
701
|
+
)
|
|
702
|
+
if (page.nextCursor === undefined) return refs
|
|
703
|
+
cursor = page.nextCursor
|
|
704
|
+
}
|
|
705
|
+
}),
|
|
699
706
|
|
|
700
707
|
destroy: destroyById,
|
|
701
708
|
|
|
@@ -703,10 +710,10 @@ const buildService = (config: MicrosandboxConfig): MicrosandboxSandboxService =>
|
|
|
703
710
|
// Microsandbox requires the source sandbox to be stopped before
|
|
704
711
|
// snapshotting. We must also flush root-FS writes BEFORE stop:
|
|
705
712
|
// SDK 0.4.6's stop path races the guest poweroff and `fs().write`
|
|
706
|
-
// data sitting in page cache never reaches the upper.ext4
|
|
707
|
-
//
|
|
708
|
-
//
|
|
709
|
-
//
|
|
713
|
+
// data sitting in page cache never reaches the upper.ext4, so we
|
|
714
|
+
// call `sync` via exec first. The upstream fix (microsandbox#746)
|
|
715
|
+
// should be in 0.6.x; drop this `flush` step once a live snapshot
|
|
716
|
+
// run confirms it.
|
|
710
717
|
//
|
|
711
718
|
// `SandboxHandle.snapshot(name)` registers the artifact in the
|
|
712
719
|
// DB index (visible to `Snapshot.list` / `msb snapshot list` /
|
|
File without changes
|