@madarco/agentbox 0.15.0 → 0.16.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 (65) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/{_cloud-attach-R6TRWG5L.js → _cloud-attach-5KJWOASL.js} +4 -4
  3. package/dist/{chunk-RSKG7AFU.js → chunk-3WCEB6RE.js} +2 -2
  4. package/dist/{chunk-XKH7NTT7.js → chunk-DBBUDKKB.js} +248 -5
  5. package/dist/chunk-DBBUDKKB.js.map +1 -0
  6. package/dist/{chunk-MLMFNN4T.js → chunk-GXJNJUEV.js} +688 -197
  7. package/dist/chunk-GXJNJUEV.js.map +1 -0
  8. package/dist/{chunk-MCOU6CZS.js → chunk-NW2UZQV6.js} +10 -6
  9. package/dist/chunk-NW2UZQV6.js.map +1 -0
  10. package/dist/{chunk-E7CHS7ZR.js → chunk-PIK47622.js} +18 -6
  11. package/dist/chunk-PIK47622.js.map +1 -0
  12. package/dist/{chunk-BKU34KYY.js → chunk-QXFNLKJJ.js} +9 -3
  13. package/dist/{chunk-BKU34KYY.js.map → chunk-QXFNLKJJ.js.map} +1 -1
  14. package/dist/{chunk-43Q5GWP6.js → chunk-SB4QTF2T.js} +7 -7
  15. package/dist/{chunk-72CJTXN6.js → chunk-SENASAU4.js} +10 -6
  16. package/dist/{chunk-72CJTXN6.js.map → chunk-SENASAU4.js.map} +1 -1
  17. package/dist/{dist-JZ3XO6EB.js → dist-4IQFJJQI.js} +5 -5
  18. package/dist/{dist-OGJGZETZ.js → dist-7YB7BMNG.js} +5 -5
  19. package/dist/{dist-FIFEFKJ7.js → dist-SL2QSMBE.js} +5 -5
  20. package/dist/{dist-AGTIA7AD.js → dist-VHI5QOSQ.js} +6 -6
  21. package/dist/{dist-S4XR4ACV.js → dist-XC47DSCR.js} +5 -5
  22. package/dist/index.js +210 -68
  23. package/dist/index.js.map +1 -1
  24. package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js → prepared-state-MQHD3M5F-2LANTRL7.js} +2 -2
  25. package/package.json +5 -4
  26. package/runtime/docker/Dockerfile.box +21 -2
  27. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +82 -29
  28. package/runtime/docker/packages/ctl/dist/bin.cjs +10675 -9191
  29. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup +5 -2
  30. package/runtime/docker/packages/sandbox-docker/scripts/linear-shim +181 -0
  31. package/runtime/docker/packages/sandbox-docker/scripts/ntn-shim +95 -0
  32. package/runtime/e2b/agentbox-checkpoint-cleanup +5 -2
  33. package/runtime/e2b/agentbox-setup-skill.md +82 -29
  34. package/runtime/e2b/ctl.cjs +10675 -9191
  35. package/runtime/e2b/linear-shim +181 -0
  36. package/runtime/e2b/ntn-shim +95 -0
  37. package/runtime/e2b/scripts/build-template.sh +13 -7
  38. package/runtime/hetzner/agentbox-checkpoint-cleanup +5 -2
  39. package/runtime/hetzner/agentbox-setup-skill.md +82 -29
  40. package/runtime/hetzner/ctl.cjs +10675 -9191
  41. package/runtime/hetzner/linear-shim +181 -0
  42. package/runtime/hetzner/ntn-shim +95 -0
  43. package/runtime/hetzner/scripts/install-box.sh +19 -9
  44. package/runtime/relay/bin.cjs +3696 -2895
  45. package/runtime/vercel/agentbox-checkpoint-cleanup +5 -2
  46. package/runtime/vercel/agentbox-setup-skill.md +82 -29
  47. package/runtime/vercel/ctl.cjs +10675 -9191
  48. package/runtime/vercel/linear-shim +181 -0
  49. package/runtime/vercel/ntn-shim +95 -0
  50. package/runtime/vercel/scripts/provision.sh +13 -7
  51. package/share/agentbox-setup/SKILL.md +82 -29
  52. package/share/host-skills/agentbox-info/SKILL.md +1 -1
  53. package/dist/chunk-E7CHS7ZR.js.map +0 -1
  54. package/dist/chunk-MCOU6CZS.js.map +0 -1
  55. package/dist/chunk-MLMFNN4T.js.map +0 -1
  56. package/dist/chunk-XKH7NTT7.js.map +0 -1
  57. /package/dist/{_cloud-attach-R6TRWG5L.js.map → _cloud-attach-5KJWOASL.js.map} +0 -0
  58. /package/dist/{chunk-RSKG7AFU.js.map → chunk-3WCEB6RE.js.map} +0 -0
  59. /package/dist/{chunk-43Q5GWP6.js.map → chunk-SB4QTF2T.js.map} +0 -0
  60. /package/dist/{dist-JZ3XO6EB.js.map → dist-4IQFJJQI.js.map} +0 -0
  61. /package/dist/{dist-OGJGZETZ.js.map → dist-7YB7BMNG.js.map} +0 -0
  62. /package/dist/{dist-FIFEFKJ7.js.map → dist-SL2QSMBE.js.map} +0 -0
  63. /package/dist/{dist-AGTIA7AD.js.map → dist-VHI5QOSQ.js.map} +0 -0
  64. /package/dist/{dist-S4XR4ACV.js.map → dist-XC47DSCR.js.map} +0 -0
  65. /package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js.map → prepared-state-MQHD3M5F-2LANTRL7.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  readPreparedDockerState,
7
7
  resolveContextFiles,
8
8
  writePreparedDockerState
9
- } from "./chunk-XKH7NTT7.js";
9
+ } from "./chunk-DBBUDKKB.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-Q27AZU53.js.map
18
+ //# sourceMappingURL=prepared-state-MQHD3M5F-2LANTRL7.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madarco/agentbox",
3
- "version": "0.15.0",
3
+ "version": "0.16.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",
@@ -62,13 +62,14 @@
62
62
  "@agentbox/config": "0.0.0",
63
63
  "@agentbox/core": "0.0.0",
64
64
  "@agentbox/ctl": "0.0.0",
65
- "@agentbox/sandbox-cloud": "0.0.0",
66
65
  "@agentbox/relay": "0.0.0",
66
+ "@agentbox/integrations": "0.0.0",
67
+ "@agentbox/sandbox-cloud": "0.0.0",
68
+ "@agentbox/sandbox-daytona": "0.0.0",
67
69
  "@agentbox/sandbox-core": "0.0.0",
68
70
  "@agentbox/sandbox-docker": "0.0.0",
69
- "@agentbox/sandbox-daytona": "0.0.0",
70
- "@agentbox/sandbox-hetzner": "0.0.0",
71
71
  "@agentbox/sandbox-e2b": "0.0.0",
72
+ "@agentbox/sandbox-hetzner": "0.0.0",
72
73
  "@agentbox/sandbox-vercel": "0.0.0"
73
74
  },
74
75
  "scripts": {
@@ -72,9 +72,9 @@ RUN apt-get update \
72
72
  vim \
73
73
  libcap2-bin \
74
74
  && rm -rf /var/lib/apt/lists/* \
75
- && mkdir -p /workspace /run/agentbox /var/log/agentbox \
75
+ && mkdir -p /workspace /run/agentbox /var/log/agentbox /var/lib/agentbox \
76
76
  && chmod 755 /workspace \
77
- && chown vscode:vscode /workspace /run/agentbox /var/log/agentbox
77
+ && chown vscode:vscode /workspace /run/agentbox /var/log/agentbox /var/lib/agentbox
78
78
 
79
79
  # The in-box supervisor (runs as non-root `vscode`) owns a TCP forwarder that
80
80
  # binds container :80 -> the `expose:`-flagged service (see WebProxy /
@@ -154,6 +154,25 @@ COPY packages/sandbox-docker/scripts/gh-shim /usr/local/bin/gh
154
154
  COPY packages/sandbox-docker/scripts/git-shim /usr/local/bin/git
155
155
  RUN chmod +x /usr/local/bin/gh /usr/local/bin/git
156
156
 
157
+ # `ntn` (Notion CLI) shim — same shape as gh-shim, routes a strict subset
158
+ # of `ntn` subcommands through the host relay (the host's `ntn` runs the
159
+ # call; the box never sees the Notion token). Symlinked as `notion` per
160
+ # docs/integrations_backlog.md's per-service surface naming. Disabled by
161
+ # default; flip `integrations.notion.enabled` to enable. See
162
+ # packages/sandbox-docker/scripts/ntn-shim and docs/notion_backlog.md.
163
+ COPY packages/sandbox-docker/scripts/ntn-shim /usr/local/bin/ntn
164
+ RUN chmod +x /usr/local/bin/ntn && ln -s /usr/local/bin/ntn /usr/local/bin/notion
165
+
166
+ # `linear` (Linear CLI — @schpet/linear-cli) shim — same shape as ntn-shim,
167
+ # routes a strict subset of `linear` subcommands through the host relay
168
+ # (the host's `linear` runs the call; the box never sees the Linear API
169
+ # token). `linear auth token` (which would print the raw token to stdout)
170
+ # is explicitly rejected by the shim. Disabled by default; flip
171
+ # `integrations.linear.enabled` to enable. See
172
+ # packages/sandbox-docker/scripts/linear-shim and docs/linear_backlog.md.
173
+ COPY packages/sandbox-docker/scripts/linear-shim /usr/local/bin/linear
174
+ RUN chmod +x /usr/local/bin/linear
175
+
157
176
  # Setup guide for the first-run wizard. This baked copy is the single source
158
177
  # of the /agentbox-setup skill: seedSetupSkillIntoVolume()
159
178
  # (packages/sandbox-docker/src/claude.ts) copies it into the box's
@@ -46,35 +46,56 @@ Look at `/workspace`:
46
46
  - **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.
47
47
  - Names: must match `[A-Za-z0-9_-]+`. Task names and service names share a namespace — no collisions.
48
48
  - No cycles in `needs:`.
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.
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**: `agentbox-ctl` re-runs pending tasks on every box stop/start (the daemon dies with the container and is relaunched), so an unguarded install would reinstall on every start. The clean way is the **`run_once: true`** field — the supervisor stores a marker keyed by a hash of the command and skips warm boots automatically (the marker lives at `/var/lib/agentbox/tasks/<name>`, on the box rootfs, captured by checkpoints, never polluting `/workspace`). Editing the command re-runs it. 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
52
  ### Stateful services: data persistence & re-seeding (read this for databases)
53
53
 
54
+ **Declare a containerized dependency with the `image:` service form** — AgentBox
55
+ generates the `docker start`-or-`run` shell (no hand-written `docker run … || docker
56
+ start …`). The container runs in the box's dockerd; a published port is reachable
57
+ from other in-box services at `127.0.0.1:<host port>`:
58
+
59
+ ```yaml
60
+ services:
61
+ postgres:
62
+ image: # bare string (image: postgres:17-alpine) or a mapping:
63
+ name: postgres:17-alpine
64
+ ports: ["5432:5432"]
65
+ env:
66
+ POSTGRES_PASSWORD: postgres
67
+ POSTGRES_DB: app
68
+ args: "-c max_connections=200" # string or ["-c","max_connections=200"]
69
+ container_name: app_db # optional; default = service name
70
+ ready_when: { port: 5432 }
71
+ restart: always
72
+ ```
73
+
74
+ The container is reused by name across box stop/start. (Changing `image`/`env`
75
+ reuses the existing container as-is; `docker rm <container_name>` + `agentbox-ctl
76
+ reload` to apply.) Install the DB client the migrate/seed tasks need (e.g.
77
+ `postgresql-client`) in the `install` task and reach the DB over TCP — don't
78
+ `docker exec` the container (nested exec fails with a `setns` error in a box).
79
+
54
80
  **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
81
 
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:
82
+ **Consequence for migrate/seed tasks of a containerized DB: do NOT use `run_once: true` (the marker form).** A command-hash marker 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 use the **`run_once: { check: <cmd> }`** form — the probe runs first and the seed runs unless the probe exits 0, and **no marker is written** (the DB is the source of truth). Gate on the actual data:
57
83
 
58
84
  ```yaml
59
85
  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
86
+ # Re-seed when the DB is empty. The postgres data lives in the in-box docker
87
+ # volume, which is NOT captured by `agentbox checkpoint` — so a box started
88
+ # from a checkpoint has the workspace warm but an empty DB. The marker form
89
+ # would be restored while the DB is blank and wrongly skip; the `check` probe
90
+ # gates on the data itself. Exit 0 = already seeded, skip. Fast no-op once
65
91
  # 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
92
+ command: pnpm db:seed
77
93
  needs: [install, migrate]
94
+ run_once:
95
+ check: |
96
+ export PGPASSWORD=postgres
97
+ psql -h 127.0.0.1 -p 5432 -U postgres -d app -tAc \
98
+ "SELECT EXISTS (SELECT 1 FROM users LIMIT 1)" 2>/dev/null | grep -q t
78
99
  ```
79
100
 
80
101
  **Lifecycle nuance (this is why the data check, not a marker, is right):**
@@ -148,22 +169,19 @@ tasks:
148
169
  # Idempotent install. /workspace is the container's writable filesystem, so
149
170
  # node_modules persists across pause/stop/start and is captured by
150
171
  # `agentbox checkpoint`. The host's node_modules is macOS-native and is
151
- # never copied in, so force a clean Linux build the first time — but skip
152
- # on every subsequent box start (agentbox-ctl re-runs pending tasks after
153
- # stop/start). Adjust the lockfile detection to the project's package
154
- # manager.
172
+ # never copied in, so the first Linux install runs; `run_once: true` then
173
+ # skips it on every subsequent box start (the supervisor stores a marker
174
+ # keyed by a hash of the command). Adjust the lockfile detection to the
175
+ # project's package manager.
155
176
  install:
156
177
  command: |
157
178
  set -e
158
- MARKER=node_modules/.agentbox-installed
159
- [ -f "$MARKER" ] && { echo "deps installed (marker present) — skip"; exit 0; }
160
- apt-get update && apt-get install -y postgresql-client
161
- rm -rf node_modules
179
+ sudo apt-get update && sudo apt-get install -y postgresql-client
162
180
  if [ -f pnpm-lock.yaml ]; then
163
181
  corepack enable >/dev/null 2>&1 || true
164
182
  pnpm install --frozen-lockfile || pnpm install
165
183
  fi
166
- touch "$MARKER"
184
+ run_once: true
167
185
 
168
186
  migrate:
169
187
  command: pnpm db:migrate
@@ -258,6 +276,41 @@ On Vercel: this actually STOPS the sandbox, so warn the user about it. Also the
258
276
 
259
277
  - For Nextjs/Vite/Tasnstack projects, makes sure to forward also websocket for hot reload.
260
278
 
261
- - Service like flask, nextjs, BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL should use the <boxname>.localhost url for the local development so that on the host it will use the same url as the box.
279
+ - Service like flask, nextjs, BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL should use the `<boxname>.localhost` url for the local development so that on the host it will use the same url as the box. Render this automatically instead of hand-writing `sed` — see section 6c.
280
+
281
+ - The `install` task above uses `run_once: true`, so it is a no-op on warm boots. Do **not** wrap it in a manual marker check too. To force a one-off rebuild, run `agentbox-ctl run-task install --force` (which bypasses the run_once marker), or edit the command (a changed command invalidates the hash and re-runs).
282
+
283
+ ## 11. Pin URLs / render config files (env, secrets)
284
+
285
+ Many apps hard-code a hostname (e.g. `optima.localhost`) or read a gitignored `.env`. Instead of long `sed` commands in a task, use the built-ins:
286
+
287
+ - **`agentbox-ctl render <src>`** — a declarative `sed` for files already in the workspace. `--env` substitutes `{{AGENTBOX_*}}` placeholders; `--rules <name>` applies a named rule-set from the top-level `replacements:` block; `--rule 'from=>to'` / `--rule-regex 'pat=>repl'` are inline. Write to `--out <path>` (or `--in-place`). The whitelist placeholders are `{{AGENTBOX_BOX_NAME}}`, `{{AGENTBOX_BOX_HOST}}` (= `<boxname>.localhost`), `{{AGENTBOX_BOX_ID}}`, `{{AGENTBOX_BOX_KIND}}`, `{{AGENTBOX_HOST_WORKSPACE}}`, `{{AGENTBOX_PROJECT_ROOT}}`.
288
+
289
+ Render a gitignored `.env` from a committed `env.example` on every boot, pinning the URLs to this box:
290
+
291
+ ```yaml
292
+ replacements:
293
+ box-host:
294
+ - { from: 'optima\.localhost', to: '{{AGENTBOX_BOX_HOST}}', regex: true } # {{AGENTBOX_BOX_HOST}} = <box>.localhost
295
+
296
+ tasks:
297
+ env:
298
+ # The render is idempotent (the rules re-pin the same lines every boot), so
299
+ # no `run_once:` guard is needed — it self-corrects on a checkpoint-started
300
+ # box that carries a different box's host in .env.
301
+ command: agentbox-ctl render apps/saas/env.example --out apps/saas/.env --env --rules box-host
302
+ ```
303
+
304
+ Note: an `run_once: { check: <cmd> }` probe runs verbatim via `bash -c` with the box env — use shell vars like `$AGENTBOX_BOX_NAME`, NOT `{{…}}` placeholders (those are only expanded by `render`/carry, never by the supervisor).
305
+
306
+ **Generated secrets:** put `{{AGENTBOX_AUTO_SECRET}}` in the template for a value like `BETTER_AUTH_SECRET` instead of shelling out to `openssl rand`. Unnamed → a fresh 32-byte base64url secret each render (stable when you render the template→`.env` once). `{{AGENTBOX_AUTO_SECRET:better-auth}}` → generated once, persisted at `/var/lib/agentbox/secrets/<name>`, reused on every render (stable even if you render every boot). Example `env.example` line: `BETTER_AUTH_SECRET="{{AGENTBOX_AUTO_SECRET:better-auth}}"`.
307
+
308
+ - **`carry:` + `replaceEnvs`/`replace`/`rules`** — for a host-only file (e.g. a real `.env` with secrets that never lives in the repo), carry it in and render it host-side in one step (file entries only):
262
309
 
263
- - 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`.
310
+ ```yaml
311
+ carry:
312
+ - src: ~/secrets/optima.env
313
+ dest: /workspace/apps/saas/.env
314
+ replaceEnvs: true
315
+ rules: [box-host]
316
+ ```