From 76126d4ad6d26d8dbafac23f540d50170f942fe2 Mon Sep 17 00:00:00 2001 From: Jonathan Flatt Date: Wed, 21 May 2025 00:05:39 +0000 Subject: [PATCH] initial commit --- .env.example | 31 +++++++++++++++++++---------- CLAUDE.md | 13 ++++++++++-- README.md | 6 ++++++ claudecode-entrypoint.sh | 6 +++--- cli/README.md | 10 +++++----- cli/setup.sh | 2 +- docs/complete-workflow.md | 4 ++-- docs/container-limitations.md | 10 +++++++--- docs/github-workflow.md | 14 ++++++------- docs/workflow.md | 8 ++++---- src/controllers/githubController.js | 2 +- src/services/claudeService.js | 6 +++++- src/utils/sanitize.js | 5 +++-- test/test-claude-response.sh | 2 +- test/test-container-privileged.sh | 2 +- test/test-full-flow.sh | 2 +- test/test-payload.json | 2 +- test/test-webhook-response.js | 2 +- test/test-with-auth.sh | 2 +- 19 files changed, 82 insertions(+), 47 deletions(-) diff --git a/.env.example b/.env.example index 46e9a34..85f34dd 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,18 @@ # Application Configuration NODE_ENV=development -PORT=3003 +PORT=3002 # GitHub Webhook Settings GITHUB_WEBHOOK_SECRET=your_webhook_secret_here GITHUB_TOKEN=ghp_your_github_token_here -# Bot Configuration (REQUIRED) - the GitHub mention that triggers the bot -BOT_USERNAME=ClaudeBot +# Bot Configuration (REQUIRED) +BOT_USERNAME=@ClaudeBot +BOT_EMAIL=claude@example.com + +# Security Configuration +AUTHORIZED_USERS=admin,username2,username3 +DEFAULT_AUTHORIZED_USER=admin # Default GitHub Configuration for CLI DEFAULT_GITHUB_OWNER=your-org @@ -15,19 +20,25 @@ DEFAULT_GITHUB_USER=your-username DEFAULT_BRANCH=main # Claude API Settings -CLAUDE_API_AUTH_REQUIRED=1 -CLAUDE_API_AUTH_TOKEN=your_auth_token_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here # Container Settings CLAUDE_USE_CONTAINERS=1 -CLAUDE_CONTAINER_IMAGE=claudecode:latest +CLAUDE_CONTAINER_IMAGE=claude-code-runner:latest REPO_CACHE_DIR=/tmp/repo-cache REPO_CACHE_MAX_AGE_MS=3600000 +CONTAINER_LIFETIME_MS=7200000 # Container execution timeout in milliseconds (default: 2 hours) -# AWS Bedrock Credentials for Claude -AWS_ACCESS_KEY_ID=secret_key -AWS_SECRET_ACCESS_KEY=access_key -AWS_REGION=us-east-2 +# AWS Bedrock Credentials for Claude (if using Bedrock) +AWS_ACCESS_KEY_ID=your_aws_access_key_id +AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key +AWS_REGION=us-east-1 CLAUDE_CODE_USE_BEDROCK=1 ANTHROPIC_MODEL=us.anthropic.claude-3-7-sonnet-20250219-v1:0 +# AWS Profile (uncomment if using AWS profiles instead of direct credentials) +# USE_AWS_PROFILE=true +# AWS_PROFILE=claude-webhook + +# Test Configuration +TEST_REPO_FULL_NAME=owner/repo \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 88ef08a..ef1606e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Claude GitHub Webhook -This repository contains a webhook service that integrates Claude with GitHub, allowing Claude to respond to mentions in GitHub comments and help with repository tasks. When someone mentions `@MCPClaude` in a GitHub issue or PR comment, the system processes the command with Claude Code and returns a helpful response. +This repository contains a webhook service that integrates Claude with GitHub, allowing Claude to respond to mentions in GitHub comments and help with repository tasks. When someone mentions the configured bot username (configured via environment variables) in a GitHub issue or PR comment, the system processes the command with Claude Code and returns a helpful response. ## Documentation Structure @@ -95,7 +95,7 @@ The repository includes a `.devcontainer` configuration that allows Claude Code This configuration enables the use of `--dangerously-skip-permissions` flag when running Claude Code CLI. ### Workflow -1. GitHub comment with `@MCPClaude` triggers a webhook event +1. GitHub comment with bot mention (configured via BOT_USERNAME) triggers a webhook event 2. Express server receives the webhook at `/api/webhooks/github` 3. Service extracts the command and processes it with Claude in a Docker container 4. Claude analyzes the repository and responds to the command @@ -126,6 +126,15 @@ The `awsCredentialProvider.js` utility handles credential retrieval and rotation - Container execution settings - Webhook URL and port configuration +### Required Environment Variables +- `BOT_USERNAME`: GitHub username that the bot responds to (e.g., `@ClaudeBot`) +- `DEFAULT_AUTHORIZED_USER`: Default GitHub username authorized to use the bot (if AUTHORIZED_USERS is not set) +- `AUTHORIZED_USERS`: Comma-separated list of GitHub usernames authorized to use the bot +- `BOT_EMAIL`: Email address used for git commits made by the bot +- `GITHUB_WEBHOOK_SECRET`: Secret for validating GitHub webhook payloads +- `GITHUB_TOKEN`: GitHub token for API access +- `ANTHROPIC_API_KEY`: Anthropic API key for Claude access + ## Code Style Guidelines - JavaScript with Node.js - Use async/await for asynchronous operations diff --git a/README.md b/README.md index 48d667b..ef8b5af 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,15 @@ For comprehensive documentation, see: - This setting is required to prevent infinite loops - Example: `BOT_USERNAME=@MyBot` - No default is provided - this must be explicitly configured + - Set `BOT_EMAIL` for the email address used in git commits made by the bot + - Set `DEFAULT_AUTHORIZED_USER` to specify the default GitHub username authorized to use the bot + - Use `AUTHORIZED_USERS` for a comma-separated list of GitHub usernames allowed to use the bot **e. Server Port and Other Settings** - By default, the server runs on port 3000 - To use a different port, set the `PORT` environment variable in your `.env` file + - Set `DEFAULT_GITHUB_OWNER` and `DEFAULT_GITHUB_USER` for CLI defaults when using the webhook CLI + - Set `TEST_REPO_FULL_NAME` to configure the default repository for test scripts - Review other settings in the `.env` file for customization options **AWS Credentials**: The service now supports multiple AWS authentication methods: @@ -264,6 +269,7 @@ To enable container-based execution: CLAUDE_CONTAINER_IMAGE=claudecode:latest REPO_CACHE_DIR=/path/to/cache # Optional REPO_CACHE_MAX_AGE_MS=3600000 # Optional, defaults to 1 hour (in milliseconds) + CONTAINER_LIFETIME_MS=7200000 # Optional, container execution timeout in milliseconds (defaults to 2 hours) ``` ### Container Test Utility diff --git a/claudecode-entrypoint.sh b/claudecode-entrypoint.sh index 06a1b2d..19ea424 100755 --- a/claudecode-entrypoint.sh +++ b/claudecode-entrypoint.sh @@ -41,9 +41,9 @@ else sudo -u node git checkout main >&2 || sudo -u node git checkout master >&2 fi -# Configure git for commits -sudo -u node git config --global user.email "claude@mcp.ai" -sudo -u node git config --global user.name "MCPClaude" +# Configure git for commits using environment variables (with defaults) +sudo -u node git config --global user.email "${BOT_EMAIL:-claude@example.com}" +sudo -u node git config --global user.name "${BOT_USERNAME:-ClaudeBot}" # Configure Anthropic API key export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}" diff --git a/cli/README.md b/cli/README.md index 6460821..054916d 100644 --- a/cli/README.md +++ b/cli/README.md @@ -25,7 +25,7 @@ GITHUB_TOKEN=your-github-token ### Basic Usage ```bash -# Using the wrapper script (defaults to Cheffromspace user) +# Using the wrapper script (defaults to the DEFAULT_GITHUB_OWNER env variable) ./claude-webhook myrepo "Your command for Claude" # With explicit owner @@ -38,7 +38,7 @@ node cli/webhook-cli.js --repo myrepo --command "Your command" ### Options - `-r, --repo `: GitHub repository (format: owner/repo or repo) [required] - - If only repo name is provided, defaults to `Cheffromspace/repo` + - If only repo name is provided, defaults to `${DEFAULT_GITHUB_OWNER}/repo` - `-c, --command `: Command to send to Claude [required] - `-i, --issue `: Issue number (default: 1) - `-p, --pr`: Treat as pull request instead of issue @@ -51,7 +51,7 @@ node cli/webhook-cli.js --repo myrepo --command "Your command" ### Examples ```bash -# Basic issue comment (defaults to Cheffromspace user) +# Basic issue comment (uses default owner) ./claude-webhook myrepo "Analyze the code structure" # With explicit owner @@ -79,7 +79,7 @@ The CLI will display: Example output: ``` -🚀 Sending command to Claude for Cheffromspace/myrepo... +🚀 Sending command to Claude for owner/myrepo... 📋 Command: Analyze the code structure 📄 Type: Issue @@ -93,7 +93,7 @@ Here's an analysis of the code structure... 📍 Context: { - "repo": "Cheffromspace/myrepo", + "repo": "owner/myrepo", "issue": 1, "type": "issue_comment" } diff --git a/cli/setup.sh b/cli/setup.sh index 6bd284b..6487221 100755 --- a/cli/setup.sh +++ b/cli/setup.sh @@ -34,5 +34,5 @@ echo "2. Run the CLI with: ./claude-webhook myrepo \"Your command\"" echo echo "Examples:" echo " ./claude-webhook myrepo \"List all files\"" -echo " ./claude-webhook Cheffromspace/myrepo \"Analyze code structure\"" +echo " ./claude-webhook owner/myrepo \"Analyze code structure\"" echo " ./claude-webhook myrepo \"Review PR\" -p -b feature-branch" \ No newline at end of file diff --git a/docs/complete-workflow.md b/docs/complete-workflow.md index 4fa1233..2fba79f 100644 --- a/docs/complete-workflow.md +++ b/docs/complete-workflow.md @@ -35,8 +35,8 @@ GitHub → Webhook Service → Docker Container → Claude API ### 3. Command Extraction -1. Checks for `@MCPClaude` mention in comment body -2. Extracts command using regex: `/@MCPClaude\s+(.*)/s` +1. Checks for bot mention in comment body (using BOT_USERNAME env variable) +2. Extracts command using regex pattern based on configured bot username 3. Captures: - Repository full name - Issue/PR number diff --git a/docs/container-limitations.md b/docs/container-limitations.md index 276be62..cca7264 100644 --- a/docs/container-limitations.md +++ b/docs/container-limitations.md @@ -31,6 +31,10 @@ These issues may be related to: - Docker engine configuration - Claude CLI output mechanism +### Container Execution Timeout + +By default, containers have a 2-hour lifetime before being terminated. This can be configured via the `CONTAINER_LIFETIME_MS` environment variable (in milliseconds). For complex tasks requiring more time, you can increase this value. + ### Claude Authentication Another issue was authentication for the Claude CLI inside the container: @@ -63,7 +67,7 @@ To properly enable full Claude execution in containers, consider: The current workaround, as implemented in `claudeService.js`, provides a reliable and useful service while addressing these limitations: -1. For known repositories (like MCPControl), we provide curated responses +1. For known repositories (configured via environment variables), we provide curated responses 2. For other repositories, we automatically: - Clone the repository (or use cache) - Analyze its structure @@ -77,8 +81,8 @@ This approach provides value while the container execution issues are resolved. You can test the current implementation using the provided test utilities: ```bash -# Test with MCPControl repository (uses predefined response) -./test-container.js Cheffromspace/MCPControl "What is this repository about?" +# Test with a repository (uses predefined response if known) +./test-container.js owner/repo "What is this repository about?" # Test with any other repository (uses automatic analysis) ./test-container.js n8n-io/n8n "What is this repository about?" diff --git a/docs/github-workflow.md b/docs/github-workflow.md index 948da63..bbffb40 100644 --- a/docs/github-workflow.md +++ b/docs/github-workflow.md @@ -1,14 +1,14 @@ -# GitHub Workflow with MCPClaude +# GitHub Workflow with Claude Webhook This document describes how the GitHub webhook integration works with Claude Code CLI. ## Overview -When someone mentions `@MCPClaude` in a GitHub issue or pull request comment, the following workflow is triggered: +When someone mentions the configured bot (via BOT_USERNAME environment variable) in a GitHub issue or pull request comment, the following workflow is triggered: 1. GitHub sends a webhook to our service 2. The service validates the webhook signature -3. If valid, it extracts the command after `@MCPClaude` +3. If valid, it extracts the command after the bot username 4. A Docker container is spun up with Claude Code CLI 5. The repository is cloned and the correct branch is checked out 6. Claude Code executes the command with full GitHub CLI access @@ -38,9 +38,9 @@ Each request runs in an isolated Docker container with: ## Supported Events -- **Issue Comments**: When `@MCPClaude` is mentioned in an issue comment -- **Pull Request Comments**: When `@MCPClaude` is mentioned in a PR comment -- **Pull Request Review Comments**: When `@MCPClaude` is mentioned in a PR review +- **Issue Comments**: When the bot is mentioned in an issue comment +- **Pull Request Comments**: When the bot is mentioned in a PR comment +- **Pull Request Review Comments**: When the bot is mentioned in a PR review ## Authentication @@ -54,7 +54,7 @@ The following credentials are required: In a GitHub issue or PR comment: ``` -@MCPClaude Please analyze the performance of the current implementation and suggest optimizations. +@ClaudeBot Please analyze the performance of the current implementation and suggest optimizations. ``` Claude will: diff --git a/docs/workflow.md b/docs/workflow.md index 9542de4..16c1e3b 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -6,11 +6,11 @@ This document describes the workflow for how GitHub comments trigger Claude resp ```mermaid graph TD - A[GitHub Comment with @MCPClaude] -->|Triggers| B[GitHub Webhook Event] + A[GitHub Comment with bot mention] -->|Triggers| B[GitHub Webhook Event] B -->|POST Request| C[claude.jonathanflatt.org API] C -->|/api/webhooks/github| D[Express Server] D -->|githubController.handleWebhook| E[Webhook Verification] - E -->|issue_comment event| F[Check for @MCPClaude mention] + E -->|issue_comment event| F[Check for bot mention] F -->|Extract command| G[claudeService.processCommand] G -->|Clone repository| H[Create temporary directory] H -->|Run Claude Code CLI| I[claude --print command] @@ -22,7 +22,7 @@ graph TD ## Detailed Flow 1. **GitHub Comment Trigger** - - User creates a GitHub comment mentioning "@MCPClaude" with a command + - User creates a GitHub comment mentioning the bot with a command - GitHub detects the comment creation event 2. **Webhook Delivery** @@ -38,7 +38,7 @@ graph TD - Validates the event type is `issue_comment` and action is `created` 5. **Command Extraction** - - For issue comments, the code checks for "@MCPClaude" mentions + - For issue comments, the code checks for mentions of the configured bot username - If found, it extracts the command text that follows the mention 6. **Claude Service Processing** diff --git a/src/controllers/githubController.js b/src/controllers/githubController.js index 610bbd1..bef50bc 100644 --- a/src/controllers/githubController.js +++ b/src/controllers/githubController.js @@ -105,7 +105,7 @@ async function handleWebhook(req, res) { // Check if the comment author is authorized const authorizedUsers = process.env.AUTHORIZED_USERS ? process.env.AUTHORIZED_USERS.split(',').map(user => user.trim()) : - ['Cheffromspace']; // Default authorized users + [process.env.DEFAULT_AUTHORIZED_USER || 'admin']; // Default authorized user const commentAuthor = comment.user.login; if (!authorizedUsers.includes(commentAuthor)) { diff --git a/src/services/claudeService.js b/src/services/claudeService.js index a0b951f..7383eb1 100644 --- a/src/services/claudeService.js +++ b/src/services/claudeService.js @@ -195,9 +195,13 @@ Please complete this task fully and autonomously.`; } }; + // Get container lifetime from environment variable or use default (2 hours) + const containerLifetimeMs = parseInt(process.env.CONTAINER_LIFETIME_MS, 10) || 7200000; // 2 hours in milliseconds + logger.info({ containerLifetimeMs }, 'Setting container lifetime'); + const result = await execAsync(dockerCommand, { maxBuffer: 10 * 1024 * 1024, // 10MB buffer - timeout: 600000 // 10 minute timeout + timeout: containerLifetimeMs // Container lifetime in milliseconds }); // Clean up temporary files used for command passing diff --git a/src/utils/sanitize.js b/src/utils/sanitize.js index b88e9a4..54abaa6 100644 --- a/src/utils/sanitize.js +++ b/src/utils/sanitize.js @@ -27,8 +27,9 @@ function sanitizeBotMentions(text) { // Look for the username with @ symbol anywhere in the text const botMentionRegex = new RegExp(escapedUsername, 'gi'); - // Replace mentions with a sanitized version (remove @ symbol) - const sanitized = text.replace(botMentionRegex, 'MCPControl'); + // Replace mentions with a sanitized version (remove @ symbol if present) + const sanitizedName = BOT_USERNAME.startsWith('@') ? BOT_USERNAME.substring(1) : BOT_USERNAME; + const sanitized = text.replace(botMentionRegex, sanitizedName); // If sanitization occurred, log it if (sanitized !== text) { diff --git a/test/test-claude-response.sh b/test/test-claude-response.sh index 166aebc..27d5183 100755 --- a/test/test-claude-response.sh +++ b/test/test-claude-response.sh @@ -10,7 +10,7 @@ docker run --rm \ --cap-add=AUDIT_WRITE \ --cap-add=SYS_ADMIN \ -v $HOME/.aws:/home/node/.aws:ro \ - -e REPO_FULL_NAME="Cheffromspace/MCPControl" \ + -e REPO_FULL_NAME="${TEST_REPO_FULL_NAME:-owner/repo}" \ -e ISSUE_NUMBER="1" \ -e IS_PULL_REQUEST="false" \ -e COMMAND="What is this repository?" \ diff --git a/test/test-container-privileged.sh b/test/test-container-privileged.sh index a18df7b..3f66012 100755 --- a/test/test-container-privileged.sh +++ b/test/test-container-privileged.sh @@ -10,7 +10,7 @@ docker run --rm \ --cap-add=AUDIT_WRITE \ --cap-add=SYS_ADMIN \ -v $HOME/.aws:/home/node/.aws:ro \ - -e REPO_FULL_NAME="Cheffromspace/MCPControl" \ + -e REPO_FULL_NAME="${TEST_REPO_FULL_NAME:-owner/repo}" \ -e ISSUE_NUMBER="1" \ -e IS_PULL_REQUEST="false" \ -e COMMAND="echo test" \ diff --git a/test/test-full-flow.sh b/test/test-full-flow.sh index bd29e88..89754a8 100755 --- a/test/test-full-flow.sh +++ b/test/test-full-flow.sh @@ -3,7 +3,7 @@ echo "Testing full entrypoint flow..." docker run --rm -i \ -v $HOME/.aws:/home/node/.aws:ro \ - -e REPO_FULL_NAME="Cheffromspace/MCPControl" \ + -e REPO_FULL_NAME="${TEST_REPO_FULL_NAME:-owner/repo}" \ -e ISSUE_NUMBER="1" \ -e IS_PULL_REQUEST="false" \ -e COMMAND="echo 'test'" \ diff --git a/test/test-payload.json b/test/test-payload.json index 2ad9487..4b362e0 100644 --- a/test/test-payload.json +++ b/test/test-payload.json @@ -2,7 +2,7 @@ "action": "created", "comment": { "id": 1234567890, - "body": "@MCPClaude What is the purpose of the authentication function in this repo?", + "body": "@ClaudeBot What is the purpose of the authentication function in this repo?", "user": { "login": "testuser" } diff --git a/test/test-webhook-response.js b/test/test-webhook-response.js index 09c65bc..57c1c2f 100755 --- a/test/test-webhook-response.js +++ b/test/test-webhook-response.js @@ -19,7 +19,7 @@ const payload = { }, comment: { id: 123, - body: '@MCPClaude Test command for webhook response', + body: `${process.env.BOT_USERNAME || '@ClaudeBot'} Test command for webhook response`, user: { login: 'testuser' } diff --git a/test/test-with-auth.sh b/test/test-with-auth.sh index 4bdfb71..3634e93 100755 --- a/test/test-with-auth.sh +++ b/test/test-with-auth.sh @@ -10,7 +10,7 @@ docker run --rm \ --cap-add=AUDIT_WRITE \ --cap-add=SYS_ADMIN \ -v $HOME/.aws:/home/node/.aws:ro \ - -e REPO_FULL_NAME="Cheffromspace/MCPControl" \ + -e REPO_FULL_NAME="${TEST_REPO_FULL_NAME:-owner/repo}" \ -e ISSUE_NUMBER="1" \ -e IS_PULL_REQUEST="false" \ -e COMMAND="What is this repository?" \