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
93 lines
3.4 KiB
Docker
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"]
|