@madarco/agentbox 0.1.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.
- package/LICENSE +21 -0
- package/dist/chunk-IDR4HVIC.js +106 -0
- package/dist/chunk-IDR4HVIC.js.map +1 -0
- package/dist/chunk-J35IH7W5.js +200 -0
- package/dist/chunk-J35IH7W5.js.map +1 -0
- package/dist/chunk-O5HS3QHW.js +2164 -0
- package/dist/chunk-O5HS3QHW.js.map +1 -0
- package/dist/chunk-OOOKFFR5.js +496 -0
- package/dist/chunk-OOOKFFR5.js.map +1 -0
- package/dist/chunk-RWJE6AER.js +515 -0
- package/dist/chunk-RWJE6AER.js.map +1 -0
- package/dist/chunk-SOMIKEN2.js +1651 -0
- package/dist/chunk-SOMIKEN2.js.map +1 -0
- package/dist/create-LSSO7H4I-GWNALUMF.js +15 -0
- package/dist/create-LSSO7H4I-GWNALUMF.js.map +1 -0
- package/dist/index.js +4067 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle-P4FSKGR2-3466P54Y.js +38 -0
- package/dist/lifecycle-P4FSKGR2-3466P54Y.js.map +1 -0
- package/dist/state-ZSP3ORXW-WI6KOIG3.js +26 -0
- package/dist/state-ZSP3ORXW-WI6KOIG3.js.map +1 -0
- package/dist/stats-GZFLPYTU-DBJ2DVBJ.js +19 -0
- package/dist/stats-GZFLPYTU-DBJ2DVBJ.js.map +1 -0
- package/package.json +73 -0
- package/runtime/docker/Dockerfile.box +316 -0
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +188 -0
- package/runtime/docker/packages/ctl/dist/bin.cjs +12770 -0
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-dockerd-start +52 -0
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +77 -0
- package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +54 -0
- package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +21 -0
- package/runtime/relay/bin.cjs +11467 -0
- package/share/agentbox-setup/SKILL.md +188 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AmbiguousBoxError,
|
|
4
|
+
BoxNotFoundError,
|
|
5
|
+
destroyBox,
|
|
6
|
+
getBoxHostPaths,
|
|
7
|
+
inspectBox,
|
|
8
|
+
listBoxes,
|
|
9
|
+
openBoxInFinder,
|
|
10
|
+
pauseBox,
|
|
11
|
+
pruneBoxes,
|
|
12
|
+
snapshotPresent,
|
|
13
|
+
startBox,
|
|
14
|
+
stopBox,
|
|
15
|
+
unpauseBox
|
|
16
|
+
} from "./chunk-RWJE6AER.js";
|
|
17
|
+
import {
|
|
18
|
+
SNAPSHOTS_ROOT
|
|
19
|
+
} from "./chunk-O5HS3QHW.js";
|
|
20
|
+
import "./chunk-IDR4HVIC.js";
|
|
21
|
+
import "./chunk-SOMIKEN2.js";
|
|
22
|
+
export {
|
|
23
|
+
AmbiguousBoxError,
|
|
24
|
+
BoxNotFoundError,
|
|
25
|
+
SNAPSHOTS_ROOT,
|
|
26
|
+
destroyBox,
|
|
27
|
+
getBoxHostPaths,
|
|
28
|
+
inspectBox,
|
|
29
|
+
listBoxes,
|
|
30
|
+
openBoxInFinder,
|
|
31
|
+
pauseBox,
|
|
32
|
+
pruneBoxes,
|
|
33
|
+
snapshotPresent,
|
|
34
|
+
startBox,
|
|
35
|
+
stopBox,
|
|
36
|
+
unpauseBox
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=lifecycle-P4FSKGR2-3466P54Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
STATE_DIR,
|
|
4
|
+
STATE_FILE,
|
|
5
|
+
allocateProjectIndex,
|
|
6
|
+
autoPickProjectBox,
|
|
7
|
+
findBox,
|
|
8
|
+
readState,
|
|
9
|
+
recordBox,
|
|
10
|
+
removeBoxRecord,
|
|
11
|
+
resolveBoxRef,
|
|
12
|
+
writeState
|
|
13
|
+
} from "./chunk-IDR4HVIC.js";
|
|
14
|
+
export {
|
|
15
|
+
STATE_DIR,
|
|
16
|
+
STATE_FILE,
|
|
17
|
+
allocateProjectIndex,
|
|
18
|
+
autoPickProjectBox,
|
|
19
|
+
findBox,
|
|
20
|
+
readState,
|
|
21
|
+
recordBox,
|
|
22
|
+
removeBoxRecord,
|
|
23
|
+
resolveBoxRef,
|
|
24
|
+
writeState
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=state-ZSP3ORXW-WI6KOIG3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
agentboxHomeBytes,
|
|
4
|
+
allCheckpointVolumesBytes,
|
|
5
|
+
boxResourceStats,
|
|
6
|
+
parseDockerSize,
|
|
7
|
+
projectCheckpointVolumeBytes,
|
|
8
|
+
volumeSizeBytes
|
|
9
|
+
} from "./chunk-J35IH7W5.js";
|
|
10
|
+
import "./chunk-SOMIKEN2.js";
|
|
11
|
+
export {
|
|
12
|
+
agentboxHomeBytes,
|
|
13
|
+
allCheckpointVolumesBytes,
|
|
14
|
+
boxResourceStats,
|
|
15
|
+
parseDockerSize,
|
|
16
|
+
projectCheckpointVolumeBytes,
|
|
17
|
+
volumeSizeBytes
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=stats-GZFLPYTU-DBJ2DVBJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@madarco/agentbox",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Launch Claude Code, Codex, and other coding agents in isolated sandboxes",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Marco D'Alia",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20.10"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"agentbox": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude",
|
|
16
|
+
"claude-code",
|
|
17
|
+
"codex",
|
|
18
|
+
"coding-agent",
|
|
19
|
+
"sandbox",
|
|
20
|
+
"docker",
|
|
21
|
+
"cli",
|
|
22
|
+
"isolation",
|
|
23
|
+
"agentbox"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/madarco/agentbox.git",
|
|
28
|
+
"directory": "apps/cli"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/madarco/agentbox#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/madarco/agentbox/issues"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"share",
|
|
40
|
+
"runtime"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@clack/prompts": "^0.9.0",
|
|
44
|
+
"@xterm/headless": "^5.5.0",
|
|
45
|
+
"commander": "^12.1.0",
|
|
46
|
+
"execa": "^9.5.2",
|
|
47
|
+
"supports-hyperlinks": "^3.1.0",
|
|
48
|
+
"yaml": "^2.6.1"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"@homebridge/node-pty-prebuilt-multiarch": "^0.13.1"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^22.10.1",
|
|
55
|
+
"tsup": "^8.3.5",
|
|
56
|
+
"typescript": "^5.7.2",
|
|
57
|
+
"vitest": "^2.1.8",
|
|
58
|
+
"@agentbox/config": "0.0.0",
|
|
59
|
+
"@agentbox/core": "0.0.0",
|
|
60
|
+
"@agentbox/ctl": "0.0.0",
|
|
61
|
+
"@agentbox/sandbox-docker": "0.0.0"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsup",
|
|
65
|
+
"dev": "tsup --watch",
|
|
66
|
+
"lint": "eslint src test",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"typecheck": "tsc --noEmit",
|
|
69
|
+
"clean": "rm -rf dist runtime .turbo",
|
|
70
|
+
"publish:patch": "npm version patch && git push --follow-tags",
|
|
71
|
+
"publish:minor": "npm version minor && git push --follow-tags"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# AgentBox base box image.
|
|
2
|
+
#
|
|
3
|
+
# Layered on Microsoft's devcontainers base:ubuntu (arm64-native — the
|
|
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.
|
|
9
|
+
#
|
|
10
|
+
# Required runtime flags:
|
|
11
|
+
# --cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined
|
|
12
|
+
FROM mcr.microsoft.com/devcontainers/base:ubuntu
|
|
13
|
+
|
|
14
|
+
USER root
|
|
15
|
+
|
|
16
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
17
|
+
|
|
18
|
+
# COLORTERM is the de-facto signal TUI apps (including Claude Code) use to
|
|
19
|
+
# decide whether to emit 24-bit RGB. Without it, claude sees TERM=tmux-256color
|
|
20
|
+
# in panes and falls back to a 256-color palette, which renders the logo and
|
|
21
|
+
# gradients as dim monochrome blocks. tmux is configured to forward RGB to the
|
|
22
|
+
# outer terminal regardless; this just unlocks the in-pane side.
|
|
23
|
+
#
|
|
24
|
+
# DISABLE_AUTOUPDATER follows Anthropic's own devcontainer guidance: there's no
|
|
25
|
+
# point updating Claude Code inside a sandbox that's torn down regularly, and
|
|
26
|
+
# leaving it enabled triggers a "installMethod is native, but .local/bin
|
|
27
|
+
# missing" warning when the host's .claude.json (synced in) reports a native
|
|
28
|
+
# install method while the box uses npm-global.
|
|
29
|
+
#
|
|
30
|
+
# LANG/LC_ALL must be set via ENV (not /etc/environment, which is what the
|
|
31
|
+
# devcontainers base seeds): `docker exec` doesn't source login files, so
|
|
32
|
+
# without this every host-launched tmux/claude inherits an empty LANG, the
|
|
33
|
+
# active locale falls back to POSIX, and tmux 3.x treats the terminal as
|
|
34
|
+
# non-UTF-8 — Claude's rounded prompt box (`╭ ─ ╮ │ ╰ ╯`) renders as ASCII.
|
|
35
|
+
# en_US.UTF-8 is pre-generated by the base image, so no locale-gen needed.
|
|
36
|
+
ENV COLORTERM=truecolor \
|
|
37
|
+
DISABLE_AUTOUPDATER=1 \
|
|
38
|
+
LANG=en_US.UTF-8 \
|
|
39
|
+
LC_ALL=en_US.UTF-8 \
|
|
40
|
+
PATH=/home/vscode/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
41
|
+
|
|
42
|
+
# The PATH prepend above is load-bearing: Anthropic's native installer drops
|
|
43
|
+
# `claude` at /home/vscode/.local/bin/, which isn't in `docker exec`'s default
|
|
44
|
+
# PATH. Without this, the tmux session started by `startClaudeSession` can't
|
|
45
|
+
# resolve `claude` via /bin/sh -c and the session exits immediately.
|
|
46
|
+
|
|
47
|
+
RUN apt-get update \
|
|
48
|
+
&& apt-get install -y --no-install-recommends \
|
|
49
|
+
curl ca-certificates gnupg \
|
|
50
|
+
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
|
51
|
+
&& apt-get install -y --no-install-recommends \
|
|
52
|
+
fuse3 \
|
|
53
|
+
fuse-overlayfs \
|
|
54
|
+
rsync \
|
|
55
|
+
nodejs \
|
|
56
|
+
python3 \
|
|
57
|
+
python3-pip \
|
|
58
|
+
python3-venv \
|
|
59
|
+
build-essential \
|
|
60
|
+
git \
|
|
61
|
+
tmux \
|
|
62
|
+
libcap2-bin \
|
|
63
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
64
|
+
&& mkdir -p /workspace /host-src /upper /snapshot /run/agentbox /var/log/agentbox \
|
|
65
|
+
&& chmod 755 /workspace /host-src /upper /snapshot \
|
|
66
|
+
&& chown vscode:vscode /run/agentbox /var/log/agentbox
|
|
67
|
+
|
|
68
|
+
# The in-box supervisor (runs as non-root `vscode`) owns a TCP forwarder that
|
|
69
|
+
# binds container :80 -> the `expose:`-flagged service (see WebProxy /
|
|
70
|
+
# agentbox.yaml `expose:`). Binding a port <1024 needs CAP_NET_BIND_SERVICE;
|
|
71
|
+
# grant it to the node binary so the supervisor can listen on :80 without root.
|
|
72
|
+
# Broad (any in-box node can bind low ports) but acceptable for a single-tenant
|
|
73
|
+
# sandbox — strictly narrower than the existing NET_ADMIN/seccomp=unconfined.
|
|
74
|
+
RUN setcap cap_net_bind_service=+ep "$(readlink -f "$(command -v node)")"
|
|
75
|
+
|
|
76
|
+
# Host repos are bind-mounted in at their identical absolute path (worktree
|
|
77
|
+
# pointer files contain absolute paths to <main>/.git/worktrees/<name>, so both
|
|
78
|
+
# sides have to resolve the same path), and the host owns those `.git/` dirs.
|
|
79
|
+
# git's recent "dubious ownership" guard would refuse to operate without an
|
|
80
|
+
# explicit allowlist. /etc/gitconfig is system-wide and unaffected by the RO
|
|
81
|
+
# bind-mount of ~/.gitconfig from the host.
|
|
82
|
+
RUN git config --system --add safe.directory '*'
|
|
83
|
+
|
|
84
|
+
# Docker-in-Docker. dockerd inside the box (storage-driver=fuse-overlayfs, set
|
|
85
|
+
# in /etc/docker/daemon.json) lets the agent run `docker build`/`docker run`
|
|
86
|
+
# in its own namespace without exposing the host daemon. fuse-overlayfs is
|
|
87
|
+
# required because the kernel `overlay` driver isn't usable from an
|
|
88
|
+
# unprivileged container — but fuse3 + fuse-overlayfs are already installed
|
|
89
|
+
# above for the /workspace overlay, so docker reuses them. iptables is needed
|
|
90
|
+
# for inner-container bridge networking. Adding `vscode` to the `docker` group
|
|
91
|
+
# lets the agent invoke `docker` without sudo once dockerd creates the socket
|
|
92
|
+
# at /var/run/docker.sock at runtime. The matching launch script is COPY'd in
|
|
93
|
+
# below; the daemon is started by the host via `docker exec -d --user root`
|
|
94
|
+
# (see launchDockerdDaemon), mirroring the VNC/ctl daemon pattern.
|
|
95
|
+
RUN apt-get update \
|
|
96
|
+
&& apt-get install -y --no-install-recommends \
|
|
97
|
+
docker.io \
|
|
98
|
+
iptables \
|
|
99
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
100
|
+
&& mkdir -p /etc/docker \
|
|
101
|
+
&& printf '%s\n' '{ "storage-driver": "fuse-overlayfs", "iptables": true }' > /etc/docker/daemon.json \
|
|
102
|
+
&& usermod -aG docker vscode
|
|
103
|
+
|
|
104
|
+
# agentbox-ctl: the in-container supervisor + CLI. Build context is the
|
|
105
|
+
# monorepo root, so packages/ctl/dist is the prebuilt bundle from
|
|
106
|
+
# `pnpm --filter @agentbox/ctl build`. The CLI is bundled with all deps
|
|
107
|
+
# inlined (see tsup config), so a single COPY is enough.
|
|
108
|
+
COPY packages/ctl/dist/bin.cjs /usr/local/bin/agentbox-ctl
|
|
109
|
+
RUN chmod +x /usr/local/bin/agentbox-ctl
|
|
110
|
+
|
|
111
|
+
# Setup guide for the first-run wizard. The CLI also installs the same file as
|
|
112
|
+
# a host-side claude skill (~/.claude/skills/agentbox-setup/SKILL.md) the
|
|
113
|
+
# first time the wizard fires; the existing ~/.claude rsync flow propagates
|
|
114
|
+
# it into every box automatically. Keeping a copy baked into the image at a
|
|
115
|
+
# stable path guarantees the wizard's initial prompt can reference the guide
|
|
116
|
+
# even on hosts where the user wiped their ~/.claude/skills.
|
|
117
|
+
COPY apps/cli/share/agentbox-setup/SKILL.md /usr/local/share/agentbox/setup-guide.md
|
|
118
|
+
RUN chmod 0644 /usr/local/share/agentbox/setup-guide.md
|
|
119
|
+
|
|
120
|
+
# Prepare /home/vscode/.claude (the volume mount target) and the .claude.json
|
|
121
|
+
# symlink. The mkdir+chown is load-bearing: when we mount the named
|
|
122
|
+
# `agentbox-claude-config` volume at runtime, Docker seeds the empty volume's
|
|
123
|
+
# perms from this directory on first mount. Without the chown the volume comes
|
|
124
|
+
# up root-owned and Claude Code can't write.
|
|
125
|
+
#
|
|
126
|
+
# The symlink ~/.claude.json -> ~/.claude/_claude.json routes Claude's
|
|
127
|
+
# top-level state file (onboarding, anonymous id, plugin caches, oauth account
|
|
128
|
+
# stub) into the volume, so it persists across box rebuilds and gets refreshed
|
|
129
|
+
# from the host on every `agentbox claude` create. The target is initially
|
|
130
|
+
# dangling — it materializes when `ensureClaudeVolume` copies the host's
|
|
131
|
+
# ~/.claude.json into the volume.
|
|
132
|
+
RUN mkdir -p /home/vscode/.claude \
|
|
133
|
+
&& chown vscode:vscode /home/vscode/.claude \
|
|
134
|
+
&& ln -s /home/vscode/.claude/_claude.json /home/vscode/.claude.json \
|
|
135
|
+
&& chown -h vscode:vscode /home/vscode/.claude.json
|
|
136
|
+
|
|
137
|
+
# Prepare /home/vscode/.vscode-server and /home/vscode/.cursor-server (+ their
|
|
138
|
+
# extensions subdirs) so the named volumes mounted at runtime — per-box
|
|
139
|
+
# `agentbox-{vscode,cursor}-server-<id>` over the server dirs, then shared
|
|
140
|
+
# `agentbox-{vscode,cursor}-extensions` over the extensions subdirs — come up
|
|
141
|
+
# owned by vscode. Same load-bearing reason as the .claude block above: empty
|
|
142
|
+
# named volumes inherit the mount point's perms on first mount; without these
|
|
143
|
+
# chowns, the Dev Containers extension (in either VS Code or Cursor) fails to
|
|
144
|
+
# mkdir <server>/bin/<commit>/ during server install ("Permission denied").
|
|
145
|
+
RUN mkdir -p /home/vscode/.vscode-server/extensions /home/vscode/.cursor-server/extensions \
|
|
146
|
+
&& chown -R vscode:vscode /home/vscode/.vscode-server /home/vscode/.cursor-server
|
|
147
|
+
|
|
148
|
+
# Claude Code via Anthropic's native installer — the recommended path per
|
|
149
|
+
# code.claude.com/docs/en/setup (npm-global is under "Advanced installation"
|
|
150
|
+
# now, with a warning against `sudo npm install -g`). Pinned to the `stable`
|
|
151
|
+
# release channel so rebuilds get a predictable ~week-old release.
|
|
152
|
+
# DISABLE_AUTOUPDATER=1 (set above) prevents in-box self-updates. We run as
|
|
153
|
+
# `vscode` so the binary lands at /home/vscode/.local/bin/claude — the path
|
|
154
|
+
# the host's `.claude.json` (with installMethod=native) expects, so the in-box
|
|
155
|
+
# integrity check doesn't fire.
|
|
156
|
+
USER vscode
|
|
157
|
+
RUN curl -fsSL https://claude.ai/install.sh | bash -s stable
|
|
158
|
+
USER root
|
|
159
|
+
|
|
160
|
+
# Browser support for in-box agents: Vercel's agent-browser drives Chrome via
|
|
161
|
+
# CDP. Two things have to happen here:
|
|
162
|
+
#
|
|
163
|
+
# 1. Install Chrome's runtime libs (the well-known set Playwright's
|
|
164
|
+
# `install-deps` installs). Package names are pinned to Ubuntu 24.04
|
|
165
|
+
# (Noble) — the t64 suffix marks libs that flipped to 64-bit time_t in
|
|
166
|
+
# the noble transition. If the base image moves back to jammy these will
|
|
167
|
+
# need the un-suffixed names.
|
|
168
|
+
#
|
|
169
|
+
# 2. Get an actual Chromium binary. `agent-browser install` would normally
|
|
170
|
+
# download Chrome for Testing, but Chrome for Testing has no Linux ARM64
|
|
171
|
+
# build (Apple Silicon hosts run linux/arm64 containers) and Noble's
|
|
172
|
+
# `chromium-browser` apt package is a snap stub that won't run inside a
|
|
173
|
+
# container. The reliable cross-arch source is Playwright's bundled
|
|
174
|
+
# Chromium, so we install playwright globally just for its
|
|
175
|
+
# `playwright install chromium` downloader, then point agent-browser at
|
|
176
|
+
# the resulting binary via AGENT_BROWSER_EXECUTABLE_PATH. agent-browser
|
|
177
|
+
# honours that env var (priority: CLI flag > env > config file >
|
|
178
|
+
# built-in default; see agent-browser's README).
|
|
179
|
+
#
|
|
180
|
+
# Runtime state (sessions, auth cookies under ~/.agent-browser/) lives in the
|
|
181
|
+
# container's writable layer, preserved across pause/unpause/stop/start and
|
|
182
|
+
# wiped on `agentbox destroy`.
|
|
183
|
+
RUN apt-get update \
|
|
184
|
+
&& apt-get install -y --no-install-recommends \
|
|
185
|
+
libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 \
|
|
186
|
+
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
|
|
187
|
+
libgbm1 libdrm2 libpango-1.0-0 libcairo2 libasound2t64 \
|
|
188
|
+
fonts-liberation xdg-utils \
|
|
189
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
190
|
+
|
|
191
|
+
RUN npm install -g agent-browser playwright
|
|
192
|
+
|
|
193
|
+
# Download Chromium as `vscode` so the ms-playwright cache lands in vscode's
|
|
194
|
+
# home (the user agent-browser runs as). The downloaded binary lives at
|
|
195
|
+
# `chromium-XXXX/chrome-linux/chrome`, where XXXX is a Playwright-internal
|
|
196
|
+
# revision number that changes between releases — we resolve it once here and
|
|
197
|
+
# write the result to a stable symlink so AGENT_BROWSER_EXECUTABLE_PATH can
|
|
198
|
+
# point at something predictable.
|
|
199
|
+
USER vscode
|
|
200
|
+
RUN playwright install chromium \
|
|
201
|
+
&& ln -sf "$(ls /home/vscode/.cache/ms-playwright/chromium-*/chrome-linux/chrome | sort | tail -1)" /tmp/chromium-link \
|
|
202
|
+
&& test -x "$(readlink /tmp/chromium-link)"
|
|
203
|
+
USER root
|
|
204
|
+
RUN mv /tmp/chromium-link /usr/local/bin/chromium
|
|
205
|
+
|
|
206
|
+
ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/local/bin/chromium
|
|
207
|
+
|
|
208
|
+
# VNC: Xvnc (TigerVNC, X server + RFB in one binary), noVNC (HTML5 client),
|
|
209
|
+
# websockify (the WS<->RFB proxy noVNC needs). No window manager: a Chromium
|
|
210
|
+
# launched with DISPLAY=:1 fills the screen, and agents drive tabs through the
|
|
211
|
+
# browser itself. Adding a WM would cost another ~10MB and complicate the
|
|
212
|
+
# lifecycle for no win.
|
|
213
|
+
#
|
|
214
|
+
# autocutsel: with no WM, nothing owns the X11 CLIPBOARD/PRIMARY selections, so
|
|
215
|
+
# the text noVNC delivers via RFB cut-text isn't reliably served back to
|
|
216
|
+
# Chromium on Ctrl+V. autocutsel (one daemon per selection, started by
|
|
217
|
+
# agentbox-vnc-start) keeps both selections populated and in sync. xclip is
|
|
218
|
+
# kept alongside it purely for debugging the clipboard round-trip.
|
|
219
|
+
RUN apt-get update \
|
|
220
|
+
&& apt-get install -y --no-install-recommends \
|
|
221
|
+
tigervnc-standalone-server tigervnc-common tigervnc-tools \
|
|
222
|
+
novnc websockify \
|
|
223
|
+
autocutsel xclip \
|
|
224
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
225
|
+
|
|
226
|
+
# DISPLAY is image-baked so every `docker exec` (the claude tmux session,
|
|
227
|
+
# `agentbox shell`, any agent subprocess) inherits it. Anything launched
|
|
228
|
+
# without an explicit DISPLAY override renders to the VNC session.
|
|
229
|
+
ENV DISPLAY=:1
|
|
230
|
+
|
|
231
|
+
# EXPOSE 6080 is load-bearing for OrbStack. OrbStack maps
|
|
232
|
+
# `<container-name>.orb.local` to the container's single EXPOSE'd port; without
|
|
233
|
+
# this, the auto-DNS responds but nothing is routed. (Docker Desktop doesn't
|
|
234
|
+
# use EXPOSE for routing — it just needs the `-p` flag passed at `docker run`.)
|
|
235
|
+
EXPOSE 6080
|
|
236
|
+
# Web service port. The canonical host-reachable URL is the published loopback
|
|
237
|
+
# port (`-p 127.0.0.1:0:80`, resolved per box); EXPOSE 80 is only an OrbStack
|
|
238
|
+
# bare-domain bonus and is not load-bearing (OrbStack honors a single EXPOSE).
|
|
239
|
+
EXPOSE 80
|
|
240
|
+
|
|
241
|
+
# Pre-create the per-user .vnc dir so it's writable from the runtime user. The
|
|
242
|
+
# password file itself is written at container start by agentbox-vnc-start
|
|
243
|
+
# (derived from AGENTBOX_VNC_PASSWORD).
|
|
244
|
+
RUN mkdir -p /home/vscode/.vnc \
|
|
245
|
+
&& chown -R vscode:vscode /home/vscode/.vnc
|
|
246
|
+
COPY packages/sandbox-docker/scripts/agentbox-vnc-start /usr/local/bin/agentbox-vnc-start
|
|
247
|
+
RUN chmod +x /usr/local/bin/agentbox-vnc-start
|
|
248
|
+
|
|
249
|
+
# DinD launcher script. Same exec-d-as-root pattern as agentbox-vnc-start.
|
|
250
|
+
COPY packages/sandbox-docker/scripts/agentbox-dockerd-start /usr/local/bin/agentbox-dockerd-start
|
|
251
|
+
RUN chmod +x /usr/local/bin/agentbox-dockerd-start
|
|
252
|
+
|
|
253
|
+
# tmux config so Claude's true-color output and OSC 8 hyperlinks survive the
|
|
254
|
+
# in-container tmux. `terminal-features` is a no-op on tmux < 3.4. Without
|
|
255
|
+
# this, claude renders without 24-bit color (logo invisible) and hyperlinks
|
|
256
|
+
# get broken across visual wrap boundaries. The WheelUp/Down rebinds drop the
|
|
257
|
+
# copy-mode scroll step from the built-in 5 lines to 2 — a macOS trackpad
|
|
258
|
+
# swipe emits a burst of wheel events, so the default flies past dozens of
|
|
259
|
+
# lines on a single flick.
|
|
260
|
+
RUN printf '%s\n' \
|
|
261
|
+
'set -g default-terminal "tmux-256color"' \
|
|
262
|
+
'set -as terminal-overrides ",*:Tc"' \
|
|
263
|
+
'set -as terminal-overrides ",*:RGB"' \
|
|
264
|
+
'set -as terminal-features ",*:hyperlinks"' \
|
|
265
|
+
'set -as terminal-features ",*:RGB"' \
|
|
266
|
+
'set -g mouse on' \
|
|
267
|
+
'bind -T copy-mode WheelUpPane send -N2 -X scroll-up' \
|
|
268
|
+
'bind -T copy-mode WheelDownPane send -N2 -X scroll-down' \
|
|
269
|
+
'bind -T copy-mode-vi WheelUpPane send -N2 -X scroll-up' \
|
|
270
|
+
'bind -T copy-mode-vi WheelDownPane send -N2 -X scroll-down' \
|
|
271
|
+
'set -g history-limit 50000' \
|
|
272
|
+
'set -g escape-time 0' \
|
|
273
|
+
> /etc/tmux.conf
|
|
274
|
+
|
|
275
|
+
# System-wide Claude Code hint. NOTE: /etc/claude-code/CLAUDE.md is not a
|
|
276
|
+
# documented Claude Code load path today (only /etc/claude-code/managed-settings.json
|
|
277
|
+
# is, and that's settings-only) — kept here so it's discoverable via
|
|
278
|
+
# `cat /etc/claude-code/CLAUDE.md` and forward-compatible if Claude Code
|
|
279
|
+
# adds it to its memory chain. Until then, in-box agents learn the same
|
|
280
|
+
# facts via /etc/agentbox/box.env + AGENTBOX_* env vars. Content lives in
|
|
281
|
+
# scripts/custom-system-CLAUDE.md (edit there, not inline) for maintainability.
|
|
282
|
+
RUN mkdir -p /etc/claude-code
|
|
283
|
+
COPY packages/sandbox-docker/scripts/custom-system-CLAUDE.md /etc/claude-code/CLAUDE.md
|
|
284
|
+
RUN chmod 0644 /etc/claude-code/CLAUDE.md
|
|
285
|
+
|
|
286
|
+
# Enterprise-managed Claude Code settings: activity-reporting hooks. Highest
|
|
287
|
+
# precedence and NOT synced from the host ~/.claude, so the host-hook filter
|
|
288
|
+
# never touches it; hook arrays merge across settings sources, so the user's
|
|
289
|
+
# own hooks still run. Drives `agentbox status/list/inspect` claude activity.
|
|
290
|
+
# (Build context is the monorepo root — see BUILD_CONTEXT_DIR in image.ts.)
|
|
291
|
+
COPY packages/sandbox-docker/scripts/claude-managed-settings.json /etc/claude-code/managed-settings.json
|
|
292
|
+
RUN chmod 0644 /etc/claude-code/managed-settings.json
|
|
293
|
+
|
|
294
|
+
# /etc/agentbox/ holds runtime-injected box.env (written by `agentbox create`
|
|
295
|
+
# via docker exec). Pre-created here so the writable layer starts with the
|
|
296
|
+
# right perms; the file itself appears at create time.
|
|
297
|
+
RUN mkdir -p /etc/agentbox && chmod 0755 /etc/agentbox
|
|
298
|
+
|
|
299
|
+
# Login-shell shim: source /etc/agentbox/box.env so `agentbox shell <box>` and
|
|
300
|
+
# any `bash -l` see AGENTBOX_* even when launched outside `docker run`'s env
|
|
301
|
+
# (e.g. interactive shells). `set -a` exports every sourced var.
|
|
302
|
+
RUN printf '%s\n' \
|
|
303
|
+
'# Auto-loaded by login shells; box.env is written by `agentbox create`.' \
|
|
304
|
+
'if [ -r /etc/agentbox/box.env ]; then' \
|
|
305
|
+
' set -a' \
|
|
306
|
+
' . /etc/agentbox/box.env' \
|
|
307
|
+
' set +a' \
|
|
308
|
+
'fi' \
|
|
309
|
+
> /etc/profile.d/agentbox.sh \
|
|
310
|
+
&& chmod 0644 /etc/profile.d/agentbox.sh
|
|
311
|
+
|
|
312
|
+
# Default to the vscode user (UID 1000) provided by base:ubuntu. The overlay
|
|
313
|
+
# mount itself happens via `docker exec --user root`, so this is just the
|
|
314
|
+
# default identity for interactive shells.
|
|
315
|
+
USER vscode
|
|
316
|
+
WORKDIR /workspace
|