diff --git a/Dockerfile b/Dockerfile index e3b8671..eb30921 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,17 @@ FROM python:3.11-slim -# System deps +# System deps — curl/gosu/node/npm for the runtime; git + gh for agent +# autonomy (agents run `gh issue list`, `gh issue create`, `gh issue edit +# --add-assignee`, `git clone`, etc. per their idle/cron prompts). +# Without these the team's claim-and-ship loop silently returns +# "(no response generated)" because tools error out. RUN apt-get update && apt-get install -y --no-install-recommends \ - curl gosu nodejs npm ca-certificates \ + curl gosu nodejs npm ca-certificates git \ + && install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ + && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \ + && apt-get update && apt-get install -y --no-install-recommends gh \ && rm -rf /var/lib/apt/lists/* # Install claude-code CLI via npm @@ -23,4 +32,11 @@ COPY __init__.py . # Set the adapter module for runtime discovery ENV ADAPTER_MODULE=adapter -ENTRYPOINT ["molecule-runtime"] +# Drop-priv entrypoint — claude-code refuses --dangerously-skip-permissions +# as root, so we run molecule-runtime as the agent user (uid 1000). +# The script handles volume-ownership fix + session-dir symlink before +# exec'ing via gosu. +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..fd9feef --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Drop privileges to the agent user before exec'ing molecule-runtime. +# claude-code refuses --dangerously-skip-permissions when running as +# root/sudo for safety. Without this entrypoint, every cron tick fails +# with `ProcessError: Command failed with exit code 1` and the agent +# logs `--dangerously-skip-permissions cannot be used with root/sudo +# privileges for security reasons`. +# +# Pattern matches the legacy monorepo workspace-template/entrypoint.sh: +# fix volume ownership as root, then re-exec via gosu as agent (uid 1000). + +if [ "$(id -u)" = "0" ]; then + # Configs volume is created by Docker as root; agent needs write access + # for plugin installs, memory writes, .auth_token rotation, etc. + chown -R agent:agent /configs 2>/dev/null + # /workspace handling — only chown when the contents are root-owned + # (typical on Docker Desktop on Windows where host uid maps to 0). + # On Linux Docker with matching uids the recursive chown is skipped + # to keep startup fast. + chown agent:agent /workspace 2>/dev/null || true + if [ -d /workspace ]; then + first_entry=$(find /workspace -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null) + if [ -n "$first_entry" ] && [ "$(stat -c '%u' "$first_entry" 2>/dev/null)" = "0" ]; then + chown -R agent:agent /workspace 2>/dev/null + fi + fi + # Claude Code session directory — mounted at /root/.claude/sessions by + # the platform provisioner. Symlink it into agent's home so the SDK + # finds it when running as agent. The provisioner's mount point is + # hardcoded to /root/.claude/sessions; we don't want to change the + # platform contract just for this template. + mkdir -p /home/agent/.claude + if [ -d /root/.claude/sessions ]; then + chown -R agent:agent /root/.claude /home/agent/.claude 2>/dev/null + ln -sfn /root/.claude/sessions /home/agent/.claude/sessions + fi + exec gosu agent "$0" "$@" +fi + +# Now running as agent (uid 1000) +exec molecule-runtime "$@"