@madarco/agentbox 0.4.1 → 0.6.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 (40) hide show
  1. package/dist/{chunk-J35IH7W5.js → chunk-BBZMA2K6.js} +61 -23
  2. package/dist/chunk-BBZMA2K6.js.map +1 -0
  3. package/dist/{chunk-SOMIKEN2.js → chunk-HHMWQNLF.js} +272 -214
  4. package/dist/chunk-HHMWQNLF.js.map +1 -0
  5. package/dist/{chunk-IDR4HVIC.js → chunk-HPZMD5DE.js} +2 -2
  6. package/dist/chunk-HPZMD5DE.js.map +1 -0
  7. package/dist/{chunk-NSIECUCS.js → chunk-HTTKML3C.js} +705 -289
  8. package/dist/chunk-HTTKML3C.js.map +1 -0
  9. package/dist/{chunk-WR5FFGE5.js → chunk-KJNZP6I3.js} +218 -128
  10. package/dist/chunk-KJNZP6I3.js.map +1 -0
  11. package/dist/{chunk-FQD6ZWYW.js → chunk-M7I247BK.js} +68 -65
  12. package/dist/chunk-M7I247BK.js.map +1 -0
  13. package/dist/create-6PWXI6HO-OWAMHBAK.js +15 -0
  14. package/dist/index.js +2394 -1283
  15. package/dist/index.js.map +1 -1
  16. package/dist/{lifecycle-LURNDNYO-UWQYPNPX.js → lifecycle-EMXR46DI-DUVBXNTV.js} +5 -5
  17. package/dist/{state-ZSP3ORXW-WI6KOIG3.js → state-KD7M46ZP-KHFTHFUS.js} +2 -2
  18. package/dist/stats-SZXOJE3D-N7OODCHW.js +19 -0
  19. package/package.json +3 -2
  20. package/runtime/docker/Dockerfile.box +65 -25
  21. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +52 -55
  22. package/runtime/docker/packages/ctl/dist/bin.cjs +272 -160
  23. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup +52 -0
  24. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-dockerd-start +87 -7
  25. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-open +28 -0
  26. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +21 -15
  27. package/runtime/relay/bin.cjs +407 -12
  28. package/share/agentbox-setup/SKILL.md +52 -55
  29. package/dist/chunk-FQD6ZWYW.js.map +0 -1
  30. package/dist/chunk-IDR4HVIC.js.map +0 -1
  31. package/dist/chunk-J35IH7W5.js.map +0 -1
  32. package/dist/chunk-NSIECUCS.js.map +0 -1
  33. package/dist/chunk-SOMIKEN2.js.map +0 -1
  34. package/dist/chunk-WR5FFGE5.js.map +0 -1
  35. package/dist/create-4BQY2UYU-CGSW3RGE.js +0 -15
  36. package/dist/stats-GZFLPYTU-DBJ2DVBJ.js +0 -19
  37. /package/dist/{create-4BQY2UYU-CGSW3RGE.js.map → create-6PWXI6HO-OWAMHBAK.js.map} +0 -0
  38. /package/dist/{lifecycle-LURNDNYO-UWQYPNPX.js.map → lifecycle-EMXR46DI-DUVBXNTV.js.map} +0 -0
  39. /package/dist/{state-ZSP3ORXW-WI6KOIG3.js.map → state-KD7M46ZP-KHFTHFUS.js.map} +0 -0
  40. /package/dist/{stats-GZFLPYTU-DBJ2DVBJ.js.map → stats-SZXOJE3D-N7OODCHW.js.map} +0 -0
@@ -13,12 +13,12 @@ import {
13
13
  startBox,
14
14
  stopBox,
15
15
  unpauseBox
16
- } from "./chunk-FQD6ZWYW.js";
16
+ } from "./chunk-M7I247BK.js";
17
17
  import {
18
18
  SNAPSHOTS_ROOT
19
- } from "./chunk-NSIECUCS.js";
20
- import "./chunk-IDR4HVIC.js";
21
- import "./chunk-SOMIKEN2.js";
19
+ } from "./chunk-HTTKML3C.js";
20
+ import "./chunk-HPZMD5DE.js";
21
+ import "./chunk-HHMWQNLF.js";
22
22
  export {
23
23
  AmbiguousBoxError,
24
24
  BoxNotFoundError,
@@ -35,4 +35,4 @@ export {
35
35
  stopBox,
36
36
  unpauseBox
37
37
  };
38
- //# sourceMappingURL=lifecycle-LURNDNYO-UWQYPNPX.js.map
38
+ //# sourceMappingURL=lifecycle-EMXR46DI-DUVBXNTV.js.map
@@ -10,7 +10,7 @@ import {
10
10
  removeBoxRecord,
11
11
  resolveBoxRef,
12
12
  writeState
13
- } from "./chunk-IDR4HVIC.js";
13
+ } from "./chunk-HPZMD5DE.js";
14
14
  export {
15
15
  STATE_DIR,
16
16
  STATE_FILE,
@@ -23,4 +23,4 @@ export {
23
23
  resolveBoxRef,
24
24
  writeState
25
25
  };
26
- //# sourceMappingURL=state-ZSP3ORXW-WI6KOIG3.js.map
26
+ //# sourceMappingURL=state-KD7M46ZP-KHFTHFUS.js.map
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ agentboxHomeBytes,
4
+ allCheckpointImagesBytes,
5
+ boxResourceStats,
6
+ parseDockerSize,
7
+ projectCheckpointImageBytes,
8
+ volumeSizeBytes
9
+ } from "./chunk-BBZMA2K6.js";
10
+ import "./chunk-HHMWQNLF.js";
11
+ export {
12
+ agentboxHomeBytes,
13
+ allCheckpointImagesBytes,
14
+ boxResourceStats,
15
+ parseDockerSize,
16
+ projectCheckpointImageBytes,
17
+ volumeSizeBytes
18
+ };
19
+ //# sourceMappingURL=stats-SZXOJE3D-N7OODCHW.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madarco/agentbox",
3
- "version": "0.4.1",
3
+ "version": "0.6.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",
@@ -57,8 +57,9 @@
57
57
  "vitest": "^2.1.8",
58
58
  "@agentbox/config": "0.0.0",
59
59
  "@agentbox/core": "0.0.0",
60
+ "@agentbox/ctl": "0.0.0",
60
61
  "@agentbox/sandbox-docker": "0.0.0",
61
- "@agentbox/ctl": "0.0.0"
62
+ "@agentbox/relay": "0.0.0"
62
63
  },
63
64
  "scripts": {
64
65
  "build": "tsup",
@@ -2,12 +2,17 @@
2
2
  #
3
3
  # Layered on Microsoft's devcontainers base:ubuntu (arm64-native — the
4
4
  # `universal:2-linux` image is amd64-only, which is a non-starter on Apple
5
- # Silicon hosts). We add the bits the box needs to mount a FUSE overlay over
6
- # /host-src + /upper, plus a "universal-ish" set of language runtimes
7
- # (Node.js 22 from NodeSource, Python 3 from apt). Anything heavier (Go, Java,
8
- # Ruby, .NET, browser tooling, vscode-server) goes in a later iteration.
5
+ # Silicon hosts). /workspace is a plain dir in the image's writable layer:
6
+ # `agentbox create` populates it via in-container `git worktree add` (or a
7
+ # tar pipe for the no-git case). The old FUSE overlay over /host-src+/upper
8
+ # is gone but fuse3 + fuse-overlayfs stay as the in-box dockerd's fallback
9
+ # storage driver (it prefers the kernel-native overlay2). Plus the "universal-ish" set of
10
+ # language runtimes (Node.js 22 from NodeSource, Python 3 from apt). Heavier
11
+ # tooling (Go, Java, Ruby, .NET, more browser tooling, vscode-server) goes in
12
+ # a later iteration.
9
13
  #
10
- # Required runtime flags:
14
+ # Required runtime flags (SYS_ADMIN + /dev/fuse + apparmor:unconfined are now
15
+ # load-bearing only for the *inner* dockerd — keep them):
11
16
  # --cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined
12
17
  #
13
18
  # Pinned to ubuntu-24.04 (Noble), NOT the rolling `:ubuntu` tag: that tag now
@@ -67,9 +72,9 @@ RUN apt-get update \
67
72
  vim \
68
73
  libcap2-bin \
69
74
  && rm -rf /var/lib/apt/lists/* \
70
- && mkdir -p /workspace /host-src /upper /snapshot /run/agentbox /var/log/agentbox \
71
- && chmod 755 /workspace /host-src /upper /snapshot \
72
- && chown vscode:vscode /run/agentbox /var/log/agentbox
75
+ && mkdir -p /workspace /run/agentbox /var/log/agentbox \
76
+ && chmod 755 /workspace \
77
+ && chown vscode:vscode /workspace /run/agentbox /var/log/agentbox
73
78
 
74
79
  # The in-box supervisor (runs as non-root `vscode`) owns a TCP forwarder that
75
80
  # binds container :80 -> the `expose:`-flagged service (see WebProxy /
@@ -79,6 +84,19 @@ RUN apt-get update \
79
84
  # sandbox — strictly narrower than the existing NET_ADMIN/seccomp=unconfined.
80
85
  RUN setcap cap_net_bind_service=+ep "$(readlink -f "$(command -v node)")"
81
86
 
87
+ # Enable corepack (pnpm/yarn shims) at build time as root. Doing this here
88
+ # rather than in the wizard's install task avoids two failures the runtime
89
+ # `corepack enable` (run as non-root `vscode`) hits: it can't write shims into
90
+ # the root-owned NodeSource bin dir (/usr/bin), and node 22's bundled corepack
91
+ # resolves its dist path relative to the symlink dirname, so a
92
+ # ~/.local/bin/pnpm symlink looks for ~/.local/dist/pnpm.js and breaks.
93
+ # `corepack@latest` fixes the symlink resolution; baking the shims into
94
+ # /usr/bin makes them root-owned-but-world-executable so `vscode` just uses
95
+ # them (each project's `packageManager` version is lazily fetched into the
96
+ # writable ~/.cache/node/corepack on first use, inside the install task).
97
+ RUN npm install -g corepack@latest \
98
+ && corepack enable pnpm yarn
99
+
82
100
  # Host repos are bind-mounted in at their identical absolute path (worktree
83
101
  # pointer files contain absolute paths to <main>/.git/worktrees/<name>, so both
84
102
  # sides have to resolve the same path), and the host owns those `.git/` dirs.
@@ -87,13 +105,15 @@ RUN setcap cap_net_bind_service=+ep "$(readlink -f "$(command -v node)")"
87
105
  # bind-mount of ~/.gitconfig from the host.
88
106
  RUN git config --system --add safe.directory '*'
89
107
 
90
- # Docker-in-Docker. dockerd inside the box (storage-driver=fuse-overlayfs, set
91
- # in /etc/docker/daemon.json) lets the agent run `docker build`/`docker run`
92
- # in its own namespace without exposing the host daemon. fuse-overlayfs is
93
- # required because the kernel `overlay` driver isn't usable from an
94
- # unprivileged container but fuse3 + fuse-overlayfs are already installed
95
- # above for the /workspace overlay, so docker reuses them. iptables is needed
96
- # for inner-container bridge networking. Adding `vscode` to the `docker` group
108
+ # Docker-in-Docker. dockerd inside the box lets the agent run
109
+ # `docker build`/`docker run` in its own namespace without exposing the host
110
+ # daemon. The storage driver is NOT pinned here: agentbox-dockerd-start picks
111
+ # it at runtime — the kernel-native overlay2 when a probe proves it works on
112
+ # the data-root filesystem, else the fuse-overlayfs fallback (fuse3 +
113
+ # fuse-overlayfs are installed above for that fallback). The baked daemon.json
114
+ # only carries `iptables: true`; the script rewrites it with the resolved
115
+ # `storage-driver` before launching dockerd. iptables is needed for
116
+ # inner-container bridge networking. Adding `vscode` to the `docker` group
97
117
  # lets the agent invoke `docker` without sudo once dockerd creates the socket
98
118
  # at /var/run/docker.sock at runtime. The matching launch script is COPY'd in
99
119
  # below; the daemon is started by the host via `docker exec -d --user root`
@@ -104,7 +124,7 @@ RUN apt-get update \
104
124
  iptables \
105
125
  && rm -rf /var/lib/apt/lists/* \
106
126
  && mkdir -p /etc/docker \
107
- && printf '%s\n' '{ "storage-driver": "fuse-overlayfs", "iptables": true }' > /etc/docker/daemon.json \
127
+ && printf '%s\n' '{ "iptables": true }' > /etc/docker/daemon.json \
108
128
  && usermod -aG docker vscode
109
129
 
110
130
  # agentbox-ctl: the in-container supervisor + CLI. Build context is the
@@ -114,12 +134,13 @@ RUN apt-get update \
114
134
  COPY packages/ctl/dist/bin.cjs /usr/local/bin/agentbox-ctl
115
135
  RUN chmod +x /usr/local/bin/agentbox-ctl
116
136
 
117
- # Setup guide for the first-run wizard. The CLI also installs the same file as
118
- # a host-side claude skill (~/.claude/skills/agentbox-setup/SKILL.md) the
119
- # first time the wizard fires; the existing ~/.claude rsync flow propagates
120
- # it into every box automatically. Keeping a copy baked into the image at a
121
- # stable path guarantees the wizard's initial prompt can reference the guide
122
- # even on hosts where the user wiped their ~/.claude/skills.
137
+ # Setup guide for the first-run wizard. This baked copy is the single source
138
+ # of the /agentbox-setup skill: seedSetupSkillIntoVolume()
139
+ # (packages/sandbox-docker/src/claude.ts) copies it into the box's
140
+ # claude-config volume at skills/agentbox-setup/SKILL.md on create /
141
+ # `claude start`. It is intentionally box-only the CLI never writes the
142
+ # skill to the host's ~/.claude. The stable path also lets the wizard's
143
+ # initial prompt reference the guide directly.
123
144
  COPY apps/cli/share/agentbox-setup/SKILL.md /usr/local/share/agentbox/setup-guide.md
124
145
  RUN chmod 0644 /usr/local/share/agentbox/setup-guide.md
125
146
 
@@ -256,6 +277,25 @@ RUN chmod +x /usr/local/bin/agentbox-vnc-start
256
277
  COPY packages/sandbox-docker/scripts/agentbox-dockerd-start /usr/local/bin/agentbox-dockerd-start
257
278
  RUN chmod +x /usr/local/bin/agentbox-dockerd-start
258
279
 
280
+ # Pre-`docker commit` cleanup for checkpoints. Baked into the image so the host
281
+ # can always reach it via `docker exec --user root <ctr>
282
+ # /usr/local/bin/agentbox-checkpoint-cleanup` regardless of the in-box state
283
+ # (e.g. an agent that swapped its login shell). Best-effort: every step is
284
+ # allowed to fail.
285
+ COPY packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup /usr/local/bin/agentbox-checkpoint-cleanup
286
+ RUN chmod +x /usr/local/bin/agentbox-checkpoint-cleanup
287
+
288
+ # Host-routed link opener. The box has no real browser; this wrapper forwards
289
+ # http(s) URLs to the host's default browser via the relay (`agentbox-ctl
290
+ # open`). It shadows xdg-utils' /usr/bin/xdg-open (the symlink lands earlier
291
+ # in PATH) and is set as $BROWSER so any tool that opens a link — Claude
292
+ # Code's OAuth flow, `gh`, `git web--browse`, python's webbrowser — routes
293
+ # to the host.
294
+ COPY packages/sandbox-docker/scripts/agentbox-open /usr/local/bin/agentbox-open
295
+ RUN chmod +x /usr/local/bin/agentbox-open \
296
+ && ln -sf /usr/local/bin/agentbox-open /usr/local/bin/xdg-open
297
+ ENV BROWSER=/usr/local/bin/agentbox-open
298
+
259
299
  # tmux config so Claude's true-color output and OSC 8 hyperlinks survive the
260
300
  # in-container tmux. `terminal-features` is a no-op on tmux < 3.4. Without
261
301
  # this, claude renders without 24-bit color (logo invisible) and hyperlinks
@@ -315,8 +355,8 @@ RUN printf '%s\n' \
315
355
  > /etc/profile.d/agentbox.sh \
316
356
  && chmod 0644 /etc/profile.d/agentbox.sh
317
357
 
318
- # Default to the vscode user (UID 1000) provided by base:ubuntu. The overlay
319
- # mount itself happens via `docker exec --user root`, so this is just the
320
- # default identity for interactive shells.
358
+ # Default to the vscode user (UID 1000) provided by base:ubuntu. /workspace is
359
+ # pre-chowned to vscode above so `agentbox create`'s in-container
360
+ # `git worktree add` (run as vscode via docker exec) can write to it.
321
361
  USER vscode
322
362
  WORKDIR /workspace
@@ -5,7 +5,21 @@ description: Generate an agentbox.yaml for the current AgentBox workspace. Invok
5
5
 
6
6
  # /agentbox-setup
7
7
 
8
- Goal: produce a `/workspace/agentbox.yaml` that captures this project's services, tasks, and box defaults so the in-box supervisor (`agentbox-ctl`) can boot the workspace deterministically.
8
+ ## Box layout (what you're configuring against)
9
+
10
+ Your user i `vscode` and you can use passwordless sudo to run commands as root.
11
+
12
+ `/workspace` is the box's plain writable filesystem — a per-box git worktree on a fresh `agentbox/<box-name>` branch (or a tar-piped copy of the host workspace for non-git projects). Anything you install or build into `/workspace` (incl. `node_modules`, `.next`, `target`, `.venv`) lives in the **container's writable layer** and is captured wholesale by `agentbox checkpoint` (`docker commit`) — so a setup task that runs the install once becomes a warm-start asset for every future box in the project. Everything is wiped on `agentbox destroy`.
13
+
14
+ Three bind mounts wire the box back to the host:
15
+
16
+ - **Host main repo's `.git/`** — bind-mounted RW at its identical absolute host path. In-box commits land on the host's branch refs (visible to `git log` on the host immediately); the box itself carries no SSH/git creds, so `git push` goes through the host relay (`agentbox-ctl git push`). The host's **working tree is never written to** — only refs/objects under `.git/`.
17
+ - **`~/.claude`** — a Docker named volume (`agentbox-claude-config`, shared across boxes by default) seeded from the host's `~/.claude` on each create so auth, skills, and plugins persist without leaking the host's home dir.
18
+ - **`agentbox.yaml`** — read by `agentbox-ctl` from `/workspace`. Tasks and services declared here are what the supervisor will run.
19
+
20
+ ## Goal
21
+
22
+ Produce a `/workspace/agentbox.yaml` that captures this project's services, tasks, and box defaults so the in-box supervisor (`agentbox-ctl`) can boot the workspace deterministically.
9
23
 
10
24
  `agentbox.yaml` is **declarative**. The supervisor reads it on box start, but you don't have to restart the box: after you write the file, `agentbox-ctl reload` (run from inside the box) makes the already-running supervisor re-read it and immediately run the declared tasks and autostart the services. See step 8.
11
25
 
@@ -23,10 +37,12 @@ Look at `/workspace`:
23
37
  ## 2. Pick services and tasks
24
38
 
25
39
  - **Services** = long-running. Web servers, watchers, queue workers, databases. `restart: on-failure` by default.
26
- - **Tasks** = one-shot. `pnpm install`, DB migrations, codegen, fixture loaders. Wire dependent services with `needs:` so they wait for the task to finish successfully.
40
+ - **Tasks** = one-shot. `pnpm install`, DB migrations, codegen, fixture loaders, install apt packages. Wire dependent services with `needs:` so they wait for the task to finish successfully.
27
41
  - Names: must match `[A-Za-z0-9_-]+`. Task names and service names share a namespace — no collisions.
28
42
  - No cycles in `needs:`.
29
- - **Always generate a dependency-install task** and make it the root of the `needs:` graph (every service that needs deps gets `needs: [install, …]`). The box runs on Linux; the host's `node_modules` (and `.next`, `target`, `.venv`) are macOS-native and now live in the box's writable upper layer, so they must be rebuilt **inside** the box. 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, or a stale host-leaked `node_modules`), and be a fast no-op once it exists. Detect the package manager from the lockfile — never hardcode `pnpm`. See the worked example below.
43
+ - **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.
44
+ - **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.
45
+ -
30
46
 
31
47
  ## 3. Wire readiness probes (services only)
32
48
 
@@ -61,7 +77,7 @@ Per service:
61
77
 
62
78
  Sets per-project defaults for `agentbox create`/`claude`/`code`/`shell` — same shape as `~/.agentbox/config.yaml`. CLI flags still override. Common keys:
63
79
 
64
- - `box.hostSnapshot` (bool) — frozen APFS clone of the *host* workspace as overlay lower (renamed from `box.snapshot`).
80
+ - `box.hostSnapshot` (bool) — APFS-clone the *host* workspace into a per-box scratch dir before seeding `/workspace` (stabilizes the tar-pipe source).
65
81
  - `box.defaultCheckpoint` (string) — checkpoint new boxes start from (normally you set this via `agentbox-ctl checkpoint --set-default` at the end of setup — see section 9, not by hand).
66
82
  - `box.withPlaywright` (bool) — install `@playwright/cli` globally inside the box.
67
83
  - `box.vnc` (bool) — run Xvnc + noVNC on container port 6080.
@@ -76,6 +92,11 @@ Full key list (run on the host): `agentbox config list --keys`.
76
92
 
77
93
  ```yaml
78
94
  # yaml-language-server: $schema=https://agentbox.dev/schema/agentbox.schema.json
95
+ # This agentbox.yaml setup this Next.js project, and includes:
96
+ # - a postgres database because it's used in the project
97
+ # - an inngest server for queues
98
+ # - a fix to move .turbo/cache folder to the workspace to avoid a permission error during setup
99
+ # - ...
79
100
  defaults:
80
101
  box:
81
102
  withPlaywright: true
@@ -83,30 +104,23 @@ defaults:
83
104
  ide: cursor
84
105
 
85
106
  tasks:
86
- # Idempotent install. node_modules lives in the box's writable upper layer
87
- # (per-box, isolated, captured by `agentbox open --upper`). The host's
88
- # node_modules is macOS-native, so force a clean Linux build the first time
89
- # and self-heal a stale one but skip on every subsequent box start
90
- # (agentbox-ctl re-runs pending tasks after stop/start). Adjust the
91
- # lockfile detection to the project's package manager.
107
+ # Idempotent install. /workspace is the container's writable filesystem, so
108
+ # node_modules persists across pause/stop/start and is captured by
109
+ # `agentbox checkpoint`. The host's node_modules is macOS-native and is
110
+ # never copied in, so force a clean Linux build the first time but skip
111
+ # on every subsequent box start (agentbox-ctl re-runs pending tasks after
112
+ # stop/start). Adjust the lockfile detection to the project's package
113
+ # manager.
92
114
  install:
93
115
  command: |
94
116
  set -e
95
117
  MARKER=node_modules/.agentbox-installed
96
118
  [ -f "$MARKER" ] && { echo "deps installed (marker present) — skip"; exit 0; }
119
+ apt-get update && apt-get install -y postgresql-client
97
120
  rm -rf node_modules
98
121
  if [ -f pnpm-lock.yaml ]; then
99
122
  corepack enable >/dev/null 2>&1 || true
100
123
  pnpm install --frozen-lockfile || pnpm install
101
- elif [ -f yarn.lock ]; then
102
- corepack enable >/dev/null 2>&1 || true
103
- yarn install --frozen-lockfile || yarn install
104
- elif [ -f bun.lockb ] || [ -f bun.lock ]; then
105
- bun install
106
- elif [ -f package-lock.json ]; then
107
- npm ci || npm install
108
- else
109
- npm install
110
124
  fi
111
125
  touch "$MARKER"
112
126
 
@@ -144,49 +158,32 @@ services:
144
158
  - A service with `restart: never` and an autostart dependency will block the dependent forever after one failed run — usually a mistake.
145
159
  - `command:` is either a shell string (run via `bash -c`) or an argv array. Use the argv form if you need to avoid shell quoting.
146
160
 
147
- ## 8. Hand-off
148
-
149
- 1. Write the file to `/workspace/agentbox.yaml`.
150
- 2. **Apply it live**: from inside the box run `agentbox-ctl reload`. The already-running supervisor re-reads the config and immediately runs the declared tasks and autostarts the services — no box restart needed. It prints the `added` / `removed` / `changed` diff. If it errors because the daemon isn't running, the config is still valid: the next `agentbox start` (or `agentbox create` in this workspace) picks it up automatically.
151
- 3. Confirm with `agentbox-ctl status`: tasks should be `running` or `done`, autostart services `starting` or `ready`. If something failed, tail it with `agentbox-ctl logs <service>` and fix the config, then `agentbox-ctl reload` again.
152
-
153
- 4. **Then do section 9** — once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --set-default` so future boxes start ready. This is the real final step; don't stop at hand-off.
154
-
155
- 5. Tell the user:
161
+ ## 8. Checkpoint the warm state (do this at the very end)
156
162
 
157
- > I wrote `/workspace/agentbox.yaml` and ran `agentbox-ctl reload` so the supervisor is already running the declared tasks/services. To land the file on the host:
158
- > - I've created a checkpoint of the warm box state so future boxes start ready in seconds, no reinstall.
159
- > - commit it inside the box (`git add agentbox.yaml && git commit -m 'add agentbox config'`) the box's `.git/` is bind-mounted, so the commit shows up on the host immediately; or
160
- > - on the host, tell the user to run `agentbox pull config` to update their original host workspace.
163
+ 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.
164
+ 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.
165
+ This doesn't need to be confirmed by the user.
166
+ It will pause the container for several seconds so warn the user about it and write Done when it's done.
161
167
 
168
+ ## 9. Hand-off
162
169
 
163
- ## 9. Checkpoint the warm state (do this at the very end)
170
+ Tell the user (verbatim):
164
171
 
165
- Once `agentbox-ctl status` shows the install task `done` and services `ready` — i.e. the box is fully warmed up — capture a **checkpoint** so future boxes in this project start from this exact state instead of cold.
172
+ ```
173
+ █████╗ ██████╗ ███████╗███╗ ██╗████████╗██████╗ ██████╗ ██╗ ██╗
174
+ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██╔══██╗██╔═══██╗╚██╗██╔╝
175
+ ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██████╔╝██║ ██║ ╚███╔╝
176
+ ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██╔══██╗██║ ██║ ██╔██╗
177
+ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ██████╔╝╚██████╔╝██╔╝ ██╗
178
+ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝
179
+ ```
166
180
 
167
- A checkpoint snapshots the box's writable layer, which includes everything you just did:
168
-
169
- - `node_modules` (and `.next`, `target`, `.venv`, build caches) — the expensive Linux-native install,
170
- - `.env` / `.env.*` / `secrets.toml` and any other gitignored config files present in `/workspace`,
171
- - any other files written during setup.
172
-
173
- A new box created from the checkpoint reuses all of it (the install task's marker is already present, so it no-ops), while git-tracked code still comes fresh from the host's current HEAD. Result: new boxes are ready in seconds with no reinstall and no missing env vars.
174
-
175
- **Run this from inside the box, as the final step:**
176
-
177
- ```sh
178
- agentbox-ctl checkpoint --set-default
179
- ```
180
-
181
- This routes through the host relay (no host shell needed), captures `<box-name>-<n>`, and writes `box.defaultCheckpoint` into the project's config so **every future `agentbox create` / `claude` in this project starts warm automatically**. Pass `--name <name>` for a stable name, or omit `--set-default` to capture without changing the default. Re-run it whenever the warm state meaningfully changes (e.g. after a dependency bump you want every new box to inherit).
182
-
183
- Then tell the user, e.g.:
184
-
185
- > I checkpointed the warm box state (node_modules, env files, build caches) and set it as this project's default — `agentbox create` will now spin up new boxes from it in seconds, no reinstall.
181
+ your box is ready, you can start more sessions with `agentbox claude`
186
182
 
187
183
  ## 10. Known issues
188
184
 
189
185
  - For Nextjs/Vite/Tasnstack projects, makes sure to forward also websocket for hot reload.
190
- - **Turbo/Nx/Jest and other git-worktree-aware tools may try to write their cache to a read-only host path.** The box runs `/workspace` as a git worktree with the host repo's `.git/` bind-mounted at its absolute host path; tools that derive paths from git (Turbo's cache root = the worktree's git *common dir*) resolve outside `/workspace` and hit `EROFS` on the Mac path. Pin the cache into the writable overlay via the task/service `env:`, e.g. `TURBO_CACHE_DIR: /workspace/.turbo/cache` (or pass `--cache-dir`).
186
+
191
187
  - The `install` task is intentionally a no-op once `node_modules/.agentbox-installed` exists. Do **not** remove the marker guard to "force a fresh install" — that reinstalls on every box start. To force a one-off rebuild, delete `node_modules` (or just the marker) then run `agentbox-ctl reload`.
192
- - `.pnpm-store` default location is read only, so you need to point it to a writable location in the box's writable workspace eg: `PNPM_STORE: /workspace/.pnpm-store` and add `.pnpm-store/` to the `.gitignore` if the workspace root is git-tracked.
188
+
189
+ - Host-only CLI wrappers (portless, etc.) must be bypassed, eg some projects wrap the dev server with a host-side proxy (here: `portless projectname next dev --turbopack`). Override the service command: to call the underlying tool directly (`next dev --turbopack`)