Spaces:
Running
Running
feat: update Claude Code authentication wrapper and increase sync interval while removing Gemini debug logs
Browse files- .env.example +13 -2
- Dockerfile +25 -17
- health-server.js +2 -16
- start.sh +19 -3
.env.example
CHANGED
|
@@ -45,8 +45,19 @@ PAPERCLIP_DEPLOYMENT_MODE=authenticated
|
|
| 45 |
# Paperclip Agent Providers (varies by setup)
|
| 46 |
# ============================================================================
|
| 47 |
|
| 48 |
-
#
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
# For other LLM providers (OpenAI, etc.)
|
| 52 |
# LLM_API_KEY=your-api-key
|
|
|
|
| 45 |
# Paperclip Agent Providers (varies by setup)
|
| 46 |
# ============================================================================
|
| 47 |
|
| 48 |
+
# Claude Code β choose one mode or neither:
|
| 49 |
+
#
|
| 50 |
+
# Subscription mode (Claude Pro/Max β no per-token cost):
|
| 51 |
+
# 1. Run `claude login` locally to authenticate once
|
| 52 |
+
# 2. Copy the token from ~/.claude/.credentials.json
|
| 53 |
+
# 3. Paste it here (takes priority over API key if both are set)
|
| 54 |
+
# ANTHROPIC_AUTH_TOKEN=your-oauth-token-from-credentials-json
|
| 55 |
+
#
|
| 56 |
+
# API key mode (pay-per-use):
|
| 57 |
+
# ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 58 |
+
#
|
| 59 |
+
# Neither set β Claude Code uses stored OAuth credentials from ~/.claude/
|
| 60 |
+
# (requires first-time login inside the container)
|
| 61 |
|
| 62 |
# For other LLM providers (OpenAI, etc.)
|
| 63 |
# LLM_API_KEY=your-api-key
|
Dockerfile
CHANGED
|
@@ -57,17 +57,30 @@ RUN npm init -y && npm install express@4 cors morgan
|
|
| 57 |
# Install agent CLIs globally
|
| 58 |
RUN npm install -g @google/gemini-cli @anthropic-ai/claude-code @openai/codex
|
| 59 |
|
| 60 |
-
#
|
| 61 |
-
#
|
| 62 |
-
#
|
| 63 |
-
#
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
# Gemini wrapper β fix for "Failed to relaunch the CLI process":
|
| 73 |
#
|
|
@@ -86,10 +99,6 @@ RUN for cmd in claude codex; do \
|
|
| 86 |
RUN mv /usr/local/bin/gemini /usr/local/bin/gemini-real && \
|
| 87 |
{ \
|
| 88 |
echo '#!/bin/sh'; \
|
| 89 |
-
echo '# Log invocation so we can see what env Paperclip actually passes'; \
|
| 90 |
-
echo 'echo "=== gemini wrapper $(date) args: $@ ===" >> /tmp/gemini-wrapper.log'; \
|
| 91 |
-
echo 'env | sort >> /tmp/gemini-wrapper.log'; \
|
| 92 |
-
echo ''; \
|
| 93 |
echo 'unset NODE_OPTIONS'; \
|
| 94 |
echo 'export NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"'; \
|
| 95 |
echo 'export GEMINI_CLI_NO_RELAUNCH=1'; \
|
|
@@ -100,8 +109,7 @@ RUN mv /usr/local/bin/gemini /usr/local/bin/gemini-real && \
|
|
| 100 |
echo 'export SANDBOX=1'; \
|
| 101 |
echo 'exec /usr/local/bin/gemini-real "$@"'; \
|
| 102 |
} > /usr/local/bin/gemini && \
|
| 103 |
-
chmod +x /usr/local/bin/gemini
|
| 104 |
-
echo "=== gemini wrapper ===" && cat /usr/local/bin/gemini
|
| 105 |
|
| 106 |
# Install Python dependencies for sync
|
| 107 |
RUN pip install --no-cache-dir --break-system-packages huggingface_hub PyYAML
|
|
|
|
| 57 |
# Install agent CLIs globally
|
| 58 |
RUN npm install -g @google/gemini-cli @anthropic-ai/claude-code @openai/codex
|
| 59 |
|
| 60 |
+
# Claude Code wrapper β auth mode selection:
|
| 61 |
+
# ANTHROPIC_AUTH_TOKEN set β subscription mode (OAuth token, takes priority)
|
| 62 |
+
# ANTHROPIC_API_KEY set β API key mode
|
| 63 |
+
# Neither set β uses stored OAuth credentials in ~/.claude/
|
| 64 |
+
# If both are set, subscription token wins: unset API key to avoid conflict.
|
| 65 |
+
# Also drops cloudflare NODE_OPTIONS and caps heap size.
|
| 66 |
+
RUN if [ -e /usr/local/bin/claude ]; then \
|
| 67 |
+
mv /usr/local/bin/claude /usr/local/bin/claude-real && \
|
| 68 |
+
{ \
|
| 69 |
+
echo '#!/bin/sh'; \
|
| 70 |
+
echo 'unset NODE_OPTIONS'; \
|
| 71 |
+
echo '[ -n "$ANTHROPIC_AUTH_TOKEN" ] && unset ANTHROPIC_API_KEY'; \
|
| 72 |
+
echo 'export NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"'; \
|
| 73 |
+
echo 'exec /usr/local/bin/claude-real "$@"'; \
|
| 74 |
+
} > /usr/local/bin/claude && \
|
| 75 |
+
chmod +x /usr/local/bin/claude; \
|
| 76 |
+
fi
|
| 77 |
+
|
| 78 |
+
# Codex wrapper β drops cloudflare NODE_OPTIONS, caps heap size
|
| 79 |
+
RUN if [ -e /usr/local/bin/codex ]; then \
|
| 80 |
+
mv /usr/local/bin/codex /usr/local/bin/codex-real && \
|
| 81 |
+
printf '#!/bin/sh\nunset NODE_OPTIONS\nexport NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"\nexec /usr/local/bin/codex-real "$@"\n' > /usr/local/bin/codex && \
|
| 82 |
+
chmod +x /usr/local/bin/codex; \
|
| 83 |
+
fi
|
| 84 |
|
| 85 |
# Gemini wrapper β fix for "Failed to relaunch the CLI process":
|
| 86 |
#
|
|
|
|
| 99 |
RUN mv /usr/local/bin/gemini /usr/local/bin/gemini-real && \
|
| 100 |
{ \
|
| 101 |
echo '#!/bin/sh'; \
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
echo 'unset NODE_OPTIONS'; \
|
| 103 |
echo 'export NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"'; \
|
| 104 |
echo 'export GEMINI_CLI_NO_RELAUNCH=1'; \
|
|
|
|
| 109 |
echo 'export SANDBOX=1'; \
|
| 110 |
echo 'exec /usr/local/bin/gemini-real "$@"'; \
|
| 111 |
} > /usr/local/bin/gemini && \
|
| 112 |
+
chmod +x /usr/local/bin/gemini
|
|
|
|
| 113 |
|
| 114 |
# Install Python dependencies for sync
|
| 115 |
RUN pip install --no-cache-dir --break-system-packages huggingface_hub PyYAML
|
health-server.js
CHANGED
|
@@ -10,7 +10,7 @@ const PAPERCLIP_PORT = 3100;
|
|
| 10 |
const startTime = Date.now();
|
| 11 |
|
| 12 |
const HF_BACKUP_ENABLED = !!process.env.HF_TOKEN;
|
| 13 |
-
const SYNC_INTERVAL = process.env.SYNC_INTERVAL || "
|
| 14 |
|
| 15 |
const UPTIMEROBOT_SETUP_ENABLED =
|
| 16 |
String(process.env.UPTIMEROBOT_SETUP_ENABLED || "true").toLowerCase() === "true";
|
|
@@ -36,8 +36,7 @@ function isLocalRoute(pathname) {
|
|
| 36 |
return (
|
| 37 |
pathname === "/health" ||
|
| 38 |
pathname === "/status" ||
|
| 39 |
-
pathname === "/uptimerobot/setup"
|
| 40 |
-
pathname === "/debug/gemini-log"
|
| 41 |
);
|
| 42 |
}
|
| 43 |
|
|
@@ -804,19 +803,6 @@ const server = http.createServer((req, res) => {
|
|
| 804 |
return;
|
| 805 |
}
|
| 806 |
|
| 807 |
-
// ββ Gemini wrapper debug log ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 808 |
-
if (pathname === "/debug/gemini-log") {
|
| 809 |
-
try {
|
| 810 |
-
const log = fs.readFileSync("/tmp/gemini-wrapper.log", "utf8");
|
| 811 |
-
res.writeHead(200, { "Content-Type": "text/plain" });
|
| 812 |
-
res.end(log);
|
| 813 |
-
} catch {
|
| 814 |
-
res.writeHead(200, { "Content-Type": "text/plain" });
|
| 815 |
-
res.end("No log yet β click 'Test now' on the Gemini adapter first.\n");
|
| 816 |
-
}
|
| 817 |
-
return;
|
| 818 |
-
}
|
| 819 |
-
|
| 820 |
// ββ UptimeRobot setup endpoint βββββββββββββββββββββββββββββββββββββββββββββ
|
| 821 |
if (pathname === "/uptimerobot/setup") {
|
| 822 |
if (req.method !== "POST") {
|
|
|
|
| 10 |
const startTime = Date.now();
|
| 11 |
|
| 12 |
const HF_BACKUP_ENABLED = !!process.env.HF_TOKEN;
|
| 13 |
+
const SYNC_INTERVAL = process.env.SYNC_INTERVAL || "86400";
|
| 14 |
|
| 15 |
const UPTIMEROBOT_SETUP_ENABLED =
|
| 16 |
String(process.env.UPTIMEROBOT_SETUP_ENABLED || "true").toLowerCase() === "true";
|
|
|
|
| 36 |
return (
|
| 37 |
pathname === "/health" ||
|
| 38 |
pathname === "/status" ||
|
| 39 |
+
pathname === "/uptimerobot/setup"
|
|
|
|
| 40 |
);
|
| 41 |
}
|
| 42 |
|
|
|
|
| 803 |
return;
|
| 804 |
}
|
| 805 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 806 |
// ββ UptimeRobot setup endpoint βββββββββββββββββββββββββββββββββββββββββββββ
|
| 807 |
if (pathname === "/uptimerobot/setup") {
|
| 808 |
if (req.method !== "POST") {
|
start.sh
CHANGED
|
@@ -35,8 +35,12 @@ export PAPERCLIP_ALLOWED_HOSTNAMES="${PAPERCLIP_ALLOWED_HOSTNAMES:-${_ALLOWED}}"
|
|
| 35 |
|
| 36 |
# LLM API keys
|
| 37 |
export GEMINI_API_KEY="${GEMINI_API_KEY:-}"
|
| 38 |
-
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}"
|
| 39 |
export OPENAI_API_KEY="${OPENAI_API_KEY:-}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
mkdir -p "${PAPERCLIP_HOME}"
|
| 42 |
|
|
@@ -64,9 +68,9 @@ if [ -z "${PAPERCLIP_AGENT_JWT_SECRET:-}" ]; then
|
|
| 64 |
fi
|
| 65 |
|
| 66 |
# ββ Validate LLM providers βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 67 |
-
if [ -z "${GEMINI_API_KEY:-}" ] && [ -z "${ANTHROPIC_API_KEY:-}" ] && [ -z "${OPENAI_API_KEY:-}" ]; then
|
| 68 |
echo "β οΈ WARNING: No LLM provider configured"
|
| 69 |
-
echo " Set at least one of: GEMINI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY"
|
| 70 |
echo " Agents will fail to run without an LLM provider"
|
| 71 |
echo ""
|
| 72 |
fi
|
|
@@ -288,6 +292,18 @@ cleanup() {
|
|
| 288 |
}
|
| 289 |
trap cleanup SIGTERM SIGINT
|
| 290 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
# ββ Ensure paperclip user owns runtime dirs ββββββββββββββββββββββββββββββββββ
|
| 292 |
chown -R paperclip:paperclip /app /paperclip 2>/dev/null || true
|
| 293 |
|
|
|
|
| 35 |
|
| 36 |
# LLM API keys
|
| 37 |
export GEMINI_API_KEY="${GEMINI_API_KEY:-}"
|
|
|
|
| 38 |
export OPENAI_API_KEY="${OPENAI_API_KEY:-}"
|
| 39 |
+
# Anthropic/Claude Code β set one or neither:
|
| 40 |
+
# ANTHROPIC_AUTH_TOKEN : subscription mode (OAuth token from ~/.claude/.credentials.json)
|
| 41 |
+
# ANTHROPIC_API_KEY : API key mode (sk-ant-...)
|
| 42 |
+
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-}"
|
| 43 |
+
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}"
|
| 44 |
|
| 45 |
mkdir -p "${PAPERCLIP_HOME}"
|
| 46 |
|
|
|
|
| 68 |
fi
|
| 69 |
|
| 70 |
# ββ Validate LLM providers βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 71 |
+
if [ -z "${GEMINI_API_KEY:-}" ] && [ -z "${ANTHROPIC_API_KEY:-}" ] && [ -z "${ANTHROPIC_AUTH_TOKEN:-}" ] && [ -z "${OPENAI_API_KEY:-}" ]; then
|
| 72 |
echo "β οΈ WARNING: No LLM provider configured"
|
| 73 |
+
echo " Set at least one of: GEMINI_API_KEY, ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, OPENAI_API_KEY"
|
| 74 |
echo " Agents will fail to run without an LLM provider"
|
| 75 |
echo ""
|
| 76 |
fi
|
|
|
|
| 292 |
}
|
| 293 |
trap cleanup SIGTERM SIGINT
|
| 294 |
|
| 295 |
+
# ββ Codex API key config βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 296 |
+
# Codex default auth mode is "chatgpt" (OAuth). Setting forced_login_method="api"
|
| 297 |
+
# makes it read OPENAI_API_KEY from env instead of prompting for browser login.
|
| 298 |
+
if [ -n "${OPENAI_API_KEY:-}" ]; then
|
| 299 |
+
mkdir -p /home/paperclip/.codex
|
| 300 |
+
cat > /home/paperclip/.codex/config.toml <<'TOMLEOF'
|
| 301 |
+
forced_login_method = "api"
|
| 302 |
+
TOMLEOF
|
| 303 |
+
chmod 600 /home/paperclip/.codex/config.toml
|
| 304 |
+
chown -R paperclip:paperclip /home/paperclip/.codex
|
| 305 |
+
fi
|
| 306 |
+
|
| 307 |
# ββ Ensure paperclip user owns runtime dirs ββββββββββββββββββββββββββββββββββ
|
| 308 |
chown -R paperclip:paperclip /app /paperclip 2>/dev/null || true
|
| 309 |
|