@madarco/agentbox 0.13.0 → 0.15.0

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 (74) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/README.md +11 -8
  3. package/dist/{_cloud-attach-HJC672UR.js → _cloud-attach-R6TRWG5L.js} +4 -4
  4. package/dist/{chunk-QYRK5H6Q.js → chunk-43Q5GWP6.js} +108 -56
  5. package/dist/chunk-43Q5GWP6.js.map +1 -0
  6. package/dist/{chunk-ECLLV5JH.js → chunk-72CJTXN6.js} +156 -5
  7. package/dist/chunk-72CJTXN6.js.map +1 -0
  8. package/dist/{chunk-R5XIDQFR.js → chunk-BKU34KYY.js} +170 -6
  9. package/dist/chunk-BKU34KYY.js.map +1 -0
  10. package/dist/{chunk-4NQXNQ53.js → chunk-E7CHS7ZR.js} +168 -58
  11. package/dist/chunk-E7CHS7ZR.js.map +1 -0
  12. package/dist/chunk-MCOU6CZS.js +346 -0
  13. package/dist/chunk-MCOU6CZS.js.map +1 -0
  14. package/dist/{chunk-B4QG2MCW.js → chunk-MLMFNN4T.js} +762 -483
  15. package/dist/chunk-MLMFNN4T.js.map +1 -0
  16. package/dist/{chunk-2LF5YILI.js → chunk-RSKG7AFU.js} +80 -6
  17. package/dist/chunk-RSKG7AFU.js.map +1 -0
  18. package/dist/{chunk-SNTHHWKY.js → chunk-XKH7NTT7.js} +80 -22
  19. package/dist/chunk-XKH7NTT7.js.map +1 -0
  20. package/dist/{dist-7KVUIKJX.js → dist-AGTIA7AD.js} +37 -226
  21. package/dist/dist-AGTIA7AD.js.map +1 -0
  22. package/dist/{dist-OPIBZ7XM.js → dist-FIFEFKJ7.js} +14 -69
  23. package/dist/dist-FIFEFKJ7.js.map +1 -0
  24. package/dist/dist-JZ3XO6EB.js +662 -0
  25. package/dist/dist-JZ3XO6EB.js.map +1 -0
  26. package/dist/{dist-OG6NW6SM.js → dist-OGJGZETZ.js} +5 -3
  27. package/dist/{dist-JAN5VABY.js → dist-S4XR4ACV.js} +25 -177
  28. package/dist/dist-S4XR4ACV.js.map +1 -0
  29. package/dist/index.js +2229 -1314
  30. package/dist/index.js.map +1 -1
  31. package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js → prepared-state-MQHD3M5F-Q27AZU53.js} +2 -2
  32. package/package.json +6 -4
  33. package/runtime/docker/Dockerfile.box +21 -26
  34. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +67 -1
  35. package/runtime/docker/packages/ctl/dist/bin.cjs +361 -43
  36. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +17 -6
  37. package/runtime/docker/packages/sandbox-docker/scripts/chromium-resolver +57 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +2 -1
  39. package/runtime/e2b/agentbox-checkpoint-cleanup +52 -0
  40. package/runtime/e2b/agentbox-codex-hooks.json +68 -0
  41. package/runtime/e2b/agentbox-open +28 -0
  42. package/runtime/e2b/agentbox-setup-skill.md +263 -0
  43. package/runtime/e2b/agentbox-vnc-start +102 -0
  44. package/runtime/e2b/attach-helper.cjs +167 -0
  45. package/runtime/e2b/claude-managed-settings.json +116 -0
  46. package/runtime/e2b/ctl.cjs +24158 -0
  47. package/runtime/e2b/custom-system-CLAUDE.md +46 -0
  48. package/runtime/e2b/gh-shim +344 -0
  49. package/runtime/e2b/git-shim +131 -0
  50. package/runtime/e2b/scripts/build-template.sh +295 -0
  51. package/runtime/hetzner/agentbox-setup-skill.md +67 -1
  52. package/runtime/hetzner/agentbox-vnc-start +17 -6
  53. package/runtime/hetzner/claude-managed-settings.json +2 -1
  54. package/runtime/hetzner/ctl.cjs +361 -43
  55. package/runtime/relay/bin.cjs +380 -233
  56. package/runtime/vercel/agentbox-setup-skill.md +67 -1
  57. package/runtime/vercel/agentbox-vnc-start +17 -6
  58. package/runtime/vercel/claude-managed-settings.json +2 -1
  59. package/runtime/vercel/ctl.cjs +361 -43
  60. package/share/agentbox-setup/SKILL.md +67 -1
  61. package/share/host-skills/agentbox-info/SKILL.md +47 -35
  62. package/dist/chunk-2LF5YILI.js.map +0 -1
  63. package/dist/chunk-4NQXNQ53.js.map +0 -1
  64. package/dist/chunk-B4QG2MCW.js.map +0 -1
  65. package/dist/chunk-ECLLV5JH.js.map +0 -1
  66. package/dist/chunk-QYRK5H6Q.js.map +0 -1
  67. package/dist/chunk-R5XIDQFR.js.map +0 -1
  68. package/dist/chunk-SNTHHWKY.js.map +0 -1
  69. package/dist/dist-7KVUIKJX.js.map +0 -1
  70. package/dist/dist-JAN5VABY.js.map +0 -1
  71. package/dist/dist-OPIBZ7XM.js.map +0 -1
  72. /package/dist/{_cloud-attach-HJC672UR.js.map → _cloud-attach-R6TRWG5L.js.map} +0 -0
  73. /package/dist/{dist-OG6NW6SM.js.map → dist-OGJGZETZ.js.map} +0 -0
  74. /package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js.map → prepared-state-MQHD3M5F-Q27AZU53.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  readPreparedDockerState,
7
7
  resolveContextFiles,
8
8
  writePreparedDockerState
9
- } from "./chunk-SNTHHWKY.js";
9
+ } from "./chunk-XKH7NTT7.js";
10
10
  export {
11
11
  DOCKERFILE_PATH,
12
12
  computeDockerContextFingerprint,
@@ -15,4 +15,4 @@ export {
15
15
  resolveContextFiles,
16
16
  writePreparedDockerState
17
17
  };
18
- //# sourceMappingURL=prepared-state-MQHD3M5F-KE4DT3GX.js.map
18
+ //# sourceMappingURL=prepared-state-MQHD3M5F-Q27AZU53.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madarco/agentbox",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Launch Claude Code, Codex, and other coding agents in isolated sandboxes",
5
5
  "license": "MIT",
6
6
  "author": "Marco D'Alia",
@@ -46,6 +46,7 @@
46
46
  "@vercel/sandbox": "^2.0.1",
47
47
  "@xterm/headless": "^5.5.0",
48
48
  "commander": "^12.1.0",
49
+ "e2b": "^2.27.1",
49
50
  "execa": "^9.5.2",
50
51
  "supports-hyperlinks": "^3.1.0",
51
52
  "yaml": "^2.6.1"
@@ -61,13 +62,14 @@
61
62
  "@agentbox/config": "0.0.0",
62
63
  "@agentbox/core": "0.0.0",
63
64
  "@agentbox/ctl": "0.0.0",
64
- "@agentbox/relay": "0.0.0",
65
65
  "@agentbox/sandbox-cloud": "0.0.0",
66
+ "@agentbox/relay": "0.0.0",
66
67
  "@agentbox/sandbox-core": "0.0.0",
68
+ "@agentbox/sandbox-docker": "0.0.0",
67
69
  "@agentbox/sandbox-daytona": "0.0.0",
68
70
  "@agentbox/sandbox-hetzner": "0.0.0",
69
- "@agentbox/sandbox-vercel": "0.0.0",
70
- "@agentbox/sandbox-docker": "0.0.0"
71
+ "@agentbox/sandbox-e2b": "0.0.0",
72
+ "@agentbox/sandbox-vercel": "0.0.0"
71
73
  },
72
74
  "scripts": {
73
75
  "build": "tsup",
@@ -268,16 +268,20 @@ RUN npm install -g opencode-ai
268
268
  # the noble transition. If the base image moves back to jammy these will
269
269
  # need the un-suffixed names.
270
270
  #
271
- # 2. Get an actual Chromium binary. `agent-browser install` would normally
272
- # download Chrome for Testing, but Chrome for Testing has no Linux ARM64
273
- # build (Apple Silicon hosts run linux/arm64 containers) and Noble's
274
- # `chromium-browser` apt package is a snap stub that won't run inside a
275
- # container. The reliable cross-arch source is Playwright's bundled
276
- # Chromium, so we install playwright globally just for its
277
- # `playwright install chromium` downloader, then point agent-browser at
278
- # the resulting binary via AGENT_BROWSER_EXECUTABLE_PATH. agent-browser
279
- # honours that env var (priority: CLI flag > env > config file >
280
- # built-in default; see agent-browser's README).
271
+ # 2. Provide a Chromium *lazily*, shared with the project's Playwright. We do
272
+ # NOT bake a Chromium download into the image: Playwright pins an exact
273
+ # build per playwright version, so a baked browser goes stale the instant a
274
+ # project pins a different Playwright its `playwright install` fetches a
275
+ # different build and anything waiting on the baked path hangs. Instead,
276
+ # `AGENT_BROWSER_EXECUTABLE_PATH` points at the `chromium-resolver` script
277
+ # (installed as /usr/local/bin/chromium below), which reuses the project's
278
+ # Playwright Chromium when present and otherwise installs one on first use
279
+ # with the project's pinned Playwright (matching build), falling back to the
280
+ # box's global Playwright. agent-browser honours that env var (priority: CLI
281
+ # flag > env > config file > built-in default; see agent-browser's README).
282
+ # Chrome-for-Testing has no linux/arm64 build and Noble's chromium apt
283
+ # package is a snap stub, so Playwright's downloader stays the only reliable
284
+ # cross-arch source — we just invoke it lazily instead of at build time.
281
285
  #
282
286
  # Runtime state (sessions, auth cookies under ~/.agent-browser/) lives in the
283
287
  # container's writable layer, preserved across pause/unpause/stop/start and
@@ -290,6 +294,8 @@ RUN apt-get update \
290
294
  fonts-liberation xdg-utils \
291
295
  && rm -rf /var/lib/apt/lists/*
292
296
 
297
+ # agent-browser + a global playwright (the latter is only the lazy fallback
298
+ # Chromium downloader for projects that don't pin their own Playwright).
293
299
  RUN npm install -g agent-browser playwright
294
300
 
295
301
  # Portless CLI (https://portless.sh). Only the client — the box never runs the
@@ -299,22 +305,11 @@ RUN npm install -g agent-browser playwright
299
305
  # Requires Node 24+ — hence the setup_24.x bump above.
300
306
  RUN npm install -g portless
301
307
 
302
- # Download Chromium as `vscode` so the ms-playwright cache lands in vscode's
303
- # home (the user agent-browser runs as). The downloaded binary lives at
304
- # `chromium-XXXX/chrome-linux*/chrome`, where XXXX is a Playwright-internal
305
- # revision number that changes between releases — and the inner dir is
306
- # `chrome-linux` for old releases and `chrome-linux64` (or `chrome-linux/arm64`)
307
- # for current Chrome-for-Testing builds. Glob both. We resolve once and write
308
- # a stable symlink so AGENT_BROWSER_EXECUTABLE_PATH can point at something
309
- # predictable.
310
- USER vscode
311
- RUN playwright install chromium \
312
- && CHROME_BIN="$(ls /home/vscode/.cache/ms-playwright/chromium-*/chrome-linux*/chrome 2>/dev/null | sort | tail -1)" \
313
- && test -n "$CHROME_BIN" \
314
- && ln -sf "$CHROME_BIN" /tmp/chromium-link \
315
- && test -x "$(readlink /tmp/chromium-link)"
316
- USER root
317
- RUN mv /tmp/chromium-link /usr/local/bin/chromium
308
+ # /usr/local/bin/chromium is a resolver (not a baked binary): on first launch it
309
+ # reuses the project's Playwright Chromium or installs one on demand, then execs
310
+ # it. See the script header and the rationale in the browser-support note above.
311
+ COPY packages/sandbox-docker/scripts/chromium-resolver /usr/local/bin/chromium
312
+ RUN chmod +x /usr/local/bin/chromium
318
313
 
319
314
  ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/local/bin/chromium
320
315
 
@@ -48,7 +48,42 @@ Look at `/workspace`:
48
48
  - No cycles in `needs:`.
49
49
  - **Always generate a dependency-install task** and make it the root of the `needs:` graph (every service that needs deps gets `needs: [install, …]`). Future boxes start from a snapshot of the final filesystem so they won't need this, but updates or moving to a cloud provider might need to rebuild the container from scratch. The filesystem can be then later captured by `agentbox-ctl checkpoint --set-default`. The task must be **idempotent and self-healing**: `agentbox-ctl` re-runs pending tasks on every box stop/start (the daemon dies with the container and is relaunched), so a plain `rm -rf node_modules && install` would wipe + reinstall on every start. Guard the rebuild with a marker file *inside* `node_modules` (the `.agentbox-installed` convention AgentBox uses internally): rebuild only when the marker is absent (fresh box), and be a fast no-op once it exists. Detect the package manager from the lockfile — never hardcode `pnpm`. See the worked example below.
50
50
  - **Add a comment to the beginning** of the file to explain what you did and what issues you encountered, so that future run might use this information in case the project evolves and you need to update the agentbox.yaml file.
51
- -
51
+
52
+ ### Stateful services: data persistence & re-seeding (read this for databases)
53
+
54
+ **A checkpoint does NOT capture docker-in-docker data.** `agentbox checkpoint` is a `docker commit` of the box's writable filesystem (the system + `/workspace`). The in-box `dockerd` keeps its storage in a *separate* per-box volume (`/var/lib/docker`), which is **not** part of that image — it's fresh on every new box and wiped on `agentbox destroy`. So a database or cache you run as a **docker container** (e.g. `docker run … postgres`) starts **empty on every new box** created from a checkpoint (every `agentbox claude` / `agentbox create`), even though `/workspace` and any marker files you wrote were restored. (A DB run as a **native process** with its data dir on the box filesystem — e.g. `postgres -D /var/lib/postgresql/data` — *is* captured by the checkpoint, since it lives in the writable layer.)
55
+
56
+ **Consequence for migrate/seed tasks of a containerized DB: do not gate them on a filesystem marker.** A marker like `node_modules/.agentbox-installed` is correct for deps (they live in `/workspace`, which the checkpoint captures), but **wrong** for DB data living in a docker volume: the marker is restored from the checkpoint while the DB is empty, so a marker-guarded seed wrongly skips and the app boots against an empty database. Instead, **gate on the actual data** — connect to the DB and check whether a sentinel table/row exists, and seed only when it's missing:
57
+
58
+ ```yaml
59
+ seed:
60
+ # Re-seed when the DB is empty. The postgres data lives in the in-box
61
+ # docker volume, which is NOT captured by `agentbox checkpoint` — so a box
62
+ # started from a checkpoint has the workspace warm but an empty DB. We can't
63
+ # use a filesystem marker here (it would be restored while the DB is blank);
64
+ # instead probe the DB and seed only if the data is absent. Fast no-op once
65
+ # the data is present.
66
+ command: |
67
+ set -e
68
+ export PGPASSWORD=postgres
69
+ # Probe for existing data. If the table is missing the query errors,
70
+ # stderr is suppressed, stdout is empty, the grep fails — so we seed.
71
+ if psql -h 127.0.0.1 -p 5432 -U postgres -d app -tAc \
72
+ "SELECT EXISTS (SELECT 1 FROM users LIMIT 1)" 2>/dev/null | grep -q t; then
73
+ echo "data present — skip seed"
74
+ exit 0
75
+ fi
76
+ pnpm db:seed
77
+ needs: [install, migrate]
78
+ ```
79
+
80
+ **Lifecycle nuance (this is why the data check, not a marker, is right):**
81
+
82
+ - **Box stop → start** (`agentbox stop`/`start`): the supervisor daemon dies with the container and is relaunched, so it **re-runs all tasks** from `pending`. The per-box docker volume *does* survive stop/start, so the DB still has data — the data check makes the seed a fast no-op.
83
+ - **New box from a checkpoint** (`agentbox claude`/`create`): tasks run and the DB volume is empty → the check fails → the seed runs. Correct.
84
+ - **Resume after pause** (`agentbox pause`/`unpause`): the daemon is frozen and thawed, **not** restarted, so tasks do **not** re-run at all — nothing to seed, the running DB is untouched.
85
+
86
+ (Migrations are usually safe to re-run as-is: migration tools track applied migrations in their own table, which on a fresh box is empty, so they simply re-apply. Only the *data* seed needs the existence check.) Install the DB client the seed/migrate tasks need (e.g. `postgresql-client`) in the `install` task — don't `docker exec` the DB container for these checks (nested `docker exec` fails inside a box with a `setns` error); reach it over TCP with the client tools instead.
52
87
 
53
88
  ## 3. Wire readiness probes (services only)
54
89
 
@@ -157,6 +192,36 @@ services:
157
192
  factor: 2
158
193
  ```
159
194
 
195
+ ## 6b. Bringing extra host files/folders into the box
196
+
197
+ Two ways to copy host files in (both COPY — never a live mount, so the box can't
198
+ write back to the host):
199
+
200
+ - **`carry:` block** (declarative, in `agentbox.yaml`) — for files/dirs every box
201
+ should get at create time. Each entry is `{ src, dest }` with optional `mode`,
202
+ `user`, `optional`, and `exclude:` (a list of tar globs / bare dir names to drop
203
+ when copying a directory). Heavy regenerable dirs (`.git`, `node_modules`, `bin`,
204
+ `obj`, `packages`, `dist`, `.next`, `target`) are dropped by default; `exclude:`
205
+ is additive. Each carry entry is capped at `box.cpMaxBytes` (default 100 MiB
206
+ after excludes) — the same limit `agentbox cp` enforces.
207
+ - **`agentbox-ctl cp fromHost <hostPath> <boxPath>`** (ad-hoc, from inside the box)
208
+ — for a one-off copy. Prompts the user on the host to approve.
209
+
210
+ **The per-copy size limit (important for large/legacy folders).** A single copy is
211
+ blocked above `box.cpMaxBytes` (default **100 MB**) *after* default excludes, so it
212
+ fails loud instead of silently hanging. When blocked you get a `du`-style tree of
213
+ the biggest remaining folders/subfolders. To get under the limit, EITHER:
214
+
215
+ - **drop what the box can regenerate** (the default excludes already remove
216
+ `node_modules`/`.git`/build output; add more with `--exclude=<glob-or-name>`), OR
217
+ - **copy the heavy folders one at a time** so each copy is under the limit, OR
218
+ - pass `--yes` to copy the whole thing anyway (only when you really need it all).
219
+
220
+ Example: a 2.4 GB legacy folder is mostly `packages/` (NuGet) + `.git`; those are
221
+ excluded by default, and what's left can be split:
222
+ `agentbox-ctl cp fromHost ../legacy/src /workspace/legacy/src` then
223
+ `... cp fromHost ../legacy/Database /workspace/legacy/Database`.
224
+
160
225
  ## 7. Validate before handing off
161
226
 
162
227
  - check with `agentbox-ctl reload` and then `agentbox-ctl status` that everything is running as expected.
@@ -184,6 +249,7 @@ Tell the user (verbatim):
184
249
  ## 9. Checkpoint the warm state - DON't SKIP THIS STEP
185
250
 
186
251
  Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
252
+ Remember the checkpoint captures the writable layer (`/workspace` + system), **not** docker-in-docker volumes — so a containerized DB's data does not carry into new boxes. That's expected; the data-existence-gated seed task from section 2 re-seeds those automatically. (If you need the data itself to persist into new boxes, run the DB as a native process with its data dir on the box filesystem, or bind a `/workspace` path as the container's data volume so it lands in the checkpoint.)
187
253
  Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
188
254
  On all providers except Vercel, this doesn't need to be confirmed by the user. It will pause the container for several seconds so warn the user about it and write Done when it's done.
189
255
  On Vercel: this actually STOPS the sandbox, so warn the user about it. Also the system will ask confirmation.