@ekairos/sandbox 1.22.34-beta.development.0 → 1.22.35

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.
Files changed (70) hide show
  1. package/README.md +59 -452
  2. package/dist/action-steps.d.ts +156 -0
  3. package/dist/action-steps.d.ts.map +1 -0
  4. package/dist/action-steps.js +153 -0
  5. package/dist/action-steps.js.map +1 -0
  6. package/dist/actions.d.ts +263 -0
  7. package/dist/actions.d.ts.map +1 -0
  8. package/dist/actions.js +208 -0
  9. package/dist/actions.js.map +1 -0
  10. package/dist/app.js +1 -1
  11. package/dist/app.js.map +1 -1
  12. package/dist/contract.d.ts +86 -0
  13. package/dist/contract.d.ts.map +1 -0
  14. package/dist/contract.js +83 -0
  15. package/dist/contract.js.map +1 -0
  16. package/dist/domain.d.ts +2 -0
  17. package/dist/domain.d.ts.map +1 -0
  18. package/dist/domain.js +2 -0
  19. package/dist/domain.js.map +1 -0
  20. package/dist/index.d.ts +11 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +7 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/providers/daytona.d.ts +14 -0
  25. package/dist/providers/daytona.d.ts.map +1 -0
  26. package/dist/providers/daytona.js +153 -0
  27. package/dist/providers/daytona.js.map +1 -0
  28. package/dist/providers/provider.d.ts +3 -0
  29. package/dist/providers/provider.d.ts.map +1 -0
  30. package/dist/providers/provider.js +18 -0
  31. package/dist/providers/provider.js.map +1 -0
  32. package/dist/providers/sprites.d.ts +39 -0
  33. package/dist/providers/sprites.d.ts.map +1 -0
  34. package/dist/providers/sprites.js +234 -0
  35. package/dist/providers/sprites.js.map +1 -0
  36. package/dist/providers/types.d.ts +15 -0
  37. package/dist/providers/types.d.ts.map +1 -0
  38. package/dist/providers/types.js +9 -0
  39. package/dist/providers/types.js.map +1 -0
  40. package/dist/providers/vercel.d.ts +26 -0
  41. package/dist/providers/vercel.d.ts.map +1 -0
  42. package/dist/providers/vercel.js +182 -0
  43. package/dist/providers/vercel.js.map +1 -0
  44. package/dist/public.d.ts +56 -0
  45. package/dist/public.d.ts.map +1 -0
  46. package/dist/public.js +37 -0
  47. package/dist/public.js.map +1 -0
  48. package/dist/runtime.d.ts +4 -0
  49. package/dist/runtime.d.ts.map +1 -1
  50. package/dist/runtime.js +7 -1
  51. package/dist/runtime.js.map +1 -1
  52. package/dist/sandbox.d.ts +76 -0
  53. package/dist/sandbox.d.ts.map +1 -0
  54. package/dist/sandbox.js +154 -0
  55. package/dist/sandbox.js.map +1 -0
  56. package/dist/schema.d.ts +18 -2
  57. package/dist/schema.d.ts.map +1 -1
  58. package/dist/schema.js +43 -15
  59. package/dist/schema.js.map +1 -1
  60. package/dist/service.d.ts +98 -43
  61. package/dist/service.d.ts.map +1 -1
  62. package/dist/service.js +811 -543
  63. package/dist/service.js.map +1 -1
  64. package/dist/types.d.ts +33 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/vercel-options.d.ts +21 -0
  67. package/dist/vercel-options.d.ts.map +1 -0
  68. package/dist/vercel-options.js +149 -0
  69. package/dist/vercel-options.js.map +1 -0
  70. package/package.json +43 -7
package/README.md CHANGED
@@ -1,492 +1,99 @@
1
1
  # @ekairos/sandbox
2
2
 
3
- Provider-agnostic helpers to provision and manage external sandboxes with durable IDs stored in InstantDB.
4
- This package is **independent** (no workflow runtime dependency). Other packages (e.g. `@ekairos/dataset`, `@ekairos/structure`)
5
- may depend on it, but `@ekairos/sandbox` does not depend on any workflow framework.
3
+ Provider-agnostic sandbox service with durable sandbox ids stored in InstantDB.
6
4
 
7
- ---
5
+ ## What it does
8
6
 
9
- ## Why this package exists
7
+ - creates sandboxes and persists them in `sandbox_sandboxes`
8
+ - reconnects by durable `sandboxId`
9
+ - runs commands
10
+ - reads and writes files
11
+ - supports multiple providers behind one API
10
12
 
11
- We need a stable, provider-agnostic interface to:
12
- - Create and reconnect sandboxes by a durable `sandboxId`.
13
- - Run commands and read/write files in a consistent way.
14
- - Persist sandbox metadata in InstantDB for continuity across runs.
15
- - Support multiple providers (Vercel, Daytona, etc.) without changing callers.
13
+ ## Main APIs
16
14
 
17
- ---
15
+ - `sandboxDomain`
16
+ - `SandboxService`
17
+ - `createVercelSandbox(...)`
18
+ - `runCommandInSandbox(...)`
18
19
 
19
- ## Installation (pnpm)
20
-
21
- ```bash
22
- pnpm add @ekairos/sandbox
23
- ```
24
-
25
- ---
26
-
27
- ## Environment variables
28
-
29
- ### InstantDB (admin)
30
- You must provide an InstantDB **admin** database client so sandbox actions can persist and query sandbox records.
31
-
32
- ### Provider selection
33
-
34
- By default, provider is `sprites` unless overridden.
35
-
36
- You can set:
37
- - `SANDBOX_PROVIDER=sprites` or `SANDBOX_PROVIDER=daytona` or `SANDBOX_PROVIDER=vercel`
38
- - or pass `provider` in `SandboxConfig`
39
-
40
- ### Sprites.dev
41
-
42
- - `SPRITES_API_TOKEN` (or `SPRITE_TOKEN`) - required
43
- - Optional:
44
- - `SPRITES_API_BASE_URL` / `SPRITES_API_URL` (default: `https://api.sprites.dev`)
45
-
46
- ### Daytona (current)
47
-
48
- `@daytonaio/sdk` supports these env vars:
49
-
50
- - `DAYTONA_API_URL` (required) - Daytona API base URL
51
- - `DAYTONA_API_KEY` (required if not using JWT)
52
- - `DAYTONA_JWT_TOKEN` + `DAYTONA_ORGANIZATION_ID` (optional auth mode)
53
- - `DAYTONA_TARGET` (optional)
54
- - `SANDBOX_DAYTONA_EPHEMERAL` (optional) - default ephemeral for Daytona sandboxes (true unless set to 0/false)
55
-
56
- ### Vercel
57
-
58
- - `SANDBOX_VERCEL_TEAM_ID`
59
- - `SANDBOX_VERCEL_PROJECT_ID`
60
- - `SANDBOX_VERCEL_TOKEN`
61
-
62
- ---
63
-
64
- ## Data model (InstantDB)
65
-
66
- ### `sandboxDomain`
67
- Defines a single entity:
68
-
69
- - `sandbox_sandboxes`
70
-
71
- Fields (high-level):
72
- - `externalSandboxId` (string, indexed): provider sandbox id (Vercel `sandbox.sandboxId`, Daytona `sandbox.id`)
73
- - `provider` (string, indexed): e.g. `"vercel"`, `"daytona"`
74
- - `sandboxUrl` (string, optional): optional URL metadata
75
- - `status` (string, indexed): `"creating" | "active" | "shutdown" | "error" | ...`
76
- - `timeout` (number, optional): milliseconds
77
- - `runtime` (string, optional): `"node22" | "python3" | ...`
78
- - `vcpus` (number, optional)
79
- - `ports` (json, optional): array of ports
80
- - `purpose` (string, optional, indexed)
81
- - `params` (json, optional)
82
- - timestamps: `createdAt`, `updatedAt`, `shutdownAt`
83
-
84
- ---
85
-
86
- ## ID semantics (important)
87
-
88
- There are two ids involved:
89
-
90
- ### 1) `sandboxId` (internal, durable handle)
91
- - The **InstantDB record id**: `sandbox_sandboxes[sandboxId]`
92
- - This is the id you store in durable state and pass around.
93
- - All sandbox actions and helpers take this `sandboxId`.
94
-
95
- ### 2) `externalSandboxId` (provider id)
96
- - The id returned by the provider SDK (Vercel `sandbox.sandboxId`, Daytona `sandbox.id`)
97
- - Stored on the record as `externalSandboxId`
98
- - Used internally to reconnect via the provider SDK
99
-
100
- ---
101
-
102
- ## Schema integration
103
-
104
- In your app schema, include `sandboxDomain` along with other domains, then initialize Instant with the composed schema.
105
-
106
- ```ts
107
- import { domain } from "@ekairos/domain";
108
- import { sandboxDomain } from "@ekairos/sandbox";
109
- // import { storyDomain } from "@ekairos/story" (if you use stories)
110
- // import { datasetDomain } from "@ekairos/dataset" (if you use dataset)
111
-
112
- export const appDomain = domain("app")
113
- .includes(sandboxDomain)
114
- // .includes(storyDomain)
115
- // .includes(datasetDomain)
116
- .schema({
117
- entities: {},
118
- links: {},
119
- rooms: {},
120
- });
121
-
122
- export const schema = appDomain.toInstantSchema();
123
- ```
124
-
125
- ---
126
-
127
- ## Exports
20
+ ## Providers
128
21
 
129
- - `sandboxDomain` (schema domain + actions factory)
130
- - `SandboxService` (high-level service)
131
- - `runCommandInSandbox` (low-level helper for Vercel)
132
- - Types: `SandboxConfig`, `SandboxRecord`, `ServiceResult<T>`, `CommandResult`
22
+ - `vercel`
23
+ - `daytona`
24
+ - `sprites`
133
25
 
134
- ---
26
+ Provider selection:
135
27
 
136
- ## Quickstart
28
+ 1. `config.provider`
29
+ 2. `SANDBOX_PROVIDER`
30
+ 3. default provider
137
31
 
138
- ### 1) Instantiate the service
32
+ ## Quick example
139
33
 
140
34
  ```ts
141
- import { init } from "@instantdb/admin";
142
- import { sandboxDomain } from "@ekairos/sandbox";
35
+ const service = new SandboxService(db);
143
36
 
144
- const db = init({
145
- appId: process.env.NEXT_PUBLIC_INSTANT_APP_ID,
146
- adminToken: process.env.INSTANT_APP_ADMIN_TOKEN,
147
- schema: {}, // your composed schema that includes sandboxDomain
148
- });
149
-
150
- const sandboxes = sandboxDomain(db);
151
- ```
152
-
153
- ### 2) Create a persisted sandbox
154
-
155
- ```ts
156
- const created = await sandboxes.createSandbox({
157
- provider: "daytona", // or set SANDBOX_PROVIDER=daytona
37
+ const created = await service.createSandbox({
38
+ provider: "vercel",
158
39
  runtime: "node22",
159
- timeoutMs: 10 * 60 * 1000,
160
- resources: { vcpus: 2 },
161
- purpose: "dataset-file-parse",
162
- params: { datasetId: "..." },
163
40
  });
164
41
 
165
42
  if (!created.ok) throw new Error(created.error);
166
- const { sandboxId } = created.data;
167
- ```
168
-
169
- ### 3) Run commands by `sandboxId`
170
-
171
- ```ts
172
- const sandbox = sandboxes.getSandbox(sandboxId);
173
- const res = await sandbox.runCommand("node", ["-e", "console.log('hello')"]);
174
- if (!res.ok) throw new Error(res.error);
175
- console.log(res.data.exitCode, res.data.output);
176
- ```
177
-
178
- ### 4) Reconnect (when you need the runtime object)
179
-
180
- ```ts
181
- const sandbox = sandboxes.getSandbox(sandboxId);
182
- const rec = await sandbox.reconnect();
183
- if (!rec.ok) throw new Error(rec.error);
184
-
185
- const { sandbox: providerSandbox } = rec.data;
186
- // Use provider-specific SDK features on providerSandbox
187
- ```
188
-
189
- ### 5) Stop (optional)
190
-
191
- ```ts
192
- const sandbox = sandboxes.getSandbox(sandboxId);
193
- await sandbox.stop();
194
- ```
195
-
196
- ---
197
-
198
- ## Provider selection rules
199
-
200
- Precedence:
201
- 1) `SandboxConfig.provider`
202
- 2) `SANDBOX_PROVIDER`
203
- 3) default: `sprites`
204
-
205
- ---
206
-
207
- ## Daytona provider details
208
-
209
- ### Config mapping
210
-
211
- `SandboxConfig` accepts Daytona-specific options via `config.daytona`:
212
43
 
213
- ```ts
214
- {
215
- provider: "daytona",
216
- runtime: "node22", // used to infer language when daytona.language not set
217
- daytona: {
218
- language: "typescript" | "javascript" | "python",
219
- snapshot: "snapshot-id",
220
- image: "debian:12" | "...",
221
- envVars: { KEY: "value" },
222
- labels: { "app": "ekairos" },
223
- public: false,
224
- ephemeral: true,
225
- autoStopIntervalMin: 30,
226
- autoArchiveIntervalMin: 60,
227
- user: "daytona",
228
- volumes: [{ volumeId: "vol-123", mountPath: "/home/daytona/volume" }],
229
- }
230
- }
44
+ const run = await service.runCommand(created.data.sandboxId, "node", ["-v"]);
231
45
  ```
232
46
 
233
- ### Command execution
234
-
235
- For Daytona, `runCommand` uses `sandbox.process.executeCommand(...)` and returns:
236
-
237
- - `exitCode`
238
- - `output` (stdout)
239
- - `error` (stderr if available)
240
-
241
- ### File IO
242
-
243
- - `writeFiles` -> `sandbox.fs.uploadFiles(...)`
244
- - `readFile` -> `sandbox.fs.downloadFile(...)`
47
+ ## Vercel cost profiles
245
48
 
246
- ---
49
+ Vercel Sandbox is metered by active CPU, provisioned memory, creations, data transfer, and storage.
50
+ `SandboxService` keeps the default Vercel profile small:
247
51
 
248
- ## Volumes (Daytona)
52
+ - `ephemeral` profile: 1 vCPU, 5 minute timeout, no persistence.
53
+ - `coding-agent` profile: 2 vCPUs, 20 minute timeout, persistent filesystem, 7 day snapshot expiration.
54
+ - `stopSandbox` deletes ephemeral Vercel sandboxes, but only stops persistent coding-agent sandboxes.
249
55
 
250
- Volumes are persistent FUSE mounts that can be attached to multiple sandboxes.
251
- They are ideal for caching datasets or large artifacts across runs.
252
-
253
- ### Create/get a volume
56
+ The `coding-agent` profile is selected automatically when `purpose` mentions `codex` or `agent`, or explicitly:
254
57
 
255
58
  ```ts
256
- import { Daytona } from "@daytonaio/sdk";
257
-
258
- const daytona = new Daytona();
259
- const volume = await daytona.volume.get("ekairos-ds-123", true);
260
- ```
261
-
262
- ### Mount a volume when creating a sandbox
263
-
264
- ```ts
265
- const created = await sandboxes.createSandbox({
266
- provider: "daytona",
267
- daytona: {
268
- // Either volumeId or volumeName can be provided.
269
- volumes: [{ volumeId: volume.id, mountPath: "/home/daytona/.ekairos" }],
270
- // volumes: [{ volumeName: "ekairos-ds-123", mountPath: "/home/daytona/.ekairos" }],
271
- },
272
- });
273
- ```
274
-
275
- ### Use the volume in the sandbox
276
-
277
- Once mounted, read/write like any other directory.
278
- Files written to a volume persist even after the sandbox is removed.
279
-
280
- ### Limitations
281
-
282
- - Volumes are FUSE-based and **slower** than the local sandbox filesystem.
283
- - Volumes are **not** block storage and are not suitable for databases.
284
- - For heavy processing, consider copying from the volume to local FS first.
285
-
286
- ---
287
-
288
- ## Volumes vs InstantDB files (how they relate)
289
-
290
- InstantDB storage (`$files`) is **durable object storage** (good for canonical datasets,
291
- outputs, and sharing data across services). Daytona volumes are **durable shared file mounts**
292
- optimized for **fast re-use inside sandboxes**.
293
-
294
- Think of it like this:
295
-
296
- - **InstantDB `$files`** is the source of truth and can be accessed by any service.
297
- - **Daytona volumes** are a performance layer to avoid re-downloading the same files into sandboxes.
298
-
299
- ### Recommended pattern
300
-
301
- 1) **Persist canonical files** in InstantDB `$files`.
302
- 2) **On sandbox start**, check a manifest in the volume:
303
- - If present and hashes match, **use volume data**.
304
- - If not, **download from InstantDB** and refresh the volume + manifest.
305
- 3) **Write output back to InstantDB** when you need durable sharing or indexing.
306
-
307
- ### Example manifest layout
308
-
309
- ```
310
- /home/daytona/.ekairos/datasets/{datasetId}/
311
- manifest.json
312
- raw/
313
- normalized/
314
- cache/
315
- ```
316
-
317
- ### Why both are needed
318
-
319
- - InstantDB guarantees durability, queryability, and cross-service access.
320
- - Volumes reduce cold-start and repeated downloads for sandbox workloads.
321
-
322
- ---
323
-
324
- ## Ephemeral sandboxes (Daytona)
325
-
326
- Set `daytona.ephemeral = true` to auto-delete a sandbox after it stops.
327
- Pair this with `autoStopIntervalMin` to avoid quota exhaustion from idle sandboxes.
328
-
329
- ```ts
330
- const created = await sandboxes.createSandbox({
331
- provider: "daytona",
332
- daytona: {
333
- ephemeral: true,
334
- autoStopIntervalMin: 5,
59
+ const created = await service.createSandbox({
60
+ provider: "vercel",
61
+ purpose: "codex-reactor",
62
+ runtime: "node22",
63
+ ports: [3000],
64
+ vercel: {
65
+ profile: "coding-agent",
66
+ name: "ekairos-codex-workspace",
67
+ reuse: true,
68
+ persistent: true,
69
+ scope: "ekairos-dev",
70
+ cwd: "C:/ek",
335
71
  },
336
72
  });
337
73
  ```
338
74
 
339
- ---
340
-
341
- ## Behavioral notes
342
-
343
- ### `createSandbox(config)`
344
- - Creates an InstantDB record `sandbox_sandboxes[sandboxId]` with status `"creating"`.
345
- - Provisions a provider sandbox (provider-specific).
346
- - Updates the record to status `"active"` and stores `externalSandboxId`.
347
-
348
- ### `reconnectToSandbox(sandboxId)`
349
- - Loads the record by internal `sandboxId`.
350
- - Validates:
351
- - record exists
352
- - `externalSandboxId` exists
353
- - Reconnects using the provider SDK/client (provider-specific).
354
- - Returns `ok: false` if sandbox is not found/not running.
355
- - If reconnection fails and the record was `"active"`, it may mark the record as `"shutdown"`.
356
-
357
- ### `runCommand(sandboxId, command, args?)`
358
- - Durable-friendly command execution.
359
- - Attempts to reconnect; if unavailable it may recreate the sandbox from the stored record configuration and retry.
360
- - Returns a capped output payload suitable for storing in logs.
361
-
362
- ### `stopSandbox(sandboxId)`
363
- - Attempts to reconnect and stop the provider sandbox.
364
- - Marks the record as `"shutdown"` regardless (the provider sandbox may already be gone).
365
-
366
- ---
367
-
368
- ## Providers
369
-
370
- ### Daytona (current)
371
-
372
- Environment variables:
373
- - `DAYTONA_API_URL`
374
- - `DAYTONA_API_KEY` or `DAYTONA_JWT_TOKEN + DAYTONA_ORGANIZATION_ID`
375
- - `DAYTONA_TARGET` (optional)
376
-
377
- Behavior notes:
378
- - `externalSandboxId` maps to Daytona `sandbox.id`.
379
- - Reconnect uses `daytona.get(id)` and starts if not running.
380
- - File IO uses `sandbox.fs`.
381
- - Command execution uses `sandbox.process.executeCommand`.
382
-
383
- ### Local Daytona OSS (Docker Desktop / Windows)
384
-
385
- We keep the Daytona OSS repo **outside** this workspace to avoid nested repos.
386
- Use the helper script in `./scripts/daytona-local.ps1` to clone and run the official
387
- Docker Compose stack.
388
-
389
- ```powershell
390
- # clone once (pin a ref for repeatability)
391
- powershell -ExecutionPolicy Bypass -File .\scripts\daytona-local.ps1 init -Ref <tag-or-commit>
392
-
393
- # start / stop
394
- powershell -ExecutionPolicy Bypass -File .\scripts\daytona-local.ps1 up
395
- powershell -ExecutionPolicy Bypass -File .\scripts\daytona-local.ps1 down
396
- ```
397
-
398
- Defaults and overrides:
399
- - Default clone path: `%USERPROFILE%\.ekairos\daytona-oss`
400
- - Override with `DAYTONA_OSS_HOME`
401
- - Pin a specific version with `DAYTONA_OSS_REF`
402
-
403
- Environment variables for local usage:
404
-
405
- ```bash
406
- SANDBOX_PROVIDER=daytona
407
- DAYTONA_API_URL=http://localhost:3000/api
408
- DAYTONA_API_KEY=... # create in the local Daytona dashboard (http://localhost:3000)
409
- ```
410
-
411
- Optional: if you need proxy/preview URLs, Windows needs wildcard DNS for `*.proxy.localhost`.
412
- You can run Daytona's `scripts/setup-proxy-dns.sh` inside WSL or use a local DNS tool.
413
- This is not required for API-only usage.
414
-
415
- ### Vercel
416
-
417
- Environment variables:
418
- - `SANDBOX_VERCEL_TEAM_ID`
419
- - `SANDBOX_VERCEL_PROJECT_ID`
420
- - `SANDBOX_VERCEL_TOKEN`
421
-
422
- Behavior notes:
423
- - `externalSandboxId` maps to Vercel `sandbox.sandboxId`.
424
- - Reconnect is done via `Sandbox.get(...)` using the stored `externalSandboxId`.
425
-
426
- ---
427
-
428
- ## Dev local (Daytona OSS en Docker Desktop)
429
-
430
- ### 1) Levantar Daytona local
431
-
432
- Usa el helper para clonar el repo de Daytona (fuera del workspace) y levantar el stack:
433
-
434
- ```powershell
435
- powershell -ExecutionPolicy Bypass -File .\scripts\daytona-local.ps1 init
436
- powershell -ExecutionPolicy Bypass -File .\scripts\daytona-local.ps1 up
437
- ```
438
-
439
- Dashboard local: `http://localhost:3000`
440
-
441
- Credenciales (dev):
442
- - usuario: `dev@daytona.io`
443
- - password: `password`
444
-
445
- ### 2) Crear API key manualmente
446
-
447
- En el dashboard, ve a **API Keys** y crea una key. Luego configura tu app:
448
-
449
- ```bash
450
- SANDBOX_PROVIDER=daytona
451
- DAYTONA_API_URL=http://localhost:3000/api
452
- DAYTONA_API_KEY=...tu_api_key_local...
453
- ```
454
-
455
- Notas:
456
- - Guarda la key localmente; **no la comitees**.
457
- - Si necesitas proxy/preview en Windows, configura `*.proxy.localhost` (ver README de Daytona OSS).
458
-
459
- ---
75
+ When `vercel.name`, `persistent`, and `reuse` are enabled, Ekairos first tries to resume the named sandbox before creating a new one. This reduces repeated installs, network transfer, and creation churn for coding-agent workspaces.
460
76
 
461
- ## Development notes
77
+ `createCheckpoint` and `listCheckpoints` use Vercel snapshots for Vercel sandboxes. Creating a Vercel snapshot stops the current VM session; the next command resumes the named sandbox from provider state.
462
78
 
463
- - Prefer `runCommand(sandboxId, ...)` for provider-agnostic usage.
464
- - Only `reconnect()` when you need direct provider SDK features.
465
- - Avoid storing secrets in `params`.
466
- - When using volumes, include a manifest/versioning strategy.
79
+ Useful env overrides:
467
80
 
468
- ---
81
+ - `SANDBOX_PROVIDER=vercel`
82
+ - `SANDBOX_VERCEL_PROFILE=coding-agent`
83
+ - `SANDBOX_VERCEL_NAME=ekairos-codex-workspace`
84
+ - `SANDBOX_VERCEL_REUSE=true`
85
+ - `SANDBOX_VERCEL_PERSISTENT=true`
86
+ - `SANDBOX_VERCEL_DELETE_ON_STOP=false`
87
+ - `SANDBOX_VERCEL_TIMEOUT_MS=1200000`
88
+ - `SANDBOX_VERCEL_VCPUS=2`
469
89
 
470
- ## Testing
90
+ ## Important ids
471
91
 
472
- A smoke test exists in:
473
- - `packages/sandbox/src/tests/sandbox.temp-app.test.ts`
92
+ - `sandboxId`: durable InstantDB record id
93
+ - `externalSandboxId`: provider-native sandbox id
474
94
 
475
- It:
476
- - Creates a temporary Instant app via `instant-cli`.
477
- - Pushes a minimal schema with `sandboxDomain`.
478
- - Creates a sandbox (Daytona) and runs a simple command.
95
+ ## Tests
479
96
 
480
- Run:
481
97
  ```bash
482
98
  pnpm --filter @ekairos/sandbox test
483
99
  ```
484
-
485
- ---
486
-
487
- ## Roadmap
488
-
489
- - Snapshot/image presets for faster cold-start.
490
- - Volume GC policy.
491
- - Preview proxy on custom domain.
492
-
@@ -0,0 +1,156 @@
1
+ import type { CommandResult } from "./commands.js";
2
+ import { type SandboxProcessRunResult, type SandboxProcessStreamChunk } from "./service.js";
3
+ import type { SandboxConfig } from "./types.js";
4
+ type ServiceResult<T = unknown> = {
5
+ ok: true;
6
+ data: T;
7
+ } | {
8
+ ok: false;
9
+ error: string;
10
+ };
11
+ type SandboxRuntime = {
12
+ db: unknown;
13
+ };
14
+ type SandboxFileInput = {
15
+ path: string;
16
+ contentBase64: string;
17
+ };
18
+ export declare function createSandboxStep({ runtime, input, }: {
19
+ runtime: SandboxRuntime;
20
+ input: SandboxConfig;
21
+ }): Promise<ServiceResult<{
22
+ sandboxId: string;
23
+ }>>;
24
+ export declare function stopSandboxStep({ runtime, input, }: {
25
+ runtime: SandboxRuntime;
26
+ input: {
27
+ sandboxId: string;
28
+ };
29
+ }): Promise<ServiceResult<void>>;
30
+ export declare function runCommandStep({ runtime, input, }: {
31
+ runtime: SandboxRuntime;
32
+ input: {
33
+ sandboxId: string;
34
+ command: string;
35
+ args?: string[];
36
+ };
37
+ }): Promise<ServiceResult<CommandResult>>;
38
+ export declare function runCommandProcessStep({ runtime, input, }: {
39
+ runtime: SandboxRuntime;
40
+ input: {
41
+ sandboxId: string;
42
+ command: string;
43
+ args?: string[];
44
+ cwd?: string;
45
+ env?: Record<string, unknown>;
46
+ kind?: "command" | "service" | "codex-app-server" | "dev-server" | "test-runner" | "watcher";
47
+ mode?: "foreground" | "background";
48
+ metadata?: Record<string, unknown>;
49
+ };
50
+ }): Promise<ServiceResult<SandboxProcessRunResult>>;
51
+ export declare function readProcessStreamStep({ runtime, input, }: {
52
+ runtime: SandboxRuntime;
53
+ input: {
54
+ processId: string;
55
+ };
56
+ }): Promise<ServiceResult<{
57
+ chunks: SandboxProcessStreamChunk[];
58
+ byteOffset: number;
59
+ }>>;
60
+ export declare function startObservedProcessStep({ runtime, input, }: {
61
+ runtime: SandboxRuntime;
62
+ input: {
63
+ sandboxId: string;
64
+ command: string;
65
+ args?: string[];
66
+ cwd?: string;
67
+ env?: Record<string, unknown>;
68
+ kind?: "command" | "service" | "codex-app-server" | "dev-server" | "test-runner" | "watcher";
69
+ mode?: "foreground" | "background";
70
+ externalProcessId?: string;
71
+ metadata?: Record<string, unknown>;
72
+ };
73
+ }): Promise<ServiceResult<SandboxProcessRunResult>>;
74
+ export declare function appendObservedProcessChunkStep({ runtime, input, }: {
75
+ runtime: SandboxRuntime;
76
+ input: {
77
+ processId: string;
78
+ type: "stdout" | "stderr" | "status" | "exit" | "error" | "heartbeat" | "metadata";
79
+ data?: Record<string, unknown>;
80
+ };
81
+ }): Promise<ServiceResult<void>>;
82
+ export declare function finishObservedProcessStep({ runtime, input, }: {
83
+ runtime: SandboxRuntime;
84
+ input: {
85
+ processId: string;
86
+ status?: "exited" | "failed" | "killed" | "lost";
87
+ exitCode?: number;
88
+ errorText?: string;
89
+ metadata?: Record<string, unknown>;
90
+ };
91
+ }): Promise<ServiceResult<void>>;
92
+ export declare function writeFilesStep({ runtime, input, }: {
93
+ runtime: SandboxRuntime;
94
+ input: {
95
+ sandboxId: string;
96
+ files: SandboxFileInput[];
97
+ };
98
+ }): Promise<ServiceResult<void>>;
99
+ export declare function readFileStep({ runtime, input, }: {
100
+ runtime: SandboxRuntime;
101
+ input: {
102
+ sandboxId: string;
103
+ path: string;
104
+ };
105
+ }): Promise<ServiceResult<{
106
+ contentBase64: string;
107
+ }>>;
108
+ export declare function installCodexAuthStep({ runtime, input, }: {
109
+ runtime: SandboxRuntime;
110
+ input: {
111
+ sandboxId: string;
112
+ codexHome?: string;
113
+ authJsonPath?: string;
114
+ credentialsJsonPath?: string;
115
+ configTomlPath?: string;
116
+ };
117
+ }): Promise<ServiceResult<{
118
+ authJson: boolean;
119
+ credentialsJson: boolean;
120
+ configToml: boolean;
121
+ }>>;
122
+ export declare function getSandboxStep({ runtime, input, }: {
123
+ runtime: SandboxRuntime;
124
+ input: {
125
+ sandboxId: string;
126
+ };
127
+ }): Promise<ServiceResult<Record<string, unknown>>>;
128
+ export declare function createCheckpointStep({ runtime, input, }: {
129
+ runtime: SandboxRuntime;
130
+ input: {
131
+ sandboxId: string;
132
+ comment?: string;
133
+ };
134
+ }): Promise<ServiceResult<{
135
+ checkpointId: string;
136
+ }>>;
137
+ export declare function getPortUrlStep({ runtime, input, }: {
138
+ runtime: SandboxRuntime;
139
+ input: {
140
+ sandboxId: string;
141
+ port: number;
142
+ };
143
+ }): Promise<ServiceResult<{
144
+ url: string;
145
+ }>>;
146
+ export declare function createEkairosAppStep({ runtime, input, }: {
147
+ runtime: SandboxRuntime;
148
+ input: {
149
+ sandboxId: string;
150
+ appDir: string;
151
+ packageManager?: string;
152
+ instantTokenEnvName?: string;
153
+ };
154
+ }): Promise<ServiceResult<SandboxProcessRunResult>>;
155
+ export {};
156
+ //# sourceMappingURL=action-steps.d.ts.map