@codeworksh/harness 0.0.1-dev.20260825093030 → 0.0.1-dev.20260907170816

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.
@@ -0,0 +1,713 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Context, DateTime, Effect, Option, Path, Schema, SchemaGetter } from "effect";
3
+ import { uuidv7 } from "uuidv7";
4
+ import { Message } from "@codeworksh/aikit";
5
+ import TypeBoxSchema from "typebox/schema";
6
+ //#region src/schema.ts
7
+ const aikitValidators = /* @__PURE__ */ new WeakMap();
8
+ const aikitValidatorFor = (schema) => {
9
+ const cached = aikitValidators.get(schema);
10
+ if (cached !== void 0) return cached;
11
+ const compiled = TypeBoxSchema.Compile(schema);
12
+ aikitValidators.set(schema, compiled);
13
+ return compiled;
14
+ };
15
+ const aikitErrorPath = (error) => {
16
+ if (error.instancePath) return error.instancePath.substring(1);
17
+ const required = error.params?.requiredProperties;
18
+ return Array.isArray(required) ? required.join(", ") : "root";
19
+ };
20
+ const validateAikitSchema = (schema, value, label) => {
21
+ const validator = aikitValidatorFor(schema);
22
+ if (validator.Check(value)) return value;
23
+ const [, issues] = validator.Errors(value);
24
+ const details = issues.map((issue) => ` - ${aikitErrorPath(issue)}: ${issue.message}`).join("\n") || "unknown error";
25
+ throw new Error(`validation failed for ${label}\n${details}`);
26
+ };
27
+ /** Validate an aikit message without coercing or rewriting durable data. */
28
+ const validateAikitMessage = (value, label) => validateAikitSchema(Message.MessageSchema, value, label);
29
+ const validateAikitUserMessage = (value, label) => validateAikitSchema(Message.UserMessageSchema, value, label);
30
+ const validateAikitAssistantMessage = (value, label) => validateAikitSchema(Message.AssistantMessageSchema, value, label);
31
+ const validateAikitToolCallTerminalPart = (value, label) => validateAikitSchema(Message.ToolCallTerminalPartSchema, value, label);
32
+ const isAikitAssistantMessage = (value) => aikitValidatorFor(Message.AssistantMessageSchema).Check(value);
33
+ const isAikitToolCallTerminalPart = (value) => aikitValidatorFor(Message.ToolCallTerminalPartSchema).Check(value);
34
+ Schema.Int.check(Schema.isGreaterThan(0));
35
+ /**
36
+ * Integer greater than or equal to zero.
37
+ */
38
+ const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
39
+ /**
40
+ * Cost greater than or equal with finite value
41
+ */
42
+ const NonNegativeCost = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0));
43
+ Schema.String.pipe(Schema.brand("RelativePath"));
44
+ /**
45
+ * Absolute file path (e.g., `/home/user/projects/myapp/src/main.ts`).
46
+ */
47
+ const AbsolutePath$1 = Schema.String.pipe(Schema.brand("AbsolutePath"));
48
+ /**
49
+ * Optional public JSON field that can hold explicit `undefined` on the type
50
+ * side but encodes it as an omitted key, matching legacy `JSON.stringify`.
51
+ */
52
+ const optional = (schema) => Schema.optionalKey(schema).pipe(Schema.decodeTo(Schema.optional(schema), {
53
+ decode: SchemaGetter.passthrough({ strict: false }),
54
+ encode: SchemaGetter.transformOptional(Option.filter((value) => value !== void 0))
55
+ }));
56
+ /**
57
+ * Attach static methods to a schema object. Designed to be used with `.pipe()`:
58
+ *
59
+ * @example
60
+ * export const Foo = fooSchema.pipe(
61
+ * withStatics((schema) => ({
62
+ * zero: schema.make(0),
63
+ * from: Schema.decodeUnknownOption(schema),
64
+ * }))
65
+ * )
66
+ */
67
+ const withStatics = (methods) => (schema) => Object.assign(schema, methods(schema));
68
+ const DateTimeUtcFromMillis = Schema.Finite.pipe(Schema.decodeTo(Schema.DateTimeUtc, {
69
+ decode: SchemaGetter.transform((value) => DateTime.makeUnsafe(value)),
70
+ encode: SchemaGetter.transform((value) => DateTime.toEpochMillis(value))
71
+ }));
72
+ //#endregion
73
+ //#region src/util/posix.ts
74
+ /** Shared POSIX path implementation for sandbox and remote-runtime paths. */
75
+ const posix = Effect.runSync(Path.Path.pipe(Effect.provide(Path.layer)));
76
+ //#endregion
77
+ //#region src/sandbox/instance.ts
78
+ var instance_exports = /* @__PURE__ */ __exportAll({
79
+ ID: () => ID,
80
+ Kind: () => Kind,
81
+ Ownership: () => Ownership,
82
+ PersistedError: () => PersistedError,
83
+ SandboxInstance: () => instance_exports,
84
+ Status: () => Status,
85
+ Usage: () => Usage,
86
+ fromColumn: () => fromColumn,
87
+ fromField: () => fromField,
88
+ isMountable: () => isMountable,
89
+ mountable: () => mountable,
90
+ toColumn: () => toColumn,
91
+ toField: () => toField
92
+ });
93
+ /**
94
+ * A Sandbox instance is a **durable filesystem namespace** plus whatever compute
95
+ * acts on it — a device in Unix terms, which exists whether or not anything has
96
+ * it mounted. `Sandbox.Controller` is the only thing that creates,
97
+ * stops, or destroys one; this module is just its identity and state model.
98
+ *
99
+ * The application ID is deliberately separate from the driver's own resource
100
+ * locator: callers never parse a Vercel name or a Daytona ID, driver formats may
101
+ * change, and a destroyed resource must stay identifiable in Project/Session
102
+ * history. A missing resource is never recreated under an existing ID — a new
103
+ * resource is a new namespace and therefore a new ID.
104
+ */
105
+ const ID = Schema.String.pipe(Schema.brand("SandboxInstance.ID"), withStatics((schema) => ({
106
+ /**
107
+ * The host. Reserved, and never written to a column — see {@link toColumn}.
108
+ * It exists at runtime so nothing has to branch on "is this the host": it is
109
+ * what identity reads, what logs show, and what the transport cache keys on.
110
+ */
111
+ local: schema.make("local"),
112
+ /** A fresh identity for a namespace nothing has named yet. */
113
+ create: () => schema.make(`sbx_${uuidv7()}`)
114
+ })));
115
+ /**
116
+ * The storage boundary for namespace references, in one place.
117
+ *
118
+ * The host filesystem exists whether or not a row describes it, so it gets no
119
+ * row and `NULL` is the only spelling of it — the Unix analogue is exact, since
120
+ * `/` has no entry in the mount table you consult to find other mounts. That
121
+ * makes a session or directory writable before any namespace is registered (the
122
+ * foreign key is skipped on NULL), makes `SET sandbox_instance_id = NULL` a
123
+ * meaningful "revert to the host", and leaves the host impossible to tombstone,
124
+ * collect, or destroy, because there is nothing to point at.
125
+ *
126
+ * Two SQLite consequences ride on this and break correctness silently if missed:
127
+ * unique indexes treat NULLs as distinct, so every uniqueness constraint
128
+ * spanning a namespace column coalesces to `'local'`; and `= NULL` never
129
+ * matches, so namespace-scoped reads use `IS`.
130
+ */
131
+ const toColumn = (id) => id === ID.local ? null : id;
132
+ const fromColumn = (value) => value === null ? ID.local : ID.make(value);
133
+ /**
134
+ * The same mapping for row models, whose optional columns are `Option` rather
135
+ * than `null`. Kept beside {@link toColumn} so the boundary stays one place:
136
+ * `toColumn`/`fromColumn` for SQL parameters, these for `Model.FieldOption`.
137
+ */
138
+ const toField = (id) => id === ID.local ? Option.none() : Option.some(id);
139
+ const fromField = (value) => Option.getOrElse(value, () => ID.local);
140
+ /**
141
+ * Filesystem-class taxonomy, mirroring Unix: disk, tmpfs/procfs, NFS/CIFS.
142
+ * Stored on the row rather than derived from the registered driver, so reading
143
+ * an instance never depends on the registry — which matters most when a driver
144
+ * is *not* configured and you need to list or clean up its rows.
145
+ */
146
+ const Kind = Schema.Literals([
147
+ "local",
148
+ "virtual",
149
+ "remote"
150
+ ]);
151
+ const Ownership = Schema.Literals(["managed", "external"]);
152
+ /**
153
+ * Lifecycle state, in ZFS pool vocabulary. This is the **last observed** value,
154
+ * not live driver truth: Daytona auto-stops and auto-archives, Vercel sandboxes
155
+ * expire on their own timeout, so drift is normal. `stateObservedAt` carries the
156
+ * freshness and `Controller.refresh` updates it without waking anything.
157
+ *
158
+ * `removed` and `unavail` are deliberately distinct. `removed` means we deleted
159
+ * it; `unavail` means the driver claims it is gone. A "not found" is frequently a
160
+ * misclassification — wrong region, wrong API url, a revoked key answering 404,
161
+ * eventual consistency right after create — so it must never be recorded as if we
162
+ * had destroyed the resource ourselves.
163
+ */
164
+ const Status = Schema.Literals([
165
+ "provisioning",
166
+ "online",
167
+ "offline",
168
+ "suspending",
169
+ "removing",
170
+ "removed",
171
+ "unavail",
172
+ "faulted"
173
+ ]);
174
+ /**
175
+ * The statuses a `mount` may proceed from. This is the predicate every
176
+ * conditional write depends on, so it is enumerated once here rather than
177
+ * restated as prose at each call site.
178
+ *
179
+ * `offline` qualifies because mounting wakes.
180
+ * `faulted` qualifies because a fault is a *usability* condition, not an identity one — see {@link Status}.
181
+ *
182
+ * There is no `resuming`: it would exist to be observed by nothing, since
183
+ * mounting wakes, `offline` is already mountable, and waking is not destructive
184
+ * so it needs no claim. `suspending` and `removing` stay because they *are*
185
+ * compare-and-set claims, blocking a concurrent mount mid-destruction.
186
+ */
187
+ const mountable = /* @__PURE__ */ new Set([
188
+ "online",
189
+ "offline",
190
+ "faulted"
191
+ ]);
192
+ const isMountable = (status) => mountable.has(status);
193
+ /**
194
+ * Reference state, derived — never stored. `busy` carries its `umount` meaning:
195
+ * something holds this and destruction must not proceed unforced.
196
+ *
197
+ * `pinned` is the kernel sense of the word: never reclaimable. It short-circuits
198
+ * counting entirely for instances that cannot be stopped or destroyed (the local
199
+ * host), which is what keeps them out of any future collector by construction
200
+ * rather than by an ownership check happening to catch them.
201
+ */
202
+ const Usage = Schema.Literals([
203
+ "idle",
204
+ "busy",
205
+ "pinned"
206
+ ]);
207
+ /** Sanitized driver failure. The only error shape allowed to be persisted or logged. */
208
+ const PersistedError = Schema.Struct({
209
+ name: Schema.String,
210
+ message: Schema.String,
211
+ code: Schema.optional(Schema.String)
212
+ });
213
+ //#endregion
214
+ //#region src/sandbox/fs/filesystem.ts
215
+ var filesystem_exports = /* @__PURE__ */ __exportAll({
216
+ FileSystemError: () => FileSystemError,
217
+ OperationUnsupportedError: () => OperationUnsupportedError,
218
+ SandboxFileSystem: () => filesystem_exports,
219
+ Service: () => Service$1,
220
+ fromProvider: () => fromProvider,
221
+ isNotFoundError: () => isNotFoundError,
222
+ validateRmOptions: () => validateRmOptions,
223
+ withCwd: () => withCwd$1
224
+ });
225
+ /**
226
+ * The runtime filesystem contract, independent of any backend.
227
+ *
228
+ * Two surfaces, deliberately:
229
+ * - {@link Provider} is what a backend implements — plain promises that reject,
230
+ * matching every SDK we wrap.
231
+ * - {@link Interface} is what the harness consumes — Effect with a typed error
232
+ * channel and tracing spans. {@link fromProvider} bridges the two exactly
233
+ * once, so no consumer ever writes `Effect.tryPromise` against a filesystem.
234
+ *
235
+ * Implementations:
236
+ * - Local:
237
+ * implement it over a VFS (`./local`);
238
+ * local filesystem use OS primitives; hence have broader filesytem capabilities.
239
+ *
240
+ * - Remote:
241
+ * implement it over a provider (`./remote`).
242
+ * remote filesytems depends on the interface provided by the remote provider; hence can have limited filesytem capabilities.
243
+ *
244
+ * `isFile`/`isDirectory` are required booleans; size, mtime, and isSymbolicLink are omitted when the
245
+ * backend cannot report them — never fabricated.
246
+ *
247
+ * **Paths are POSIX, and the harness is Unix-only (for now!).** Every path uses `/`
248
+ * separators on both the host and inside a sandbox — Windows is not supported,
249
+ * so no translation layer exists. Consumers must use the shared Effect POSIX
250
+ * path implementation, never the platform default, or a host running the harness would
251
+ * impose its own flavour on a remote sandbox's paths. Relative paths resolve
252
+ * against the backend's configured `cwd`.
253
+ */
254
+ var OperationUnsupportedError = class extends Schema.TaggedError()("OperationUnsupportedError", {
255
+ operation: Schema.String,
256
+ message: Schema.String
257
+ }) {};
258
+ /** A backend operation failed. `cause` carries the provider's own rejection. */
259
+ var FileSystemError = class extends Schema.TaggedError()("SandboxFileSystemError", {
260
+ method: Schema.String,
261
+ path: Schema.String,
262
+ cause: Schema.optional(Schema.Defect())
263
+ }) {};
264
+ /** Whether a provider failure means the path itself is definitively absent. */
265
+ const isNotFoundError = (cause) => {
266
+ const code = cause?.code;
267
+ return code === "ENOENT" || code === "ENOTDIR";
268
+ };
269
+ /** The runtime filesystem service — the live {@link Interface} for the active sandbox. */
270
+ var Service$1 = class extends Context.Service()("@codeworksh/harness/sandbox/fs/filesystem/Service") {};
271
+ /**
272
+ * Reject `rm` options a provider does not implement, before any mutation. Only
273
+ * `recursive` and `force` are part of the contract; anything else is refused
274
+ * loudly rather than silently ignored.
275
+ */
276
+ const validateRmOptions = (options, operation = "rm") => Effect.suspend(() => {
277
+ for (const option of Object.keys(options ?? {})) {
278
+ if (option === "recursive" || option === "force") continue;
279
+ return Effect.fail(new OperationUnsupportedError({
280
+ operation,
281
+ message: `Unsupported rm option: ${option}`
282
+ }));
283
+ }
284
+ return Effect.void;
285
+ });
286
+ /**
287
+ * Lift a {@link Provider} into the runtime {@link Interface}: one place that
288
+ * converts rejections into {@link FileSystemError}, validates `rm` options,
289
+ * creates missing parents on write, and names a tracing span per operation.
290
+ */
291
+ const fromProvider = (provider) => {
292
+ const attempt = (method, path, run) => Effect.tryPromise({
293
+ try: run,
294
+ catch: (cause) => new FileSystemError({
295
+ method,
296
+ path,
297
+ cause
298
+ })
299
+ });
300
+ const writeCreatingParents = (path, content) => {
301
+ const write = attempt("writeFile", path, () => provider.writeFile(path, content));
302
+ const parent = posix.dirname(path);
303
+ return write.pipe(Effect.catch(() => attempt("mkdir", parent, () => provider.mkdir(parent, { recursive: true })).pipe(Effect.ignore, Effect.andThen(write))));
304
+ };
305
+ return {
306
+ readFile: Effect.fn("SandboxFileSystem.readFile")((path) => attempt("readFile", path, () => provider.readFile(path))),
307
+ readFileBuffer: Effect.fn("SandboxFileSystem.readFileBuffer")((path) => attempt("readFileBuffer", path, () => provider.readFileBuffer(path))),
308
+ writeFile: Effect.fn("SandboxFileSystem.writeFile")(writeCreatingParents),
309
+ stat: Effect.fn("SandboxFileSystem.stat")((path) => attempt("stat", path, () => provider.stat(path))),
310
+ ...provider.lstat === void 0 ? {} : { lstat: Effect.fn("SandboxFileSystem.lstat")((path) => attempt("lstat", path, () => provider.lstat(path))) },
311
+ readdir: Effect.fn("SandboxFileSystem.readdir")((path) => attempt("readdir", path, () => provider.readdir(path))),
312
+ exists: Effect.fn("SandboxFileSystem.exists")((path) => attempt("exists", path, () => provider.exists(path))),
313
+ mkdir: Effect.fn("SandboxFileSystem.mkdir")((path, options) => attempt("mkdir", path, () => provider.mkdir(path, options))),
314
+ rm: Effect.fn("SandboxFileSystem.rm")((path, options) => validateRmOptions(options).pipe(Effect.andThen(attempt("rm", path, () => provider.rm(path, options)))))
315
+ };
316
+ };
317
+ /**
318
+ * Bind a cwd-neutral filesystem to one mount's working directory.
319
+ *
320
+ * The counterpart of `Shell.withCwd`, and the reason relative paths mean the
321
+ * same thing to both: a shared transport stays rooted at the namespace root, so
322
+ * resolution happens here, per mount, rather than inside a VFS whose `chdir` is
323
+ * global state two mounts would fight over.
324
+ */
325
+ const withCwd$1 = (fs, cwd) => {
326
+ const at = (path) => posix.resolve(cwd, path);
327
+ return {
328
+ readFile: (path) => fs.readFile(at(path)),
329
+ readFileBuffer: (path) => fs.readFileBuffer(at(path)),
330
+ writeFile: (path, content) => fs.writeFile(at(path), content),
331
+ stat: (path) => fs.stat(at(path)),
332
+ readdir: (path) => fs.readdir(at(path)),
333
+ exists: (path) => fs.exists(at(path)),
334
+ mkdir: (path, options) => fs.mkdir(at(path), options),
335
+ rm: (path, options) => fs.rm(at(path), options),
336
+ ...fs.lstat === void 0 ? {} : { lstat: (path) => fs.lstat(at(path)) }
337
+ };
338
+ };
339
+ //#endregion
340
+ //#region src/sandbox/shell/shell.ts
341
+ /**
342
+ * The pluggable execution contract. Local sandboxes usually get a Shell through
343
+ * just-bash over the local `Local.Vfs`; remote sandboxes provide their own
344
+ * native Shell. Either way the rest of the harness depends only on this service
345
+ * tag.
346
+ */
347
+ var ShellError = class extends Schema.TaggedError()("ShellError", {
348
+ command: Schema.String,
349
+ cause: Schema.optional(Schema.Defect())
350
+ }) {};
351
+ /**
352
+ * Mount-local shell factories are attached out-of-band so the public Shell
353
+ * contract stays about command execution only.
354
+ *
355
+ * Most transports are safely shared and need only the cwd wrapper below.
356
+ * Stateful in-process interpreters use this hook to construct one interpreter
357
+ * per mount while retaining the underlying filesystem transport. This tag must
358
+ * remain on the outermost wrapper consumed by {@link withCwd}; wrapper helpers
359
+ * such as {@link fromExec} must propagate it when they return a new object.
360
+ */
361
+ const mountFactories = /* @__PURE__ */ new WeakMap();
362
+ const perMount = (transport, make) => {
363
+ mountFactories.set(transport, make);
364
+ return transport;
365
+ };
366
+ /**
367
+ * POSIX single-quote escaping: wrap in `'…'` and rewrite each embedded quote as
368
+ * `'\''`. Everything inside single quotes is literal to the shell, so this is
369
+ * safe for arbitrary bytes.
370
+ */
371
+ const quote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
372
+ /** Render an argument vector as one shell-safe command string. */
373
+ const quoteArgv = (argv) => argv.map(quote).join(" ");
374
+ /**
375
+ * Resolve a per-command cwd against the sandbox cwd. Passing a relative cwd
376
+ * straight to a host or remote process API would otherwise resolve it against
377
+ * that API's own default, which need not be the filesystem's configured cwd.
378
+ */
379
+ const resolveCwd = (base, cwd) => {
380
+ if (cwd === void 0 || posix.isAbsolute(cwd) || base === void 0) return cwd ?? base;
381
+ return posix.resolve(base, cwd);
382
+ };
383
+ /**
384
+ * Complete a string-only backend: `execArgv` quotes the vector and runs it
385
+ * through `exec`. Backends that spawn a real argument vector (Vercel) implement
386
+ * `execArgv` themselves instead, so the args never meet a shell parser at all.
387
+ *
388
+ * `cwd` rides the options rather than a `cd <dir> && …` prefix. Every backend
389
+ * we wrap takes a working directory natively, and the prefix form cannot tell a
390
+ * failed `cd` from a failed command — both arrive as one exit code.
391
+ */
392
+ const fromExec = (backend) => {
393
+ const wrapped = {
394
+ ...backend,
395
+ execArgv: (argv, options) => backend.exec(quoteArgv(argv), options)
396
+ };
397
+ const mountFactory = mountFactories.get(backend);
398
+ return mountFactory === void 0 ? wrapped : perMount(wrapped, (cwd) => fromExec(mountFactory(cwd)));
399
+ };
400
+ /**
401
+ * Bind a cwd-neutral backend to one mount's working directory.
402
+ *
403
+ * The transport is shared between mounts and must stay rooted at the namespace
404
+ * root, so the directory cannot live inside it: two mounts at different
405
+ * directories would otherwise see each other's. This wrapper is per mount, and
406
+ * an operation-level `cwd` still wins — resolved against the mount's, so a
407
+ * relative one means what it reads like.
408
+ */
409
+ const withCwd = (backend, cwd) => {
410
+ const mounted = mountFactories.get(backend)?.(cwd) ?? backend;
411
+ const at = (options) => {
412
+ const resolvedCwd = resolveCwd(cwd, options?.cwd);
413
+ return {
414
+ ...options,
415
+ ...resolvedCwd === void 0 ? {} : { cwd: resolvedCwd }
416
+ };
417
+ };
418
+ const stream = mounted.stream;
419
+ return {
420
+ exec: (command, options) => mounted.exec(command, at(options)),
421
+ execArgv: (argv, options) => mounted.execArgv(argv, at(options)),
422
+ ...stream === void 0 ? {} : { stream: (command, options) => stream(command, at(options)) }
423
+ };
424
+ };
425
+ /** Execution service — the live {@link ISandboxExe} for the active sandbox. */
426
+ var Shell$1 = class extends Context.Service()("@codeworksh/harness/sandbox/shell/shell") {};
427
+ //#endregion
428
+ //#region src/sandbox/io.ts
429
+ /**
430
+ * `SandboxIO` is a **mount**: a filesystem, a shell, and the identity and
431
+ * working directory they act on.
432
+ *
433
+ * It is the whole vocabulary a consumer needs. Project, Git, Copy, Location, and
434
+ * every tool ask for `SandboxIO.FileSystem`, `SandboxIO.Shell`, and
435
+ * `SandboxIO.Current` — never for a driver, an address, or a provider SDK — so
436
+ * a host directory, an in-memory VFS, and a remote microVM are interchangeable
437
+ * behind one contract.
438
+ *
439
+ * The mount neither creates nor destroys infrastructure. `SandboxInstance` is
440
+ * the durable namespace it acts on — a device, which exists whether or not
441
+ * anything has it mounted — and `Sandbox.Controller` is the only path that
442
+ * creates, stops, or destroys one.
443
+ */
444
+ /**
445
+ * The filesystem tag. Re-exported here so consumers import one namespace —
446
+ * Project, Git, Copy, and the runner ask for `SandboxIO.FileSystem`, never for
447
+ * the module that happens to define it. Code *inside* `sandbox/` keeps importing
448
+ * the tag directly, since `io.ts` is built on top of it.
449
+ */
450
+ const FileSystem$1 = Service$1;
451
+ /** The shell tag. Re-exported here so consumers import one namespace. */
452
+ const Shell = Shell$1;
453
+ /** Identity and working directory of the current mount. */
454
+ var Current = class extends Context.Service()("@codeworksh/harness/sandbox/io/Current") {};
455
+ /**
456
+ * Resolve a mount's cwd without consulting ambient process state.
457
+ *
458
+ * The default is supplied by the namespace adapter:
459
+ * provider metadata remotely, `/` for a virtual filesystem, and `process.cwd()` for the host adapter.
460
+ * An explicit absolute cwd replaces it; a relative cwd is resolved inside it. The
461
+ * result is therefore always the one concrete absolute path `Current` requires.
462
+ *
463
+ * A non-absolute default throws rather than failing typed. It is the one
464
+ * defect-level guard in this module, and deliberately so:
465
+ * Adapters reading a value from a provider call this inside their own error channel,
466
+ * where the throw becomes their typed failure (see `EnvDaytona.mountCwd`).
467
+ */
468
+ const resolveMountCwd = (defaultCwd, cwd) => {
469
+ if (!posix.isAbsolute(defaultCwd)) throw new TypeError(`Sandbox default cwd must be absolute: ${defaultCwd}`);
470
+ if (cwd === void 0) return posix.resolve(defaultCwd);
471
+ return posix.isAbsolute(cwd) ? posix.resolve(cwd) : posix.resolve(defaultCwd, cwd);
472
+ };
473
+ /** Open driver identity. Adding a driver never extends a union in core. */
474
+ const Name = Schema.String.check(Schema.isNonEmpty()).pipe(Schema.brand("SandboxDriver.Name"));
475
+ /** A path whose coordinate system is the mounted namespace. */
476
+ const AbsolutePath = Schema.String.check(Schema.isStartsWith("/")).pipe(Schema.brand("SandboxDriver.AbsolutePath"));
477
+ const RuntimeConfigBase = Schema.Struct({ defaultCwd: AbsolutePath });
478
+ const erase = (value) => ({
479
+ name: value.name,
480
+ kind: value.kind,
481
+ capabilities: value.capabilities,
482
+ createConfigCodec: value.createConfigCodec,
483
+ runtimeConfigCodec: value.runtimeConfigCodec,
484
+ create: (input) => value.create({
485
+ instanceId: input.instanceId,
486
+ config: input.config
487
+ }),
488
+ ...value.runtimeConfigFor === void 0 ? {} : { runtimeConfigFor: (input) => value.runtimeConfigFor({
489
+ providerResourceId: input.providerResourceId,
490
+ ...input.overrides === void 0 ? {} : { overrides: input.overrides }
491
+ }) },
492
+ attach: (input) => value.attach(input),
493
+ ...value.inspect === void 0 ? {} : { inspect: (input) => value.inspect(input) },
494
+ ...value.wake === void 0 ? {} : { wake: (input) => value.wake(input) },
495
+ ...value.stop === void 0 ? {} : { stop: (input) => value.stop(input) },
496
+ ...value.destroy === void 0 ? {} : { destroy: (input) => value.destroy(input) }
497
+ });
498
+ /** Define the default export of a loadable sandbox package. */
499
+ const defineModule = (value) => ({
500
+ ...value,
501
+ name: Name.make(value.name)
502
+ });
503
+ /** Construct a driver and its registry contribution. */
504
+ const driver = (value) => Object.assign(value, {
505
+ registered: erase(value),
506
+ apiVersion: 1,
507
+ source: "builtin"
508
+ });
509
+ /** Attach trusted origin metadata without changing the driver implementation. */
510
+ const withSource = (registration, source) => ({
511
+ ...registration,
512
+ source
513
+ });
514
+ //#endregion
515
+ //#region src/sandbox/errors.ts
516
+ /**
517
+ * Lifecycle errors are confined to the control plane. Once a mount succeeds,
518
+ * consumers continue to see only FileSystemError and ShellError.
519
+ */
520
+ var SandboxNotFoundError = class extends Schema.TaggedError()("SandboxNotFoundError", { id: ID }) {};
521
+ var SandboxDriverNotRegisteredError = class extends Schema.TaggedError()("SandboxDriverNotRegisteredError", {
522
+ driver: Schema.String,
523
+ registered: Schema.optional(Schema.Array(Schema.String))
524
+ }) {};
525
+ var SandboxDriverRegistrationError = class extends Schema.TaggedError()("SandboxDriverRegistrationError", {
526
+ driver: Schema.String,
527
+ reason: Schema.String
528
+ }) {};
529
+ const SandboxDriverLoadPhase = Schema.Literals([
530
+ "resolve",
531
+ "import",
532
+ "module",
533
+ "api-version",
534
+ "options",
535
+ "factory",
536
+ "registration"
537
+ ]);
538
+ var SandboxDriverLoadError = class extends Schema.TaggedError()("SandboxDriverLoadError", {
539
+ specifier: Schema.String,
540
+ phase: SandboxDriverLoadPhase,
541
+ driver: Schema.optional(Schema.String),
542
+ reason: Schema.String
543
+ }) {};
544
+ var SandboxBusyError = class extends Schema.TaggedError()("SandboxBusyError", {
545
+ id: ID,
546
+ refCount: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
547
+ }) {};
548
+ var SandboxMustBeStoppedError = class extends Schema.TaggedError()("SandboxMustBeStoppedError", {
549
+ id: ID,
550
+ status: Status
551
+ }) {};
552
+ var SandboxRemovedError = class extends Schema.TaggedError()("SandboxRemovedError", {
553
+ id: ID,
554
+ removedAt: Schema.optional(Schema.DateTimeUtc)
555
+ }) {};
556
+ var SandboxUnavailError = class extends Schema.TaggedError()("SandboxUnavailError", {
557
+ id: ID,
558
+ reason: Schema.String
559
+ }) {};
560
+ var SandboxUnsupportedError = class extends Schema.TaggedError()("SandboxUnsupportedError", {
561
+ id: Schema.optional(ID),
562
+ driver: Schema.String,
563
+ operation: Schema.String
564
+ }) {};
565
+ var SandboxTransitionConflictError = class extends Schema.TaggedError()("SandboxTransitionConflictError", {
566
+ id: ID,
567
+ expected: Schema.Array(Status),
568
+ actual: Status
569
+ }) {};
570
+ /**
571
+ * A lifecycle failure safe to serialize, persist, or log.
572
+ *
573
+ * The raw SDK defect is deliberately absent from the schema. It is retained on
574
+ * a non-enumerable symbol by {@link providerError}, so Effect's default
575
+ * formatting and JSON serialization cannot expose credentials nested in it.
576
+ */
577
+ var SandboxProviderError = class extends Schema.TaggedError()("SandboxProviderError", {
578
+ driver: Schema.String,
579
+ operation: Schema.String,
580
+ sanitized: PersistedError
581
+ }) {};
582
+ const rawCause = Symbol("@codework/sandbox/provider/error/raw/cause");
583
+ const missingResource = Symbol("@codework/sandbox/provider/error/missing/resource");
584
+ const providerErrorCause = (error) => error[rawCause];
585
+ const providerErrorIsNotFound = (error) => error[missingResource] === true;
586
+ const REDACTED = "<redacted>";
587
+ /**
588
+ * Conservative text redaction shared by every driver sanitizer.
589
+ *
590
+ * Disclaimer: This can really leak. Its never safe
591
+ *
592
+ * Configured secrets are removed exactly. Common authorization/header and URL
593
+ * query shapes are masked as a second line of defence for values the caller did
594
+ * not explicitly seed.
595
+ *
596
+ * Note: add support for diff regex as needed.
597
+ */
598
+ const makeRedactor = (secrets = []) => {
599
+ const configured = [...secrets].filter((secret) => secret.length > 0).sort((a, b) => b.length - a.length);
600
+ return (value) => {
601
+ let redacted = value;
602
+ for (const secret of configured) redacted = redacted.replaceAll(secret, REDACTED);
603
+ return redacted.replace(/\b(authorization\s*:\s*(?:bearer|basic)\s+)[^\s,;]+/gi, `$1${REDACTED}`).replace(/\b((?:api[-_]?key|token|secret|password)\s*[=:]\s*)[^\s,;&]+/gi, `$1${REDACTED}`).replace(/([?&](?:access_token|api_key|token|secret|password)=)[^&#\s]+/gi, `$1${REDACTED}`).replace(/\b(?:gh[opsu]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, REDACTED);
604
+ };
605
+ };
606
+ const errorCode = (cause) => {
607
+ if (typeof cause !== "object" || cause === null || !("code" in cause)) return void 0;
608
+ const code = cause.code;
609
+ return typeof code === "string" || typeof code === "number" ? String(code) : void 0;
610
+ };
611
+ const errorName = (cause) => {
612
+ if (typeof cause !== "object" || cause === null || !("name" in cause) || typeof cause.name !== "string") return "Error";
613
+ return cause.name;
614
+ };
615
+ const errorMessage = (cause) => {
616
+ if (typeof cause === "object" && cause !== null && "message" in cause && typeof cause.message === "string") return cause.message;
617
+ return String(cause);
618
+ };
619
+ const sanitizeError = (cause, redact = makeRedactor()) => {
620
+ const code = errorCode(cause);
621
+ return {
622
+ name: redact(errorName(cause)),
623
+ message: redact(errorMessage(cause)),
624
+ ...code === void 0 ? {} : { code: redact(code) }
625
+ };
626
+ };
627
+ const providerError = (input) => {
628
+ const redact = input.redact ?? makeRedactor();
629
+ const error = new SandboxProviderError({
630
+ driver: input.driver,
631
+ operation: input.operation,
632
+ sanitized: (input.sanitize ?? sanitizeError)(input.cause, redact)
633
+ });
634
+ Object.defineProperty(error, rawCause, {
635
+ configurable: false,
636
+ enumerable: false,
637
+ value: input.cause,
638
+ writable: false
639
+ });
640
+ Object.defineProperty(error, missingResource, {
641
+ configurable: false,
642
+ enumerable: false,
643
+ value: input.notFound === true,
644
+ writable: false
645
+ });
646
+ return error;
647
+ };
648
+ //#endregion
649
+ //#region src/sandbox/public/driver.ts
650
+ var driver_exports = /* @__PURE__ */ __exportAll({
651
+ AbsolutePath: () => AbsolutePath,
652
+ Name: () => Name,
653
+ RuntimeConfigBase: () => RuntimeConfigBase,
654
+ SandboxDriver: () => driver_exports,
655
+ apiVersion: () => 1,
656
+ driver: () => driver,
657
+ module: () => defineModule
658
+ });
659
+ //#endregion
660
+ //#region src/sandbox/public/io.ts
661
+ var io_exports = /* @__PURE__ */ __exportAll({
662
+ Current: () => Current,
663
+ FileSystem: () => FileSystem$1,
664
+ SandboxIO: () => io_exports,
665
+ Shell: () => Shell
666
+ });
667
+ //#endregion
668
+ //#region src/sandbox/error.ts
669
+ var error_exports = /* @__PURE__ */ __exportAll({
670
+ SandboxProviderError: () => SandboxProviderError,
671
+ makeRedactor: () => makeRedactor,
672
+ providerError: () => providerError,
673
+ providerErrorCause: () => providerErrorCause,
674
+ providerErrorIsNotFound: () => providerErrorIsNotFound,
675
+ sanitizeError: () => sanitizeError
676
+ });
677
+ //#endregion
678
+ //#region src/sandbox/resource.ts
679
+ var resource_exports = /* @__PURE__ */ __exportAll({
680
+ SandboxResource: () => resource_exports,
681
+ Service: () => Service
682
+ });
683
+ /**
684
+ * The driver's own locator for the attached resource — a Vercel sandbox name, a
685
+ * Daytona sandbox id.
686
+ *
687
+ * Deliberately *not* on `SandboxIO.Current`: consumers never parse one,
688
+ * and keeps it separate from the application id precisely so a driver's format can
689
+ * change without touching identity.
690
+ *
691
+ * It exists for the control plane, which records it as `provider_resource_id`,
692
+ * and for tests that reattach to the same resource.
693
+ *
694
+ * One tag for every driver rather than one per driver. A mount has exactly one
695
+ * driver, so there is nothing to disambiguate, and the control plane has to read
696
+ * the locator without knowing which driver produced it — keeps the
697
+ * driver name open, so anything keyed on a closed `"vercel" | "daytona"` set is
698
+ * a bug waiting for the third driver.
699
+ */
700
+ var Service = class extends Context.Service()("@codeworksh/harness/sandbox/resource/Service") {};
701
+ //#endregion
702
+ //#region src/sandbox/public/shell.ts
703
+ var shell_exports = /* @__PURE__ */ __exportAll({
704
+ Shell: () => Shell$1,
705
+ ShellError: () => ShellError,
706
+ fromExec: () => fromExec,
707
+ quote: () => quote,
708
+ quoteArgv: () => quoteArgv
709
+ });
710
+ //#endregion
711
+ export { isMountable as $, FileSystem$1 as A, withCwd as B, Name as C, erase as D, driver as E, fromExec as F, withCwd$1 as G, filesystem_exports as H, perMount as I, Ownership as J, ID as K, quote as L, resolveMountCwd as M, Shell$1 as N, withSource as O, ShellError as P, instance_exports as Q, quoteArgv as R, AbsolutePath as S, defineModule as T, fromProvider as U, Service$1 as V, isNotFoundError as W, Status as X, PersistedError as Y, fromField as Z, SandboxUnsupportedError as _, io_exports as a, NonNegativeCost as at, providerErrorIsNotFound as b, SandboxDriverLoadError as c, isAikitToolCallTerminalPart as ct, SandboxMustBeStoppedError as d, validateAikitMessage as dt, toColumn as et, SandboxNotFoundError as f, validateAikitToolCallTerminalPart as ft, SandboxUnavailError as g, SandboxTransitionConflictError as h, error_exports as i, DateTimeUtcFromMillis as it, Shell as j, Current as k, SandboxDriverNotRegisteredError as l, optional as lt, SandboxRemovedError as m, withStatics as mt, Service as n, posix as nt, driver_exports as o, NonNegativeInt as ot, SandboxProviderError as p, validateAikitUserMessage as pt, Kind as q, resource_exports as r, AbsolutePath$1 as rt, SandboxBusyError as s, isAikitAssistantMessage as st, shell_exports as t, toField as tt, SandboxDriverRegistrationError as u, validateAikitAssistantMessage as ut, makeRedactor as v, RuntimeConfigBase as w, sanitizeError as x, providerError as y, resolveCwd as z };
712
+
713
+ //# sourceMappingURL=sandbox-QCmZ3UhD.mjs.map