Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,28 @@ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& apt-get update && apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/*

# xurl CLI (X/Twitter API v2)
RUN XURL_TAG=$(curl -sf https://api.github.com/repos/xdevplatform/xurl/releases/latest | jq -r '.tag_name') \
&& [ "$XURL_TAG" != "null" ] && [ -n "$XURL_TAG" ] \
&& curl -fsSL "https://github.com/xdevplatform/xurl/releases/download/${XURL_TAG}/xurl_Linux_x86_64.tar.gz" \
| tar -xz -C /usr/local/bin xurl \
&& chmod +x /usr/local/bin/xurl

# Google Workspace CLI (gws) + agent skills
RUN npm install -g @googleworkspace/cli
# xurl CLI (X/Twitter API v2) — pinned version with SHA256 verification
ARG XURL_VERSION=1.0.3
ARG XURL_SHA256_AMD64=34bc67bfbaf29ae121f7788fbd2491d3a8b95cb3947333ad39732e694497c182
ARG XURL_SHA256_ARM64=3b56605e66508d7bc77c36cc711d41307b4cd76aec09111890b33f9d82975483
RUN ARCH=$(dpkg --print-architecture) \
&& case "$ARCH" in \
amd64) XURL_ARCH="x86_64"; XURL_SHA256="${XURL_SHA256_AMD64}" ;; \
arm64) XURL_ARCH="arm64"; XURL_SHA256="${XURL_SHA256_ARM64}" ;; \
*) echo "Unsupported architecture: $ARCH" && exit 1 ;; \
esac \
&& curl -fsSL "https://github.com/xdevplatform/xurl/releases/download/v${XURL_VERSION}/xurl_Linux_${XURL_ARCH}.tar.gz" \
-o /tmp/xurl.tar.gz \
&& echo "${XURL_SHA256} /tmp/xurl.tar.gz" | sha256sum -c - \
&& tar -xz -C /usr/local/bin -f /tmp/xurl.tar.gz xurl \
&& chmod +x /usr/local/bin/xurl \
&& rm /tmp/xurl.tar.gz

# Google Workspace CLI + Claude CLI — pinned versions, single layer
ARG CLAUDE_CLI_VERSION=2.1.83
RUN npm install -g \
@googleworkspace/cli@0.22.1 \
@anthropic-ai/claude-code@${CLAUDE_CLI_VERSION}

# Agent templates (read-only source for entrypoint to copy into workspace)
COPY --chown=node:node agents/ /opt/openclaw-agents/
Expand Down
17 changes: 6 additions & 11 deletions init.d/01-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@
# SCRIPT_NAME and log() are provided by docker-entrypoint.sh
# Runs as `node` user. Tools are installed once and persist across restarts.

# --- Claude Code CLI ---
if command -v claude &>/dev/null; then
log "Claude CLI already installed: $(claude --version 2>/dev/null || echo 'unknown')"
else
log "Installing Claude CLI..."
curl -fsSL https://claude.ai/install.sh | bash
log "Claude CLI installed: $(claude --version 2>/dev/null || echo 'unknown')"
fi
# Pinned versions (correspond to Dockerfile npm pins)
GWS_COMMIT="a52d297cdfafbc53dfed66a3721a9bbd1d50dc31" # @googleworkspace/cli@0.22.1

# --- Google Workspace CLI skills ---
# Claude CLI is pre-installed in the Docker image; assert it is on PATH.
command -v claude >/dev/null 2>&1 || log "WARNING: Claude CLI not found in image."
if npx skills list 2>/dev/null | grep -q googleworkspace; then
log "GWS skills already installed."
else
log "Installing Google Workspace skills..."
npx -y skills add https://github.com/googleworkspace/cli -y
log "Installing Google Workspace skills (pinned commit: ${GWS_COMMIT})..."
npx -y skills add "https://github.com/googleworkspace/cli#${GWS_COMMIT}" -y
log "GWS skills installed."
fi

Expand Down
60 changes: 39 additions & 21 deletions installer/gum.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
GUM_MIN_MAJOR=0
GUM_MIN_MINOR=14

# Pinned release — update SHA256 values when bumping version
GUM_PINNED_VERSION="0.17.0"
# SHA256 checksums from https://github.com/charmbracelet/gum/releases/download/v0.17.0/checksums.txt
GUM_SHA256_LINUX_X86_64="69ee169bd6387331928864e94d47ed01ef649fbfe875baed1bbf27b5377a6fdb"
GUM_SHA256_LINUX_ARM64="b0b9ed95cbf7c8b7073f17b9591811f5c001e33c7cfd066ca83ce8a07c576f9c"
GUM_SHA256_DARWIN_ARM64="e2a4b8596efa05821d8c58d0c1afbcd7ad1699ba69c689cc3ff23a4a99c8b237"
GUM_SHA256_DARWIN_X86_64="cd66576aeebe6cd19c771863c7e8d696e0e1d5387d1e7075666baa67c2052e53"

# gum_detect — returns 0 if gum >= 0.14 is installed, 1 otherwise
gum_detect() {
if ! command -v gum >/dev/null 2>&1; then
Expand Down Expand Up @@ -86,37 +94,47 @@ gum_install() {
fi
fi

# Binary download fallback
echo "Downloading gum binary from GitHub releases..."
local os arch download_url tmpdir
echo "Downloading gum v${GUM_PINNED_VERSION} binary from GitHub releases..."
local os arch download_url expected_sha256 tmpdir
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m)
case "$arch" in
x86_64) arch="x86_64" ;;
aarch64|arm64) arch="arm64" ;;

local os_cap
os_cap=$(echo "$os" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')

case "${os}/${arch}" in
linux/x86_64) arch="x86_64"; expected_sha256="${GUM_SHA256_LINUX_X86_64}" ;;
linux/aarch64|\
linux/arm64) arch="arm64"; expected_sha256="${GUM_SHA256_LINUX_ARM64}" ;;
darwin/arm64) arch="arm64"; expected_sha256="${GUM_SHA256_DARWIN_ARM64}" ;;
darwin/x86_64) arch="x86_64"; expected_sha256="${GUM_SHA256_DARWIN_X86_64}" ;;
*)
echo "Unsupported architecture: $arch"
echo "Unsupported platform: ${os}/${arch}"
return 1
;;
esac

# Query latest release from GitHub API
local version
version=$(curl -sf "https://api.github.com/repos/charmbracelet/gum/releases/latest" 2>/dev/null | \
grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4 | tr -d 'v' || echo "")
if [ -z "$version" ]; then
# Fallback version if API is unreachable
version="0.14.5"
echo "Could not fetch latest version, trying $version..."
fi

local os_cap
os_cap=$(echo "$os" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')

download_url="https://github.com/charmbracelet/gum/releases/download/v${version}/gum_${version}_${os_cap}_${arch}.tar.gz"
download_url="https://github.com/charmbracelet/gum/releases/download/v${GUM_PINNED_VERSION}/gum_${GUM_PINNED_VERSION}_${os_cap}_${arch}.tar.gz"

tmpdir=$(mktemp -d)
if curl -fsSL "$download_url" -o "${tmpdir}/gum.tar.gz" 2>/dev/null; then
# Verify integrity before extracting (tampered archive could exploit tar path traversal)
local sha_cmd
if command -v sha256sum >/dev/null 2>&1; then
sha_cmd="sha256sum"
elif command -v shasum >/dev/null 2>&1; then
sha_cmd="shasum -a 256"
else
echo "ERROR: No sha256sum or shasum found; cannot verify download."
rm -rf "$tmpdir"
return 1
fi
if ! echo "${expected_sha256} ${tmpdir}/gum.tar.gz" | $sha_cmd -c - >/dev/null 2>&1; then
echo "ERROR: SHA256 mismatch for gum download."
rm -rf "$tmpdir"
return 1
fi

tar -xzf "${tmpdir}/gum.tar.gz" -C "$tmpdir" 2>/dev/null
local gum_bin
gum_bin=$(find "$tmpdir" -name "gum" -type f | head -1)
Expand Down
Loading