From 55cd3a249fa7c34b8f3115434f5ad74988639c5b Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Tue, 5 May 2026 14:38:42 -0400 Subject: [PATCH 1/6] feat: Add containerized Claude agent Co-Authored-By: Claude Opus 4.5 --- agents/claude/claude_agent/Containerfile | 149 +++++++++ agents/claude/claude_agent/README.md | 291 ++++++++++++++++++ .../claude_agent/deployment-vertex.yaml | 218 +++++++++++++ agents/claude/claude_agent/deployment.yaml | 176 +++++++++++ agents/claude/claude_agent/entrypoint.sh | 199 ++++++++++++ 5 files changed, 1033 insertions(+) create mode 100644 agents/claude/claude_agent/Containerfile create mode 100644 agents/claude/claude_agent/README.md create mode 100644 agents/claude/claude_agent/deployment-vertex.yaml create mode 100644 agents/claude/claude_agent/deployment.yaml create mode 100644 agents/claude/claude_agent/entrypoint.sh diff --git a/agents/claude/claude_agent/Containerfile b/agents/claude/claude_agent/Containerfile new file mode 100644 index 00000000..081384d3 --- /dev/null +++ b/agents/claude/claude_agent/Containerfile @@ -0,0 +1,149 @@ +# Claude Code Container for Red Hat OpenShift AI +# +# A customer-facing container image for running Claude Code on RHOAI. +# Defaults to headless (non-interactive) mode for programmatic use. +# Interactive mode available by overriding CMD at runtime. +# +# Features: +# - UBI 10 minimal base (RHEL-compatible, supported) +# - Claude Code installed via npm (version-pinned) +# - Non-root user compatible with restricted-v2 SCC +# - Configurable API endpoint (Anthropic, vLLM, OGX) +# - Skills injectable at runtime via ConfigMap/PVC mount +# - MCP configuration injectable at runtime via environment variables: +# MCP_CONFIG_FILE - path to mounted JSON config file +# MCP_CONFIG_JSON - inline JSON string with MCP server definitions +# +# Build: +# podman build -t claude-code:latest -f Containerfile . +# +# Build with specific version: +# podman build --build-arg CLAUDE_CODE_VERSION=2.1.123 -t claude-code:2.1.123 . + +FROM registry.access.redhat.com/ubi10/ubi-minimal:10.1 + +LABEL name="claude-code" \ + vendor="Red Hat" \ + summary="Claude Code agent for OpenShift AI" \ + description="Container image for running Anthropic's Claude Code CLI on Red Hat OpenShift AI" + +# ============================================================================= +# Build Arguments +# ============================================================================= + +# Claude Code version to install (pinned for reproducibility) +ARG CLAUDE_CODE_VERSION=2.1.123 + +# Node.js version (major version, installs latest in that series) +ARG NODE_VERSION=22 + +# User configuration +ARG USER_NAME=claude-agent +ARG USER_UID=1001 +ARG USER_GID=0 + +# ============================================================================= +# Install System Dependencies +# ============================================================================= + +# Install Node.js and minimal utilities +# - nodejs: Required for Claude Code (npm package) +# - npm: Package manager for installing Claude Code +# - git: Version control (agent may need to clone repos) +# - curl: HTTP client (agent may need to fetch resources) +# - jq: JSON processor (useful for scripting) +RUN microdnf install -y --nodocs \ + nodejs \ + npm \ + git \ + curl \ + jq \ + && microdnf clean all \ + && rm -rf /var/cache/yum + +# ============================================================================= +# Create Non-Root User +# ============================================================================= + +# Create user with specific UID for OpenShift compatibility +# GID=0 (root group) is required for OpenShift's random UID assignment. +# OpenShift runs containers with a random UID that is a member of GID 0. +# Directories below use chmod 775 so GID 0 members have write access. +RUN useradd -u ${USER_UID} -g ${USER_GID} -d /home/${USER_NAME} -m -s /bin/bash ${USER_NAME} + +# ============================================================================= +# Install Claude Code +# ============================================================================= + +# Install Claude Code globally with pinned version +RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \ + && npm cache clean --force + +# Verify installation +RUN claude --version + +# ============================================================================= +# Create Directory Structure +# ============================================================================= + +# Skills directory - empty, mountable via ConfigMap/PVC +# Customers inject their skills here at deploy time +# Each skill should be a subdirectory containing a SKILL.md file +# +# Mount at runtime: +# podman run -v /path/to/skills:/opt/skills:ro,z ... +# Or in OpenShift, mount a ConfigMap or PVC to /opt/skills +RUN mkdir -p /opt/skills \ + && chown -R ${USER_UID}:${USER_GID} /opt/skills \ + && chmod -R 775 /opt/skills + +# Workspace directory - for agent working files +# Can be mounted with PVC for persistence +RUN mkdir -p /workspace \ + && chown -R ${USER_UID}:${USER_GID} /workspace \ + && chmod -R 775 /workspace + +# Claude config directory +RUN mkdir -p /home/${USER_NAME}/.claude \ + && chown -R ${USER_UID}:${USER_GID} /home/${USER_NAME}/.claude \ + && chmod -R 775 /home/${USER_NAME}/.claude + +# Ensure home directory has correct permissions for OpenShift random UID +RUN chown -R ${USER_UID}:${USER_GID} /home/${USER_NAME} \ + && chmod -R 775 /home/${USER_NAME} + +# ============================================================================= +# Copy Entrypoint +# ============================================================================= + +COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh + +# ============================================================================= +# Environment Configuration +# ============================================================================= + +# Disable Claude Code auto-updater (we control versions via image builds) +ENV DISABLE_AUTOUPDATER=1 + +# Default working directory +WORKDIR /workspace + +# ============================================================================= +# Runtime Configuration +# ============================================================================= + +# Switch to non-root user +USER ${USER_UID} + +# Expose no ports by default (Claude Code is CLI-based) +# If a gateway/API layer is added later, ports can be exposed + +# Entrypoint handles environment setup and runs Claude Code +ENTRYPOINT ["entrypoint.sh"] + +# Default command - can be overridden at runtime +# This runs Claude in print (non-interactive) mode with stream-json for programmatic interaction +# +# For interactive mode, override the CMD at runtime: +# podman run -it --rm ... claude-code:latest claude +CMD ["claude", "--print", "--output-format", "stream-json", "--input-format", "stream-json"] diff --git a/agents/claude/claude_agent/README.md b/agents/claude/claude_agent/README.md new file mode 100644 index 00000000..315be863 --- /dev/null +++ b/agents/claude/claude_agent/README.md @@ -0,0 +1,291 @@ +# How to Deploy Claude Code on OpenShift + +A step-by-step guide to build, test, and deploy the Claude Code container image. + +## Prerequisites + +- `podman` installed locally +- `oc` CLI installed and logged into your OpenShift cluster +- An Anthropic API key OR a GCP service account key for Vertex AI +- The Containerfile and entrypoint.sh files + +--- + +## Option A: Deploy with Anthropic API Key + +### 0. Get your Anthropic API key + +1. Go to [https://console.anthropic.com/](https://console.anthropic.com/) +2. Sign in or create an account +3. Navigate to **API Keys** in the left sidebar +4. Click **Create Key** and give it a name +5. Copy the key (it starts with `sk-ant-api03-...`) + +**Note**: You need a paid account with credits to use the API. + +### 1. Build and test locally + +```bash +# Build +podman build -t claude-code:latest -f Containerfile . + +# Test (non-interactive) +podman run --rm \ + -e ANTHROPIC_API_KEY="sk-ant-api03-YOUR_KEY_HERE" \ + claude-code:latest \ + claude -p "What is 2+2?" +``` + +### 2. Create OpenShift namespace + +```bash +oc new-project my-claude-project +``` + +### 3. Create the API key secret + +```bash +oc create secret generic claude-credentials \ + --from-literal=ANTHROPIC_API_KEY="sk-ant-api03-YOUR_KEY_HERE" +``` + +### 4. Apply the deployment manifest + +```bash +oc apply -f deployment.yaml +``` + +### 5. Build the image on OpenShift + +```bash +oc start-build claude-code --from-dir=. --follow +``` + +### 6. Wait for deployment and test + +```bash +# Wait for rollout +oc rollout status deployment/claude-code + +# Test (non-interactive) +oc exec deployment/claude-code -- bash -c ' + export HOME=/home/claude-agent + claude -p "What is 2+2?" +' +``` + +### 7. Interactive mode (optional) + +For a full interactive Claude Code experience with multi-turn conversations: + +```bash +# Local interactive mode +podman run -it --rm \ + -e ANTHROPIC_API_KEY="sk-ant-api03-YOUR_KEY_HERE" \ + -v $(pwd):/workspace:z \ + claude-code:latest \ + claude + +# OpenShift interactive mode +oc exec -it deployment/claude-code -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + claude +' +``` + +**Note**: Interactive mode requires a TTY, so use `-it` flags with podman and `oc exec`. + +### 8. Debug mode (optional) + +To see detailed logging of Claude Code activity, use the `--debug` flag: + +```bash +# Enable full debug logging +oc exec -it deployment/claude-code -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + claude --debug +' + +# Enable debug logging for API calls only +oc exec -it deployment/claude-code -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + claude --debug api +' +``` + +Debug logs are written to a file. To monitor the logs in real-time, open a second terminal and run: + +```bash +oc exec deployment/claude-code -- bash -c 'tail -f /home/claude-agent/.claude/debug/*.txt' +``` + +--- + +## Option B: Deploy with Vertex AI + +### 0. Get your GCP service account key + +You need a GCP service account with Vertex AI access. Choose one of these methods: + +**Option 1: Create a new service account in GCP Console** + +1. Go to [GCP Console](https://console.cloud.google.com) +2. Select your project (must have Vertex AI API enabled) +3. Navigate to **IAM & Admin → Service Accounts** +4. Click **+ CREATE SERVICE ACCOUNT** + - Name: `claude-code-user` (or your preferred name) + - Grant role: **Vertex AI User** (`roles/aiplatform.user`) +5. Click on the service account → **Keys** tab +6. Click **Add Key → Create new key → JSON** +7. The key file downloads automatically - save it securely + +**Option 2: Use gcloud CLI** + +```bash +# Create service account +gcloud iam service-accounts create claude-code-user \ + --display-name="Claude Code User" + +# Grant Vertex AI access +gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \ + --member="serviceAccount:claude-code-user@YOUR_PROJECT_ID.iam.gserviceaccount.com" \ + --role="roles/aiplatform.user" + +# Create and download key +gcloud iam service-accounts keys create ~/claude-vertex-key.json \ + --iam-account=claude-code-user@YOUR_PROJECT_ID.iam.gserviceaccount.com +``` + +**Note**: Creating service accounts requires **IAM Admin** or **Service Account Admin** permissions in the GCP project. + +### 1. Build and test locally + +```bash +# Build +podman build -t claude-code:latest -f Containerfile . + +# Test (non-interactive) +podman run --rm \ + -e CLAUDE_CODE_USE_VERTEX=1 \ + -e ANTHROPIC_VERTEX_PROJECT_ID="your-gcp-project-id" \ + -e CLOUD_ML_REGION="global" \ + -e GOOGLE_APPLICATION_CREDENTIALS="/var/secrets/google/key.json" \ + -v /path/to/your-service-account-key.json:/var/secrets/google/key.json:ro,z \ + claude-code:latest \ + claude -p "What is 2+2?" +``` + +### 2. Create OpenShift namespace + +```bash +oc new-project my-claude-project +``` + +### 3. Create the GCP credentials secret + +```bash +oc create secret generic claude-vertex-credentials \ + --from-file=key.json=/path/to/your-service-account-key.json +``` + +### 4. Apply the Vertex AI deployment manifest + +```bash +oc apply -f deployment-vertex.yaml +``` + +### 5. Update the ConfigMap with your project details + +```bash +oc patch configmap claude-vertex-config \ + -p '{"data":{"ANTHROPIC_VERTEX_PROJECT_ID":"your-gcp-project-id","CLOUD_ML_REGION":"global"}}' +``` + +### 6. Build the image on OpenShift + +```bash +oc start-build claude-code --from-dir=. --follow +``` + +### 7. Wait for deployment and test + +```bash +oc rollout status deployment/claude-code-vertex + +# Test (non-interactive) +oc exec deployment/claude-code-vertex -- bash -c ' + export HOME=/home/claude-agent + claude -p "What is 2+2?" +' +``` + +### 8. Interactive mode (optional) + +For a full interactive Claude Code experience with multi-turn conversations: + +```bash +# Local interactive mode +podman run -it --rm \ + -e CLAUDE_CODE_USE_VERTEX=1 \ + -e ANTHROPIC_VERTEX_PROJECT_ID="your-gcp-project-id" \ + -e CLOUD_ML_REGION="global" \ + -e GOOGLE_APPLICATION_CREDENTIALS="/var/secrets/google/key.json" \ + -v /path/to/your-service-account-key.json:/var/secrets/google/key.json:ro,z \ + -v $(pwd):/workspace:z \ + claude-code:latest \ + claude + +# OpenShift interactive mode +oc exec -it deployment/claude-code-vertex -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + claude +' +``` + +**Note**: Interactive mode requires a TTY, so use `-it` flags with podman and `oc exec`. + +### 9. Debug mode (optional) + +To see detailed logging of Claude Code activity, use the `--debug` flag: + +```bash +# Enable full debug logging +oc exec -it deployment/claude-code-vertex -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + claude --debug +' + +# Enable debug logging for API calls only +oc exec -it deployment/claude-code-vertex -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + claude --debug api +' +``` + +Debug logs are written to a file. To monitor the logs in real-time, open a second terminal and run: + +```bash +oc exec deployment/claude-code-vertex -- bash -c 'tail -f /home/claude-agent/.claude/debug/*.txt' +``` + +--- + +## Cleanup + +```bash +oc delete deployment claude-code +oc delete buildconfig claude-code +oc delete imagestream claude-code +oc delete secret claude-credentials # if using Anthropic API +oc delete secret claude-vertex-credentials # if using Vertex AI +oc delete configmap claude-mcp-config +oc delete configmap claude-skills +oc delete pvc claude-workspace +oc delete project my-claude-project +``` diff --git a/agents/claude/claude_agent/deployment-vertex.yaml b/agents/claude/claude_agent/deployment-vertex.yaml new file mode 100644 index 00000000..d1170c26 --- /dev/null +++ b/agents/claude/claude_agent/deployment-vertex.yaml @@ -0,0 +1,218 @@ +# Claude Code Deployment for OpenShift - Vertex AI Authentication +# +# This deployment uses Google Vertex AI for Claude API access instead of +# direct Anthropic API keys. It requires GCP credentials to be mounted. +# +# Prerequisites: +# 1. A GCP service account with Vertex AI access +# 2. Vertex AI API enabled in your GCP project +# +# Before applying this manifest, create the credentials secret: +# oc create secret generic claude-vertex-credentials \ +# --from-file=key.json=/path/to/your-service-account-key.json + +--- +# ImageStream to hold the built image +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: claude-code + labels: + app: claude-code-vertex +spec: + lookupPolicy: + local: true + +--- +# BuildConfig to build from Containerfile +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: claude-code + labels: + app: claude-code-vertex +spec: + source: + type: Binary + strategy: + type: Docker + dockerStrategy: + dockerfilePath: Containerfile + output: + to: + kind: ImageStreamTag + name: claude-code:latest + triggers: [] + +--- +# ConfigMap for Vertex AI configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: claude-vertex-config + labels: + app: claude-code-vertex +data: + # GCP Project ID with Vertex AI access to Claude + ANTHROPIC_VERTEX_PROJECT_ID: "YOUR_PROJECT_ID" + # Region for Vertex AI (us-east5, europe-west1, us, eu, or global) + CLOUD_ML_REGION: "global" + +--- +# ConfigMap for optional MCP configuration (shared with main deployment) +apiVersion: v1 +kind: ConfigMap +metadata: + name: claude-vertex-mcp-config + labels: + app: claude-code-vertex +data: + config.json: | + { + "mcpServers": {} + } + +--- +# ConfigMap for skills (empty by default, shared pattern) +apiVersion: v1 +kind: ConfigMap +metadata: + name: claude-vertex-skills + labels: + app: claude-code-vertex +data: {} + +--- +# PVC for workspace persistence +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: claude-vertex-workspace + labels: + app: claude-code-vertex +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + +--- +# Deployment with Vertex AI authentication +apiVersion: apps/v1 +kind: Deployment +metadata: + name: claude-code-vertex + labels: + app: claude-code-vertex +spec: + replicas: 1 + selector: + matchLabels: + app: claude-code-vertex + template: + metadata: + labels: + app: claude-code-vertex + spec: + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: claude-code + # Uses the same image as the main deployment + image: claude-code:latest + imagePullPolicy: Always + command: ["entrypoint.sh"] + args: ["sleep", "infinity"] # Keep container running for exec access + env: + # =========================================== + # Vertex AI Authentication + # =========================================== + # Enable Vertex AI mode + - name: CLAUDE_CODE_USE_VERTEX + value: "1" + # GCP Project ID (from ConfigMap) + - name: ANTHROPIC_VERTEX_PROJECT_ID + valueFrom: + configMapKeyRef: + name: claude-vertex-config + key: ANTHROPIC_VERTEX_PROJECT_ID + # Vertex AI Region (from ConfigMap) + - name: CLOUD_ML_REGION + valueFrom: + configMapKeyRef: + name: claude-vertex-config + key: CLOUD_ML_REGION + # Path to GCP service account credentials + - name: GOOGLE_APPLICATION_CREDENTIALS + value: "/var/secrets/google/key.json" + # =========================================== + # Claude Code Configuration + # =========================================== + - name: MCP_CONFIG_FILE + value: "/etc/mcp/config.json" + - name: SKIP_PERMISSIONS + value: "true" # Safe in sandboxed container + volumeMounts: + - name: workspace + mountPath: /workspace + - name: skills + mountPath: /opt/skills + readOnly: true + - name: mcp-config + mountPath: /etc/mcp + readOnly: true + # GCP credentials mount + - name: gcp-credentials + mountPath: /var/secrets/google + readOnly: true + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumes: + - name: workspace + persistentVolumeClaim: + claimName: claude-vertex-workspace + - name: skills + configMap: + name: claude-vertex-skills + optional: true + - name: mcp-config + configMap: + name: claude-vertex-mcp-config + optional: true + # GCP service account key + - name: gcp-credentials + secret: + secretName: claude-vertex-credentials + +--- +# Service (optional, for future gateway integration) +apiVersion: v1 +kind: Service +metadata: + name: claude-code-vertex + labels: + app: claude-code-vertex +spec: + selector: + app: claude-code-vertex + ports: + - name: http + port: 8080 + targetPort: 8080 + type: ClusterIP diff --git a/agents/claude/claude_agent/deployment.yaml b/agents/claude/claude_agent/deployment.yaml new file mode 100644 index 00000000..18a09539 --- /dev/null +++ b/agents/claude/claude_agent/deployment.yaml @@ -0,0 +1,176 @@ +# Claude Code Deployment for OpenShift +# +# This file contains all resources needed to deploy Claude Code on OpenShift. +# +# Before applying this manifest, create the credentials secret: +# oc create secret generic claude-credentials \ +# --from-literal=ANTHROPIC_API_KEY="sk-ant-api03-YOUR_KEY_HERE" + +--- +# ImageStream to hold the built image +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: claude-code + labels: + app: claude-code +spec: + lookupPolicy: + local: true + +--- +# BuildConfig to build from Containerfile +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: claude-code + labels: + app: claude-code +spec: + source: + type: Binary + strategy: + type: Docker + dockerStrategy: + dockerfilePath: Containerfile + output: + to: + kind: ImageStreamTag + name: claude-code:latest + triggers: [] + +--- +# ConfigMap for optional MCP configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: claude-mcp-config + labels: + app: claude-code +data: + config.json: | + { + "mcpServers": {} + } + +--- +# ConfigMap for skills (empty by default) +apiVersion: v1 +kind: ConfigMap +metadata: + name: claude-skills + labels: + app: claude-code +data: {} + +--- +# PVC for workspace persistence (optional) +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: claude-workspace + labels: + app: claude-code +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + +--- +# Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: claude-code + labels: + app: claude-code +spec: + replicas: 1 + selector: + matchLabels: + app: claude-code + template: + metadata: + labels: + app: claude-code + spec: + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: claude-code + image: claude-code:latest + imagePullPolicy: Always + command: ["entrypoint.sh"] + args: ["sleep", "infinity"] # Keep container running for exec access + env: + - name: ANTHROPIC_API_KEY + valueFrom: + secretKeyRef: + name: claude-credentials + key: ANTHROPIC_API_KEY + # Uncomment for custom endpoint: + # - name: ANTHROPIC_BASE_URL + # value: "https://your-endpoint/v1" + # - name: CLAUDE_MODEL + # value: "your-model" + - name: MCP_CONFIG_FILE + value: "/etc/mcp/config.json" + - name: SKIP_PERMISSIONS + value: "true" # Safe in sandboxed container + volumeMounts: + - name: workspace + mountPath: /workspace + - name: skills + mountPath: /opt/skills + readOnly: true + - name: mcp-config + mountPath: /etc/mcp + readOnly: true + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumes: + - name: workspace + persistentVolumeClaim: + claimName: claude-workspace + - name: skills + configMap: + name: claude-skills + optional: true + - name: mcp-config + configMap: + name: claude-mcp-config + optional: true + +--- +# Service (optional, for future gateway integration) +apiVersion: v1 +kind: Service +metadata: + name: claude-code + labels: + app: claude-code +spec: + selector: + app: claude-code + ports: + - name: http + port: 8080 + targetPort: 8080 + type: ClusterIP diff --git a/agents/claude/claude_agent/entrypoint.sh b/agents/claude/claude_agent/entrypoint.sh new file mode 100644 index 00000000..084f9496 --- /dev/null +++ b/agents/claude/claude_agent/entrypoint.sh @@ -0,0 +1,199 @@ +#!/bin/bash +# ============================================================================= +# Claude Code Container Entrypoint +# ============================================================================= +# +# This script handles environment setup before running Claude Code. +# It configures authentication, MCP servers, and other runtime settings +# based on environment variables passed to the container. +# +# Supported Environment Variables: +# +# Authentication: +# ANTHROPIC_API_KEY - API key for Anthropic (or compatible endpoint) +# ANTHROPIC_BASE_URL - Custom API endpoint (vLLM, OGX, etc.) +# +# Vertex AI Authentication (alternative to ANTHROPIC_API_KEY): +# CLAUDE_CODE_USE_VERTEX - Set to "1" to enable Vertex AI mode +# ANTHROPIC_VERTEX_PROJECT_ID - GCP project ID with Vertex AI access +# CLOUD_ML_REGION - Vertex AI region (e.g., us-east5, global) +# GOOGLE_APPLICATION_CREDENTIALS - Path to GCP service account key JSON +# +# Model Configuration: +# CLAUDE_MODEL - Model to use (e.g., sonnet, opus, claude-sonnet-4-5-20250929) +# +# MCP Configuration: +# MCP_CONFIG_FILE - Path to MCP configuration JSON file +# MCP_CONFIG_JSON - MCP configuration as inline JSON string +# +# Skills Configuration: +# SKILLS_DIR - Path to skills directory (default: /opt/skills) +# +# Permissions: +# SKIP_PERMISSIONS - Set to "true" to bypass permission checks (sandboxed environments only) +# +# ============================================================================= + +set -euo pipefail + +# ----------------------------------------------------------------------------- +# Helper Functions +# ----------------------------------------------------------------------------- + +log_info() { + echo "[entrypoint] INFO: $*" >&2 +} + +log_warn() { + echo "[entrypoint] WARN: $*" >&2 +} + +log_error() { + echo "[entrypoint] ERROR: $*" >&2 +} + +# ----------------------------------------------------------------------------- +# Validation +# ----------------------------------------------------------------------------- + +validate_environment() { + # Check for API key (required unless using alternative auth) + if [[ -z "${ANTHROPIC_API_KEY:-}" ]]; then + log_warn "ANTHROPIC_API_KEY not set. Claude Code may not be able to authenticate." + log_warn "Set ANTHROPIC_API_KEY or ensure alternative authentication is configured." + fi + + # Log API endpoint if custom + if [[ -n "${ANTHROPIC_BASE_URL:-}" ]]; then + log_info "Using custom API endpoint: ${ANTHROPIC_BASE_URL}" + fi + + # Log model if specified + if [[ -n "${CLAUDE_MODEL:-}" ]]; then + log_info "Using model: ${CLAUDE_MODEL}" + fi +} + +# ----------------------------------------------------------------------------- +# MCP Configuration +# ----------------------------------------------------------------------------- + +setup_mcp() { + local mcp_args=() + + # MCP config from file + if [[ -n "${MCP_CONFIG_FILE:-}" ]]; then + if [[ -f "${MCP_CONFIG_FILE}" ]]; then + log_info "Loading MCP config from file: ${MCP_CONFIG_FILE}" + mcp_args+=("--mcp-config" "${MCP_CONFIG_FILE}") + else + log_error "MCP_CONFIG_FILE specified but not found: ${MCP_CONFIG_FILE}" + exit 1 + fi + fi + + # MCP config from inline JSON + if [[ -n "${MCP_CONFIG_JSON:-}" ]]; then + log_info "Loading MCP config from environment variable" + mcp_args+=("--mcp-config" "${MCP_CONFIG_JSON}") + fi + + # Export for use in command building + export MCP_ARGS="${mcp_args[*]:-}" +} + +# ----------------------------------------------------------------------------- +# Skills Configuration +# ----------------------------------------------------------------------------- + +setup_skills() { + local skills_dir="${SKILLS_DIR:-/opt/skills}" + + # Check if skills directory exists and has SKILL.md files + # Claude Code expects skills as directories containing SKILL.md files + # Structure: /opt/skills//SKILL.md + if [[ -d "${skills_dir}" ]]; then + local skill_count + skill_count=$(find "${skills_dir}" -name "SKILL.md" -type f 2>/dev/null | wc -l) + if [[ ${skill_count} -gt 0 ]]; then + log_info "Found ${skill_count} skill(s) in ${skills_dir}" + export SKILLS_DIR_FOUND="${skills_dir}" + else + log_info "Skills directory is empty (no SKILL.md files found): ${skills_dir}" + fi + else + log_info "Skills directory not found: ${skills_dir}" + fi +} + +# ----------------------------------------------------------------------------- +# Build Command Arguments +# ----------------------------------------------------------------------------- + +build_claude_args() { + local args=() + + # Model selection + if [[ -n "${CLAUDE_MODEL:-}" ]]; then + args+=("--model" "${CLAUDE_MODEL}") + fi + + # MCP configuration + if [[ -n "${MCP_ARGS:-}" ]]; then + # shellcheck disable=SC2206 + args+=(${MCP_ARGS}) + fi + + # Skills directory - use --add-dir to make skills discoverable + # Claude Code looks for SKILL.md files in added directories + if [[ -n "${SKILLS_DIR_FOUND:-}" ]]; then + args+=("--add-dir" "${SKILLS_DIR_FOUND}") + fi + + # Permission bypass for sandboxed environments + if [[ "${SKIP_PERMISSIONS:-false}" == "true" ]]; then + log_warn "Permission checks disabled (SKIP_PERMISSIONS=true)" + args+=("--dangerously-skip-permissions") + fi + + # Add workspace directory access + args+=("--add-dir" "/workspace") + + # Export for use in exec + export CLAUDE_EXTRA_ARGS="${args[*]:-}" +} + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- + +main() { + log_info "Starting Claude Code container" + log_info "Claude Code version: $(claude --version 2>/dev/null || echo 'unknown')" + + # Run setup functions + validate_environment + setup_mcp + setup_skills + build_claude_args + + # If no arguments provided, show help + if [[ $# -eq 0 ]]; then + log_info "No command provided. Running: claude --help" + exec claude --help + fi + + # If first argument is "claude", run it with our extra args + if [[ "$1" == "claude" ]]; then + shift + log_info "Running: claude ${CLAUDE_EXTRA_ARGS} $*" + # shellcheck disable=SC2086 + exec claude ${CLAUDE_EXTRA_ARGS} "$@" + fi + + # Otherwise, run the command as-is (allows running bash, sh, etc.) + log_info "Running: $*" + exec "$@" +} + +main "$@" From d3dc1492e0de63b7581aac7b125dafb97233005e Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Fri, 8 May 2026 12:47:50 -0400 Subject: [PATCH 2/6] fix: Update containerized Claude agent per code review feedback Co-Authored-By: Claude Opus 4.5 --- agents/claude/claude_agent/Containerfile | 3 - agents/claude/claude_agent/README.md | 85 ++++++++++++++++--- .../claude_agent/deployment-vertex.yaml | 15 +++- agents/claude/claude_agent/deployment.yaml | 12 ++- agents/claude/claude_agent/entrypoint.sh | 78 +++++++++++++++-- 5 files changed, 171 insertions(+), 22 deletions(-) diff --git a/agents/claude/claude_agent/Containerfile b/agents/claude/claude_agent/Containerfile index 081384d3..34863243 100644 --- a/agents/claude/claude_agent/Containerfile +++ b/agents/claude/claude_agent/Containerfile @@ -34,9 +34,6 @@ LABEL name="claude-code" \ # Claude Code version to install (pinned for reproducibility) ARG CLAUDE_CODE_VERSION=2.1.123 -# Node.js version (major version, installs latest in that series) -ARG NODE_VERSION=22 - # User configuration ARG USER_NAME=claude-agent ARG USER_UID=1001 diff --git a/agents/claude/claude_agent/README.md b/agents/claude/claude_agent/README.md index 315be863..56a56a8e 100644 --- a/agents/claude/claude_agent/README.md +++ b/agents/claude/claude_agent/README.md @@ -67,10 +67,20 @@ oc start-build claude-code --from-dir=. --follow # Wait for rollout oc rollout status deployment/claude-code -# Test (non-interactive) +# Test using the claude-run wrapper (includes all configured args) oc exec deployment/claude-code -- bash -c ' export HOME=/home/claude-agent - claude -p "What is 2+2?" + ~/.claude/claude-run -p "What is 2+2?" +' +``` + +**Note**: The `claude-run` wrapper automatically includes all container-configured arguments (permission bypass, MCP config, model selection, etc.). You can also source the environment directly: + +```bash +oc exec deployment/claude-code -- bash -c ' + export HOME=/home/claude-agent + source ~/.claude/env.sh + claude $CLAUDE_EXTRA_ARGS -p "What is 2+2?" ' ``` @@ -86,11 +96,11 @@ podman run -it --rm \ claude-code:latest \ claude -# OpenShift interactive mode +# OpenShift interactive mode (uses claude-run for proper config) oc exec -it deployment/claude-code -- bash -c ' export HOME=/home/claude-agent cd /workspace - claude + ~/.claude/claude-run ' ``` @@ -215,10 +225,20 @@ oc start-build claude-code --from-dir=. --follow ```bash oc rollout status deployment/claude-code-vertex -# Test (non-interactive) +# Test using the claude-run wrapper (includes all configured args) oc exec deployment/claude-code-vertex -- bash -c ' export HOME=/home/claude-agent - claude -p "What is 2+2?" + ~/.claude/claude-run -p "What is 2+2?" +' +``` + +**Note**: The `claude-run` wrapper automatically includes all container-configured arguments (permission bypass, MCP config, model selection, etc.). You can also source the environment directly: + +```bash +oc exec deployment/claude-code-vertex -- bash -c ' + export HOME=/home/claude-agent + source ~/.claude/env.sh + claude $CLAUDE_EXTRA_ARGS -p "What is 2+2?" ' ``` @@ -238,11 +258,19 @@ podman run -it --rm \ claude-code:latest \ claude -# OpenShift interactive mode +# OpenShift interactive mode (uses claude-run for proper config) oc exec -it deployment/claude-code-vertex -- bash -c ' export HOME=/home/claude-agent cd /workspace - claude + ~/.claude/claude-run +' + +# Alternative: source env.sh directly +oc exec -it deployment/claude-code-vertex -- bash -c ' + export HOME=/home/claude-agent + cd /workspace + source ~/.claude/env.sh + claude $CLAUDE_EXTRA_ARGS ' ``` @@ -276,16 +304,53 @@ oc exec deployment/claude-code-vertex -- bash -c 'tail -f /home/claude-agent/.cl --- +## Security Considerations + +### SKIP_PERMISSIONS + +The deployment manifests set `SKIP_PERMISSIONS=true` by default, which passes `--dangerously-skip-permissions` to Claude Code. This disables all file-system write permission prompts. + +**Why it's enabled by default:** + +- The container runs as non-root with dropped capabilities and seccomp profiles +- Claude only has access to the isolated `/workspace` PVC, not host filesystems +- Permission prompts don't work well in non-interactive `oc exec` scenarios + +**When to disable:** + +- If you mount sensitive host directories into the container +- If you're running in a less isolated environment +- If you want Claude to prompt before file operations + +To disable, set `SKIP_PERMISSIONS=false` in the deployment manifest or remove the environment variable entirely. + +--- + ## Cleanup +### Option A: Anthropic API resources + ```bash oc delete deployment claude-code oc delete buildconfig claude-code oc delete imagestream claude-code -oc delete secret claude-credentials # if using Anthropic API -oc delete secret claude-vertex-credentials # if using Vertex AI +oc delete secret claude-credentials oc delete configmap claude-mcp-config oc delete configmap claude-skills oc delete pvc claude-workspace oc delete project my-claude-project ``` + +### Option B: Vertex AI resources + +```bash +oc delete deployment claude-code-vertex +oc delete buildconfig claude-code +oc delete imagestream claude-code +oc delete secret claude-vertex-credentials +oc delete configmap claude-vertex-config +oc delete configmap claude-vertex-mcp-config +oc delete configmap claude-vertex-skills +oc delete pvc claude-vertex-workspace +oc delete project my-claude-project +``` diff --git a/agents/claude/claude_agent/deployment-vertex.yaml b/agents/claude/claude_agent/deployment-vertex.yaml index d1170c26..484ecbd6 100644 --- a/agents/claude/claude_agent/deployment-vertex.yaml +++ b/agents/claude/claude_agent/deployment-vertex.yaml @@ -107,6 +107,10 @@ metadata: app: claude-code-vertex spec: replicas: 1 + # Recreate strategy required for ReadWriteOnce PVC - prevents Multi-Attach errors + # during rolling updates (old pod must release PVC before new pod can mount it) + strategy: + type: Recreate selector: matchLabels: app: claude-code-vertex @@ -128,6 +132,12 @@ spec: args: ["sleep", "infinity"] # Keep container running for exec access env: # =========================================== + # Runtime Environment + # =========================================== + # HOME must be set for the entrypoint to create ~/.claude/ + - name: HOME + value: "/home/claude-agent" + # =========================================== # Vertex AI Authentication # =========================================== # Enable Vertex AI mode @@ -153,8 +163,11 @@ spec: # =========================================== - name: MCP_CONFIG_FILE value: "/etc/mcp/config.json" + # WARNING: Setting SKIP_PERMISSIONS=true disables all Claude permission checks. + # This bypasses file-system write confirmations. Only use in fully sandboxed + # environments where the container has no access to sensitive host resources. - name: SKIP_PERMISSIONS - value: "true" # Safe in sandboxed container + value: "true" volumeMounts: - name: workspace mountPath: /workspace diff --git a/agents/claude/claude_agent/deployment.yaml b/agents/claude/claude_agent/deployment.yaml index 18a09539..2e0e9e6c 100644 --- a/agents/claude/claude_agent/deployment.yaml +++ b/agents/claude/claude_agent/deployment.yaml @@ -88,6 +88,10 @@ metadata: app: claude-code spec: replicas: 1 + # Recreate strategy required for ReadWriteOnce PVC - prevents Multi-Attach errors + # during rolling updates (old pod must release PVC before new pod can mount it) + strategy: + type: Recreate selector: matchLabels: app: claude-code @@ -107,6 +111,9 @@ spec: command: ["entrypoint.sh"] args: ["sleep", "infinity"] # Keep container running for exec access env: + # HOME must be set for the entrypoint to create ~/.claude/ + - name: HOME + value: "/home/claude-agent" - name: ANTHROPIC_API_KEY valueFrom: secretKeyRef: @@ -119,8 +126,11 @@ spec: # value: "your-model" - name: MCP_CONFIG_FILE value: "/etc/mcp/config.json" + # WARNING: Setting SKIP_PERMISSIONS=true disables all Claude permission checks. + # This bypasses file-system write confirmations. Only use in fully sandboxed + # environments where the container has no access to sensitive host resources. - name: SKIP_PERMISSIONS - value: "true" # Safe in sandboxed container + value: "true" volumeMounts: - name: workspace mountPath: /workspace diff --git a/agents/claude/claude_agent/entrypoint.sh b/agents/claude/claude_agent/entrypoint.sh index 084f9496..695d1f46 100644 --- a/agents/claude/claude_agent/entrypoint.sh +++ b/agents/claude/claude_agent/entrypoint.sh @@ -36,6 +36,16 @@ set -euo pipefail +# Track temp files for cleanup +TEMP_FILES=() + +cleanup_temp_files() { + for f in "${TEMP_FILES[@]}"; do + rm -f "$f" 2>/dev/null || true + done +} +trap cleanup_temp_files EXIT + # ----------------------------------------------------------------------------- # Helper Functions # ----------------------------------------------------------------------------- @@ -57,10 +67,24 @@ log_error() { # ----------------------------------------------------------------------------- validate_environment() { - # Check for API key (required unless using alternative auth) - if [[ -z "${ANTHROPIC_API_KEY:-}" ]]; then - log_warn "ANTHROPIC_API_KEY not set. Claude Code may not be able to authenticate." - log_warn "Set ANTHROPIC_API_KEY or ensure alternative authentication is configured." + # Check authentication based on mode + if [[ "${CLAUDE_CODE_USE_VERTEX:-}" == "1" ]]; then + # Vertex AI mode - uses GCP credentials + log_info "Vertex AI mode enabled" + if [[ -z "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]]; then + log_warn "GOOGLE_APPLICATION_CREDENTIALS not set. Vertex AI authentication may fail." + elif [[ ! -f "${GOOGLE_APPLICATION_CREDENTIALS}" ]]; then + log_warn "GOOGLE_APPLICATION_CREDENTIALS file not found: ${GOOGLE_APPLICATION_CREDENTIALS}" + fi + if [[ -z "${ANTHROPIC_VERTEX_PROJECT_ID:-}" ]]; then + log_warn "ANTHROPIC_VERTEX_PROJECT_ID not set. Vertex AI requires a GCP project ID." + fi + else + # Standard Anthropic API mode + if [[ -z "${ANTHROPIC_API_KEY:-}" ]]; then + log_warn "ANTHROPIC_API_KEY not set. Claude Code may not be able to authenticate." + log_warn "Set ANTHROPIC_API_KEY or use Vertex AI mode (CLAUDE_CODE_USE_VERTEX=1)." + fi fi # Log API endpoint if custom @@ -92,10 +116,14 @@ setup_mcp() { fi fi - # MCP config from inline JSON + # MCP config from inline JSON - write to temp file since --mcp-config expects a path if [[ -n "${MCP_CONFIG_JSON:-}" ]]; then - log_info "Loading MCP config from environment variable" - mcp_args+=("--mcp-config" "${MCP_CONFIG_JSON}") + local tmp_mcp_config + tmp_mcp_config=$(mktemp /tmp/mcp-config-XXXXXX.json) + TEMP_FILES+=("${tmp_mcp_config}") + echo "${MCP_CONFIG_JSON}" > "${tmp_mcp_config}" + log_info "Loading MCP config from environment variable (written to ${tmp_mcp_config})" + mcp_args+=("--mcp-config" "${tmp_mcp_config}") fi # Export for use in command building @@ -160,7 +188,43 @@ build_claude_args() { args+=("--add-dir" "/workspace") # Export for use in exec + # NOTE: Flattening array to string loses proper quoting. This is acceptable + # because our flags (--model, --mcp-config, --add-dir, --dangerously-skip-permissions) + # don't contain spaces. + # shellcheck disable=SC2124 export CLAUDE_EXTRA_ARGS="${args[*]:-}" + + # Persist args to file so oc exec sessions can use them + # Users can: source ~/.claude/env.sh && claude $CLAUDE_EXTRA_ARGS -p "..." + # Or use the claude-run wrapper: claude-run -p "..." + local claude_dir="${HOME}/.claude" + mkdir -p "${claude_dir}" + + # Write environment file + cat > "${claude_dir}/env.sh" < "${claude_dir}/claude-run" <<'WRAPPER' +#!/bin/bash +# Wrapper script that runs claude with the container's configured args +# Usage: claude-run [additional-args...] +source "${HOME}/.claude/env.sh" 2>/dev/null || true +# shellcheck disable=SC2086 +exec claude ${CLAUDE_EXTRA_ARGS} "$@" +WRAPPER + chmod +x "${claude_dir}/claude-run" + + # Add to PATH via .bashrc if not already there + if ! grep -q 'claude-run' "${HOME}/.bashrc" 2>/dev/null; then + echo 'export PATH="${HOME}/.claude:${PATH}"' >> "${HOME}/.bashrc" + fi + + log_info "Claude args persisted to ${claude_dir}/env.sh" + log_info "Wrapper script available: claude-run (or ~/.claude/claude-run)" } # ----------------------------------------------------------------------------- From 92555d80ed75caea76a036b56953d14773368d54 Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Fri, 15 May 2026 15:09:56 -0400 Subject: [PATCH 3/6] fix: Use Claude's native installer rather than npm. Also, add licensing note. Co-Authored-By: Claude Opus 4.5 --- agents/claude/claude_agent/Containerfile | 42 ++++++++++++++---------- agents/claude/claude_agent/README.md | 10 ++++++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/agents/claude/claude_agent/Containerfile b/agents/claude/claude_agent/Containerfile index 34863243..c63170d7 100644 --- a/agents/claude/claude_agent/Containerfile +++ b/agents/claude/claude_agent/Containerfile @@ -6,7 +6,7 @@ # # Features: # - UBI 10 minimal base (RHEL-compatible, supported) -# - Claude Code installed via npm (version-pinned) +# - Claude Code installed via native installer (version-pinned) # - Non-root user compatible with restricted-v2 SCC # - Configurable API endpoint (Anthropic, vLLM, OGX) # - Skills injectable at runtime via ConfigMap/PVC mount @@ -43,15 +43,11 @@ ARG USER_GID=0 # Install System Dependencies # ============================================================================= -# Install Node.js and minimal utilities -# - nodejs: Required for Claude Code (npm package) -# - npm: Package manager for installing Claude Code +# Install minimal utilities # - git: Version control (agent may need to clone repos) -# - curl: HTTP client (agent may need to fetch resources) +# - curl: HTTP client (agent may need to fetch resources, also used by installer) # - jq: JSON processor (useful for scripting) RUN microdnf install -y --nodocs \ - nodejs \ - npm \ git \ curl \ jq \ @@ -68,17 +64,6 @@ RUN microdnf install -y --nodocs \ # Directories below use chmod 775 so GID 0 members have write access. RUN useradd -u ${USER_UID} -g ${USER_GID} -d /home/${USER_NAME} -m -s /bin/bash ${USER_NAME} -# ============================================================================= -# Install Claude Code -# ============================================================================= - -# Install Claude Code globally with pinned version -RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \ - && npm cache clean --force - -# Verify installation -RUN claude --version - # ============================================================================= # Create Directory Structure # ============================================================================= @@ -109,6 +94,27 @@ RUN mkdir -p /home/${USER_NAME}/.claude \ RUN chown -R ${USER_UID}:${USER_GID} /home/${USER_NAME} \ && chmod -R 775 /home/${USER_NAME} +# ============================================================================= +# Install Claude Code (as non-root user) +# ============================================================================= + +# Switch to non-root user for installation +# The native installer writes to $HOME/.claude/local/ +USER ${USER_UID} + +# Install Claude Code via native installer with pinned version +# Version pinning: pass version as argument to install.sh +RUN curl -fsSL https://claude.ai/install.sh | bash -s -- "${CLAUDE_CODE_VERSION}" + +# Add Claude to PATH (native installer puts it in ~/.local/bin) +ENV PATH="/home/claude-agent/.local/bin:${PATH}" + +# Verify installation +RUN claude --version + +# Switch back to root for remaining setup +USER 0 + # ============================================================================= # Copy Entrypoint # ============================================================================= diff --git a/agents/claude/claude_agent/README.md b/agents/claude/claude_agent/README.md index 56a56a8e..dc99101a 100644 --- a/agents/claude/claude_agent/README.md +++ b/agents/claude/claude_agent/README.md @@ -2,6 +2,10 @@ A step-by-step guide to build, test, and deploy the Claude Code container image. +## Licensing Notice + +**Do not redistribute built container images.** The Containerfile installs Claude Code at build time via Anthropic's native installer. The resulting image contains Anthropic's proprietary binary, which is subject to their [commercial terms](https://code.claude.com/docs/en/legal-and-compliance) ("All rights reserved"). Building the image yourself for internal use is permitted, but redistributing the built image (e.g., pushing to a public registry) is not authorized. + ## Prerequisites - `podman` installed locally @@ -29,6 +33,9 @@ A step-by-step guide to build, test, and deploy the Claude Code container image. # Build podman build -t claude-code:latest -f Containerfile . +# Build with a specific Claude Code version +podman build --build-arg CLAUDE_CODE_VERSION=2.1.123 -t claude-code:2.1.123 -f Containerfile . + # Test (non-interactive) podman run --rm \ -e ANTHROPIC_API_KEY="sk-ant-api03-YOUR_KEY_HERE" \ @@ -177,6 +184,9 @@ gcloud iam service-accounts keys create ~/claude-vertex-key.json \ # Build podman build -t claude-code:latest -f Containerfile . +# Build with a specific Claude Code version +podman build --build-arg CLAUDE_CODE_VERSION=2.1.123 -t claude-code:2.1.123 -f Containerfile . + # Test (non-interactive) podman run --rm \ -e CLAUDE_CODE_USE_VERTEX=1 \ From b22618286cd39294b3ff61876298b28d31096a68 Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Mon, 18 May 2026 17:48:30 -0400 Subject: [PATCH 4/6] fix: Update claude directory structure --- .../{claude/claude_agent => claude-code/deployment}/Containerfile | 0 agents/{claude/claude_agent => claude-code/deployment}/README.md | 0 .../deployment}/deployment-vertex.yaml | 0 .../claude_agent => claude-code/deployment}/deployment.yaml | 0 .../{claude/claude_agent => claude-code/deployment}/entrypoint.sh | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename agents/{claude/claude_agent => claude-code/deployment}/Containerfile (100%) rename agents/{claude/claude_agent => claude-code/deployment}/README.md (100%) rename agents/{claude/claude_agent => claude-code/deployment}/deployment-vertex.yaml (100%) rename agents/{claude/claude_agent => claude-code/deployment}/deployment.yaml (100%) rename agents/{claude/claude_agent => claude-code/deployment}/entrypoint.sh (100%) diff --git a/agents/claude/claude_agent/Containerfile b/agents/claude-code/deployment/Containerfile similarity index 100% rename from agents/claude/claude_agent/Containerfile rename to agents/claude-code/deployment/Containerfile diff --git a/agents/claude/claude_agent/README.md b/agents/claude-code/deployment/README.md similarity index 100% rename from agents/claude/claude_agent/README.md rename to agents/claude-code/deployment/README.md diff --git a/agents/claude/claude_agent/deployment-vertex.yaml b/agents/claude-code/deployment/deployment-vertex.yaml similarity index 100% rename from agents/claude/claude_agent/deployment-vertex.yaml rename to agents/claude-code/deployment/deployment-vertex.yaml diff --git a/agents/claude/claude_agent/deployment.yaml b/agents/claude-code/deployment/deployment.yaml similarity index 100% rename from agents/claude/claude_agent/deployment.yaml rename to agents/claude-code/deployment/deployment.yaml diff --git a/agents/claude/claude_agent/entrypoint.sh b/agents/claude-code/deployment/entrypoint.sh similarity index 100% rename from agents/claude/claude_agent/entrypoint.sh rename to agents/claude-code/deployment/entrypoint.sh From eea4bed4b3ff4163266907caa8f4ebb4247886fd Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Mon, 18 May 2026 19:22:16 -0400 Subject: [PATCH 5/6] fix: Fix issues flagged by coderabbit --- agents/claude-code/deployment/Containerfile | 2 +- .../deployment/deployment-vertex.yaml | 17 ----------------- agents/claude-code/deployment/deployment.yaml | 17 ----------------- agents/claude-code/deployment/entrypoint.sh | 4 ++-- 4 files changed, 3 insertions(+), 37 deletions(-) diff --git a/agents/claude-code/deployment/Containerfile b/agents/claude-code/deployment/Containerfile index c63170d7..a771396c 100644 --- a/agents/claude-code/deployment/Containerfile +++ b/agents/claude-code/deployment/Containerfile @@ -107,7 +107,7 @@ USER ${USER_UID} RUN curl -fsSL https://claude.ai/install.sh | bash -s -- "${CLAUDE_CODE_VERSION}" # Add Claude to PATH (native installer puts it in ~/.local/bin) -ENV PATH="/home/claude-agent/.local/bin:${PATH}" +ENV PATH="/home/${USER_NAME}/.local/bin:${PATH}" # Verify installation RUN claude --version diff --git a/agents/claude-code/deployment/deployment-vertex.yaml b/agents/claude-code/deployment/deployment-vertex.yaml index 484ecbd6..57e4ac90 100644 --- a/agents/claude-code/deployment/deployment-vertex.yaml +++ b/agents/claude-code/deployment/deployment-vertex.yaml @@ -212,20 +212,3 @@ spec: - name: gcp-credentials secret: secretName: claude-vertex-credentials - ---- -# Service (optional, for future gateway integration) -apiVersion: v1 -kind: Service -metadata: - name: claude-code-vertex - labels: - app: claude-code-vertex -spec: - selector: - app: claude-code-vertex - ports: - - name: http - port: 8080 - targetPort: 8080 - type: ClusterIP diff --git a/agents/claude-code/deployment/deployment.yaml b/agents/claude-code/deployment/deployment.yaml index 2e0e9e6c..915fcc4c 100644 --- a/agents/claude-code/deployment/deployment.yaml +++ b/agents/claude-code/deployment/deployment.yaml @@ -167,20 +167,3 @@ spec: configMap: name: claude-mcp-config optional: true - ---- -# Service (optional, for future gateway integration) -apiVersion: v1 -kind: Service -metadata: - name: claude-code - labels: - app: claude-code -spec: - selector: - app: claude-code - ports: - - name: http - port: 8080 - targetPort: 8080 - type: ClusterIP diff --git a/agents/claude-code/deployment/entrypoint.sh b/agents/claude-code/deployment/entrypoint.sh index 695d1f46..69eb4bf5 100644 --- a/agents/claude-code/deployment/entrypoint.sh +++ b/agents/claude-code/deployment/entrypoint.sh @@ -250,13 +250,13 @@ main() { # If first argument is "claude", run it with our extra args if [[ "$1" == "claude" ]]; then shift - log_info "Running: claude ${CLAUDE_EXTRA_ARGS} $*" + log_info "Running: claude ${CLAUDE_EXTRA_ARGS}" # shellcheck disable=SC2086 exec claude ${CLAUDE_EXTRA_ARGS} "$@" fi # Otherwise, run the command as-is (allows running bash, sh, etc.) - log_info "Running: $*" + log_info "Running custom command: $1" exec "$@" } From 20a601dee83f27499ddf4afab15335347129e3fb Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Mon, 18 May 2026 19:42:21 -0400 Subject: [PATCH 6/6] fix: Don't log the full ANTHROPIC_BASE_URL value. --- agents/claude-code/deployment/entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agents/claude-code/deployment/entrypoint.sh b/agents/claude-code/deployment/entrypoint.sh index 69eb4bf5..8a8f0a32 100644 --- a/agents/claude-code/deployment/entrypoint.sh +++ b/agents/claude-code/deployment/entrypoint.sh @@ -87,9 +87,9 @@ validate_environment() { fi fi - # Log API endpoint if custom + # Log API endpoint if custom (don't log full URL - may contain credentials) if [[ -n "${ANTHROPIC_BASE_URL:-}" ]]; then - log_info "Using custom API endpoint: ${ANTHROPIC_BASE_URL}" + log_info "Using custom API endpoint (value redacted)" fi # Log model if specified