From 1b2603b8bead53515a54d16419857d945e2d346f Mon Sep 17 00:00:00 2001 From: vincent nadal Date: Tue, 17 Feb 2026 19:44:18 +0100 Subject: [PATCH] support a proxy and set model as variables --- .env.example | 7 +++++ README.md | 26 ++++++++++++++++++ docker-compose.yml | 4 +++ shannon | 55 +++++++++++++++++++++++++-------------- src/ai/claude-executor.ts | 14 +++++++++- 5 files changed, 86 insertions(+), 20 deletions(-) diff --git a/.env.example b/.env.example index b9aab986..5b9613d3 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,9 @@ # Recommended output token configuration for larger tool outputs CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 +CLAUDE_MODEL=claude-sonnet-4-5-20250929 +# Optional model override specifically for Haiku +# CLAUDE_HAIKU_MODEL=claude-haiku-4-5-20251001 # ============================================================================= # OPTION 1: Direct Anthropic (default, no router) @@ -12,6 +15,10 @@ ANTHROPIC_API_KEY=your-api-key-here # OR use OAuth token instead # CLAUDE_CODE_OAUTH_TOKEN=your-oauth-token-here +# Optional: Route Claude API calls through a custom proxy endpoint +# CLAUDE_API_BASE_URL=https://your-proxy.example.com +# CLAUDE_API_AUTH_TOKEN=your-proxy-auth-token + # ============================================================================= # OPTION 2: Router Mode (use alternative providers) # ============================================================================= diff --git a/README.md b/README.md index ffd82f7e..d83bb2b1 100644 --- a/README.md +++ b/README.md @@ -121,11 +121,22 @@ cd shannon # Option A: Export environment variables export ANTHROPIC_API_KEY="your-api-key" # or CLAUDE_CODE_OAUTH_TOKEN export CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 # recommended +export CLAUDE_MODEL="claude-sonnet-4-5-20250929" # optional override +# export CLAUDE_HAIKU_MODEL="claude-haiku-4-5-20251001" # optional Haiku-specific override +# Optional: route Claude calls through your own proxy +# export CLAUDE_API_BASE_URL="https://your-proxy.example.com" +# export CLAUDE_API_AUTH_TOKEN="your-proxy-auth-token" # Option B: Create a .env file cat > .env << 'EOF' ANTHROPIC_API_KEY=your-api-key CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 +CLAUDE_MODEL=claude-sonnet-4-5-20250929 +# Optional: +# CLAUDE_HAIKU_MODEL=claude-haiku-4-5-20251001 +# Optional: +# CLAUDE_API_BASE_URL=https://your-proxy.example.com +# CLAUDE_API_AUTH_TOKEN=your-proxy-auth-token EOF # 3. Run a pentest @@ -297,6 +308,21 @@ rules: If your application uses two-factor authentication, simply add the TOTP secret to your config file. The AI will automatically generate the required codes during testing. +### Custom Claude Proxy Endpoint + +You can route Claude SDK traffic through a custom proxy without enabling Router Mode. + +```bash +CLAUDE_API_BASE_URL=https://your-proxy.example.com +# Optional if your proxy requires token auth +CLAUDE_API_AUTH_TOKEN=your-proxy-auth-token +``` + +Environment variable precedence: + +- `CLAUDE_API_BASE_URL` overrides `ANTHROPIC_BASE_URL` +- `CLAUDE_API_AUTH_TOKEN` overrides `ANTHROPIC_AUTH_TOKEN` + ### [EXPERIMENTAL - UNSUPPORTED] Router Mode (Alternative Providers) Shannon can experimentally route requests through alternative AI providers using claude-code-router. This mode is not officially supported and is intended primarily for: diff --git a/docker-compose.yml b/docker-compose.yml index e54ba1ff..c9376dab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,9 +20,13 @@ services: environment: - TEMPORAL_ADDRESS=temporal:7233 - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} + - CLAUDE_API_BASE_URL=${CLAUDE_API_BASE_URL:-} # Optional: custom proxy endpoint for Claude API + - CLAUDE_API_AUTH_TOKEN=${CLAUDE_API_AUTH_TOKEN:-} # Optional: auth token for custom proxy - ANTHROPIC_BASE_URL=${ANTHROPIC_BASE_URL:-} # Optional: route through claude-code-router - ANTHROPIC_AUTH_TOKEN=${ANTHROPIC_AUTH_TOKEN:-} # Auth token for router - ROUTER_DEFAULT=${ROUTER_DEFAULT:-} # Model name when using router (e.g., "gemini,gemini-2.5-pro") + - CLAUDE_MODEL=${CLAUDE_MODEL:-claude-sonnet-4-5-20250929} + - CLAUDE_HAIKU_MODEL=${CLAUDE_HAIKU_MODEL:-} - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-} - CLAUDE_CODE_MAX_OUTPUT_TOKENS=${CLAUDE_CODE_MAX_OUTPUT_TOKENS:-64000} depends_on: diff --git a/shannon b/shannon index 70a74e0b..4ba6b505 100755 --- a/shannon +++ b/shannon @@ -8,17 +8,34 @@ case "$OSTYPE" in msys*) export MSYS_NO_PATHCONV=1 ;; esac -# Detect Podman vs Docker and set compose files accordingly -# Podman doesn't support host-gateway, so we only include the Docker override for actual Docker -COMPOSE_BASE="docker-compose.yml" -if command -v podman &>/dev/null; then - # Podman detected (either native or via Docker Desktop shim) - use base config only - COMPOSE_OVERRIDE="" +# Detect a working compose command and compose files. +# Prefer Docker Compose v2, then docker-compose, then podman compose. +COMPOSE_CMD=() +COMPOSE_ENGINE="" +if docker compose version >/dev/null 2>&1; then + COMPOSE_CMD=(docker compose) + COMPOSE_ENGINE="docker" +elif command -v docker-compose >/dev/null 2>&1; then + COMPOSE_CMD=(docker-compose) + COMPOSE_ENGINE="docker" +elif podman compose version >/dev/null 2>&1; then + COMPOSE_CMD=(podman compose) + COMPOSE_ENGINE="podman" else - # Docker detected - include extra_hosts override for Linux localhost access - COMPOSE_OVERRIDE="-f docker-compose.docker.yml" + echo "ERROR: No supported compose command found." + echo "Install Docker Compose (docker compose / docker-compose) or Podman Compose." + exit 1 fi -COMPOSE_FILE="$COMPOSE_BASE" + +COMPOSE_FILES=(-f docker-compose.yml) +# Podman doesn't support host-gateway; only include Docker override when using Docker. +if [ "$COMPOSE_ENGINE" = "docker" ]; then + COMPOSE_FILES+=(-f docker-compose.docker.yml) +fi + +compose() { + "${COMPOSE_CMD[@]}" "${COMPOSE_FILES[@]}" "$@" +} # Load .env if present if [ -f .env ]; then @@ -90,7 +107,7 @@ parse_args() { # Check if Temporal is running and healthy is_temporal_ready() { - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T temporal \ + compose exec -T temporal \ temporal operator cluster health --address localhost:7233 2>/dev/null | grep -q "SERVING" } @@ -100,7 +117,7 @@ ensure_containers() { # Docker compose will only recreate if the mount actually changed if [ -n "$OUTPUT_DIR" ]; then echo "Ensuring worker has correct output mount..." - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE up -d worker 2>/dev/null || true + compose up -d worker 2>/dev/null || true fi # Quick check: if Temporal is already healthy, we're good @@ -113,9 +130,9 @@ ensure_containers() { if [ "$REBUILD" = "true" ]; then # Force rebuild without cache (use when code changes aren't being picked up) echo "Rebuilding with --no-cache..." - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE build --no-cache worker + compose build --no-cache worker fi - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE up -d --build + compose up -d --build # Wait for Temporal to be ready echo "Waiting for Temporal to be ready..." @@ -184,7 +201,7 @@ cmd_start() { # Handle ROUTER flag - start claude-code-router for multi-model support if [ "$ROUTER" = "true" ]; then # Check if router is already running - if docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router ps router 2>/dev/null | grep -q "running"; then + if compose --profile router ps router 2>/dev/null | grep -q "running"; then echo "Router already running, skipping startup..." else echo "Starting claude-code-router..." @@ -195,7 +212,7 @@ cmd_start() { fi # Start router with profile - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router up -d router + compose --profile router up -d router # Give router a few seconds to start (health check disabled for now - TODO: debug later) echo "Waiting for router to start..." @@ -235,7 +252,7 @@ cmd_start() { [ -n "$WORKSPACE" ] && ARGS="$ARGS --workspace $WORKSPACE" # Run the client to submit workflow - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T worker \ + compose exec -T worker \ node dist/temporal/client.js "$URL" "$CONTAINER_REPO" $ARGS } @@ -299,7 +316,7 @@ cmd_workspaces() { # Ensure containers are running (need worker to execute node) ensure_containers - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T worker \ + compose exec -T worker \ node dist/temporal/workspaces.js } @@ -307,9 +324,9 @@ cmd_stop() { parse_args "$@" if [ "$CLEAN" = "true" ]; then - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router down -v + compose --profile router down -v else - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router down + compose --profile router down fi } diff --git a/src/ai/claude-executor.ts b/src/ai/claude-executor.ts index ceab2d67..b599c04a 100644 --- a/src/ai/claude-executor.ts +++ b/src/ai/claude-executor.ts @@ -229,9 +229,21 @@ export async function runClaudePrompt( if (process.env.CLAUDE_CODE_OAUTH_TOKEN) { sdkEnv.CLAUDE_CODE_OAUTH_TOKEN = process.env.CLAUDE_CODE_OAUTH_TOKEN; } + const anthropicBaseUrl = process.env.CLAUDE_API_BASE_URL || process.env.ANTHROPIC_BASE_URL; + if (anthropicBaseUrl) { + sdkEnv.ANTHROPIC_BASE_URL = anthropicBaseUrl; + } + const anthropicAuthToken = process.env.CLAUDE_API_AUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN; + if (anthropicAuthToken) { + sdkEnv.ANTHROPIC_AUTH_TOKEN = anthropicAuthToken; + } + const claudeModel = + process.env.CLAUDE_MODEL || + process.env.CLAUDE_HAIKU_MODEL || + 'claude-sonnet-4-5-20250929'; const options = { - model: 'claude-sonnet-4-5-20250929', + model: claudeModel, maxTurns: 10_000, cwd: sourceDir, permissionMode: 'bypassPermissions' as const,