Files
nas-tools/claude-dev/Dockerfile.base
T
Gan, Jimmy 9a2b1f8941
Deploy Dashboard (Dev) / Backend Tests (push) Successful in 1m14s
Deploy Dashboard (Dev) / Frontend Tests (push) Successful in 50s
Deploy Dashboard (Dev) / Build Dev Image (push) Failing after 12m34s
Deploy Dashboard (Dev) / Deploy to Dev (push) Has been skipped
Deploy Claude Dev (Dev) / deploy-claude-dev-dev (push) Has been cancelled
ci: clean trigger - tests passing, labels fixed
2026-06-07 16:21:36 +08:00

93 lines
3.4 KiB
Docker

FROM node:20-bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
fd-find \
gh \
git \
jq \
make \
openssh-client \
python3 \
python3-pip \
python3-venv \
ripgrep \
rsync \
tmux \
unzip \
wget \
zip \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install --break-system-packages --no-cache-dir "litellm[proxy]" -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN ln -sf /usr/bin/fdfind /usr/local/bin/fd
ARG YQ_VERSION=v4.44.6
RUN wget -q -O /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" \
&& chmod +x /usr/local/bin/yq
ARG TEA_VERSION=0.12.0
RUN wget -q -O /usr/local/bin/tea "https://gitea.com/gitea/tea/releases/download/v${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" \
&& chmod +x /usr/local/bin/tea
RUN npm install -g @anthropic-ai/claude-code
# Bake LiteLLM auto-start script into the image (used by Dockerfile runtime)
COPY scripts/start-litellm.sh /opt/claude-dev/scripts/start-litellm.sh
RUN chmod +x /opt/claude-dev/scripts/start-litellm.sh
# Preflight bypass: Claude Code tries to validate connectivity against api.anthropic.com.
# Since we use a custom proxy (LiteLLM → DeepSeek), we disable this check.
# Strategy: replace the preflight function body with a no-op return.
RUN python3 - <<'PY'
from pathlib import Path
import re
path = Path("/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js")
if not path.exists():
raise SystemExit(f"claude cli not found: {path}")
content = path.read_text()
# For v2.1.x bundled binary: find and remove the preflight connectivity check
# Pattern: the function that GETs api/hello and oauth/hello endpoints
# Strategy 1: try the known old regex pattern
old_pattern = r'let A=U7\(\),q=new URL\(A\.TOKEN_URL\),K=\[`\$\{A\.BASE_API_URL\}/api/hello`,`\$\{q\.origin\}/v1/oauth/hello`\],Y=async\(_\)=>\{try\{let \$=await I8\.get\(_,\{headers:\{"User-Agent":ey\(\)\}\}\);if\(\$\.status!==200\)return\{success:!1,error:`Failed to connect to \$\{new URL\(_\)\.hostname\}: Status \$\{\$\.status\}`\};return\{success:!0\}\}catch\(\$\)\{'
replacement = 'return'
patched, count = re.subn(old_pattern, replacement, content, count=1)
if count == 1:
path.write_text(patched)
print("Applied preflight bypass (old pattern)")
else:
# Strategy 2: more general - remove any function calling api/hello for connectivity check
print("Old pattern not found, trying broader patch...")
# Find the connectivity check function and replace it
broad_pattern = r'Failed to connect to \$\{new URL\(_\)\.hostname\}: Status'
if re.search(broad_pattern, content):
# Replace whole preflight function - match from "let A=U7" up to the catch block
content = re.sub(
r'let \w+=U7\(\)[^;]+;[^}]+Failed to connect to[^}]+}\}catch\([^}]+\)\{',
'return',
content,
count=1
)
path.write_text(content)
print("Applied preflight bypass (broad pattern)")
else:
# Strategy 3: replace api/hello references with empty health endpoint
content = content.replace(
'api/hello',
'x-no-op'
)
path.write_text(content)
print("Applied preflight bypass (url-replace pattern)")
PY
CMD ["bash"]