maesttro 0.4.4 → 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.
package/docker/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  # git is built from source: Debian ships 2.47 and the runner wants a recent
2
- # git. Builder matches node:22-slim's Debian so the runtime libs line up.
2
+ # git. Builder matches node:24-slim's Debian so the runtime libs line up.
3
3
  FROM debian:bookworm-slim AS git-build
4
4
  ARG GIT_VERSION=2.55.0
5
5
  ARG GIT_SHA256=457fdb04dc8728e007d4688695e6912e6f680727920f2a40bf11eacc17505357
@@ -14,9 +14,18 @@ RUN apt-get update \
14
14
  && make -j"$(nproc)" prefix=/usr/local sysconfdir=/etc NO_TCLTK=1 NO_PERL=1 NO_PYTHON=1 NO_GETTEXT=1 NO_RUST=1 \
15
15
  DESTDIR=/out all install
16
16
 
17
- FROM node:22-slim
17
+ FROM oven/bun:1.4.2-slim AS bun
18
+ FROM ghcr.io/astral-sh/uv:0.12.19 AS uv
19
+
20
+ FROM node:24-slim
21
+
22
+ # Lets `maesttro update` find and remove older runner images once they lose their tag
23
+ LABEL org.maesttro.runner="1"
18
24
 
19
25
  COPY --from=git-build /out/usr/local /usr/local
26
+ COPY --from=bun /usr/local/bin/bun /usr/local/bin/bun
27
+ COPY --from=uv /uv /uvx /usr/local/bin/
28
+ RUN ln -s /usr/local/bin/bun /usr/local/bin/bunx
20
29
 
21
30
  # gh comes from GitHub's apt repo: Debian's lags by years and predates the
22
31
  # hosts.yml / config.yml behaviour the CLI's read-only session mount relies on
@@ -30,28 +39,74 @@ RUN apt-get update \
30
39
  > /etc/apt/sources.list.d/github-cli.list \
31
40
  && apt-get update \
32
41
  && apt-get install -y --no-install-recommends gh \
42
+ build-essential pkg-config \
43
+ unzip zip xz-utils bzip2 wget jq ripgrep fd-find tree file procps rsync patch sqlite3 \
44
+ libicu72 libssl3 zlib1g libgssapi-krb5-2 \
45
+ && ln -s /usr/bin/fdfind /usr/local/bin/fd \
33
46
  && rm -rf /var/lib/apt/lists/*
34
47
 
35
- # System-level, not ~/.gitconfig: /home/agent is the persistent volume and
36
- # shadows anything baked into home for existing installs
48
+ ARG DOTNET_SDK_CHANNEL=10.0
49
+ ENV DOTNET_ROOT=/usr/share/dotnet \
50
+ DOTNET_CLI_TELEMETRY_OPTOUT=1 \
51
+ DOTNET_NOLOGO=1 \
52
+ DOTNET_GENERATE_ASPNET_CERTIFICATE=false \
53
+ DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK=true \
54
+ NUGET_XMLDOC_MODE=skip
55
+ RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
56
+ && bash /tmp/dotnet-install.sh --channel "${DOTNET_SDK_CHANNEL}" --install-dir "${DOTNET_ROOT}" \
57
+ && ln -s "${DOTNET_ROOT}/dotnet" /usr/local/bin/dotnet \
58
+ && rm /tmp/dotnet-install.sh
59
+
60
+ # Python comes from uv (python-build-standalone), not Debian: one toolchain for
61
+ # interpreters, venvs and tools; python/python3/pip stay on PATH for node-gyp and habit
62
+ ARG PYTHON_VERSION=3.14
63
+ RUN UV_PYTHON_INSTALL_DIR=/usr/local/share/uv/python UV_PYTHON_BIN_DIR=/usr/local/bin \
64
+ uv python install "${PYTHON_VERSION}" --default \
65
+ && ln -s "$(dirname "$(readlink -f /usr/local/bin/python3)")/pip3" /usr/local/bin/pip3 \
66
+ && ln -s pip3 /usr/local/bin/pip
67
+
68
+ # System-level, not ~/.gitconfig: the per-session home mounted at /home/agent
69
+ # shadows anything baked into the image's home
37
70
  COPY gitconfig /etc/gitconfig
38
71
  COPY runner-marker /etc/maesttro/runner
39
72
 
40
73
  # Curated client CLIs — keep in sync with src/clients.ts
41
74
  RUN npm install -g \
42
75
  @anthropic-ai/claude-code \
43
- opencode-ai
76
+ opencode-ai \
77
+ typescript \
78
+ && corepack enable
79
+ # The image pins the client version; an in-container update would only land on
80
+ # the ephemeral layer and be re-downloaded every session
81
+ ENV DISABLE_AUTOUPDATER=1
44
82
 
45
83
  RUN useradd -m -s /bin/bash agent
46
84
  USER agent
47
- # Created as agent so a fresh volume inherits them; otherwise Docker creates
48
- # ~/.config and ~/.claude as root when it sets up the nested read-only mounts
49
- RUN mkdir -p /home/agent/.config/opencode /home/agent/.claude
50
85
  WORKDIR /workspace
51
86
 
87
+ # Everything the agent installs lands on the container's writable layer, never
88
+ # the session's home, so it is gone when the session's container exits
89
+ ENV AGENT_TOOLS=/opt/agent-tools
90
+ ENV NPM_CONFIG_PREFIX=${AGENT_TOOLS} \
91
+ NPM_CONFIG_CACHE=${AGENT_TOOLS}/cache/npm \
92
+ COREPACK_HOME=${AGENT_TOOLS}/corepack \
93
+ COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
94
+ BUN_INSTALL=${AGENT_TOOLS}/bun \
95
+ PYTHONUSERBASE=${AGENT_TOOLS} \
96
+ PIP_BREAK_SYSTEM_PACKAGES=1 \
97
+ PIP_CACHE_DIR=${AGENT_TOOLS}/cache/pip \
98
+ UV_CACHE_DIR=${AGENT_TOOLS}/cache/uv \
99
+ UV_TOOL_DIR=${AGENT_TOOLS}/uv/tools \
100
+ UV_TOOL_BIN_DIR=${AGENT_TOOLS}/bin \
101
+ UV_PYTHON_INSTALL_DIR=${AGENT_TOOLS}/uv/python \
102
+ UV_PYTHON_BIN_DIR=${AGENT_TOOLS}/bin \
103
+ DOTNET_CLI_HOME=${AGENT_TOOLS}/dotnet \
104
+ NUGET_PACKAGES=${AGENT_TOOLS}/cache/nuget \
105
+ NUGET_HTTP_CACHE_PATH=${AGENT_TOOLS}/cache/nuget-http
106
+ ENV PATH=${AGENT_TOOLS}/bin:${AGENT_TOOLS}/bun/bin:${AGENT_TOOLS}/dotnet/.dotnet/tools:${PATH}
107
+
52
108
  # Client configs are applied at container start by `maesttro configure`
53
- # (mounted from the host's install): /home/agent is a persistent volume, so
54
- # anything baked here would only ever reach brand-new volumes.
109
+ # (mounted from the host's install): the session home shadows /home/agent
55
110
 
56
111
  # Starts as root only to remap agent to the host user's uid/gid (bind mounts
57
112
  # keep host ownership on Linux), then drops to agent before running anything
@@ -9,10 +9,16 @@ if [ -n "$uid" ] && [ "$uid" != "$(id -u agent)" ]; then
9
9
  usermod --non-unique --uid "$uid" --gid "$gid" agent
10
10
  fi
11
11
 
12
- owner="$(id -u agent):$(id -g agent)"
13
- if [ -n "$uid" ] && [ "$(stat -c %u:%g /home/agent)" != "$owner" ]; then
14
- find /home/agent -xdev -exec chown -h "$owner" {} + 2>/dev/null || true
12
+ # cp -a onto the directory itself would copy /etc/skel's own mode, clobbering the 0700 the host set
13
+ if [ ! -e /home/agent/.profile ]; then
14
+ find /etc/skel -mindepth 1 -maxdepth 1 -exec cp -a {} /home/agent/ \;
15
15
  fi
16
16
 
17
+ owner="$(id -u agent):$(id -g agent)"
18
+ find /home/agent /home/agent/.claude -xdev \! -user agent -exec chown -h "$owner" {} + 2>/dev/null || true
19
+
20
+ mkdir -p "$AGENT_TOOLS"
21
+ chown "$owner" "$AGENT_TOOLS"
22
+
17
23
  export HOME=/home/agent USER=agent LOGNAME=agent
18
24
  exec setpriv --reuid=agent --regid=agent --init-groups /bin/bash -c "$*"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maesttro",
3
- "version": "0.4.4",
3
+ "version": "0.6.0",
4
4
  "description": "Isolated Docker runner for local coding agents (opencode, claude, ...) under a dedicated bot GitHub identity, with curated configs baked in per client.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,5 +26,8 @@
26
26
  "node": ">=18"
27
27
  },
28
28
  "license": "UNLICENSED",
29
- "private": false
29
+ "private": false,
30
+ "devDependencies": {
31
+ "@clack/prompts": "1.8.1"
32
+ }
30
33
  }