Implements weeks 1-4 of the multi-provider architecture: Week 1 - Foundation: - Add domain models (AgentProvider, AgentRequest, AgentEvent, AgentResult) - Define CodeAgent port interface with Execute, Cancel, Capabilities - Create thread-safe provider registry with first-registered default Week 2 - Claude Code Adapter: - Extract kubectl exec logic into CodeAgent implementation - Parse stream-json output format (init, message, tool_use, result) - Support session continuation via --resume flag Week 3 - OpenCode Adapter: - HTTP/SSE client for opencode serve API - Session management (create, send message, abort) - Event streaming with documented buffer rationale Week 4 - Quality & Polish: - Fix race condition in OpenCode Cancel method - Add AgentRequest.Validate() with ErrPromptRequired, ErrInvalidTimeout - Document DefaultAvailabilityTimeout constants - Add HTTP error context for debugging Also includes: - Work queue system with PostgreSQL adapter - Credential store for infrastructure secrets - Project templates with Woodpecker CI integration - Comprehensive test coverage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
162 lines
4.9 KiB
Bash
Executable File
162 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# load-credentials.sh - Load credentials from .secrets file into rdev-api
|
|
#
|
|
# Usage:
|
|
# ./scripts/load-credentials.sh # Load to localhost:8080
|
|
# ./scripts/load-credentials.sh https://rdev.example.com # Load to remote
|
|
# RDEV_ADMIN_KEY=xxx ./scripts/load-credentials.sh # With explicit admin key
|
|
#
|
|
# Reads credentials from .secrets file (KEY=VALUE format) and uploads them
|
|
# to the rdev-api /credentials/batch endpoint.
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
RDEV_API_URL="${1:-http://localhost:8080}"
|
|
SECRETS_FILE="${SECRETS_FILE:-.secrets}"
|
|
ADMIN_KEY="${RDEV_ADMIN_KEY:-}"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
# Check for required tools
|
|
if ! command -v curl &> /dev/null; then
|
|
log_error "curl is required but not installed"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v jq &> /dev/null; then
|
|
log_error "jq is required but not installed"
|
|
exit 1
|
|
fi
|
|
|
|
# Check secrets file exists
|
|
if [[ ! -f "$SECRETS_FILE" ]]; then
|
|
log_error "Secrets file not found: $SECRETS_FILE"
|
|
log_info "Create a .secrets file with KEY=VALUE pairs, e.g.:"
|
|
echo " GITEA_TOKEN=your-token-here"
|
|
echo " CLOUDFLARE_API_TOKEN=your-token-here"
|
|
exit 1
|
|
fi
|
|
|
|
# Get admin key if not provided
|
|
if [[ -z "$ADMIN_KEY" ]]; then
|
|
# Try to get from K8s secret
|
|
if command -v kubectl &> /dev/null; then
|
|
ADMIN_KEY=$(kubectl get secret rdev-credentials -n rdev -o jsonpath='{.data.RDEV_ADMIN_KEY}' 2>/dev/null | base64 -d 2>/dev/null || true)
|
|
fi
|
|
|
|
if [[ -z "$ADMIN_KEY" ]]; then
|
|
log_error "RDEV_ADMIN_KEY not set and could not retrieve from K8s"
|
|
log_info "Set RDEV_ADMIN_KEY environment variable or ensure kubectl access to rdev namespace"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
log_info "Loading credentials from $SECRETS_FILE to $RDEV_API_URL"
|
|
|
|
# Map of secret keys to categories and descriptions
|
|
declare -A CATEGORIES=(
|
|
["GITEA_TOKEN"]="gitea"
|
|
["GITEA_API_TOKEN"]="gitea"
|
|
["GITEA_URL"]="gitea"
|
|
["CLOUDFLARE_API_TOKEN"]="cloudflare"
|
|
["CLOUDFLARE_ZONE_ID"]="cloudflare"
|
|
["WOODPECKER_URL"]="woodpecker"
|
|
["WOODPECKER_API_TOKEN"]="woodpecker"
|
|
["WOODPECKER_WEBHOOK_SECRET"]="woodpecker"
|
|
["REGISTRY_URL"]="registry"
|
|
)
|
|
|
|
declare -A DESCRIPTIONS=(
|
|
["GITEA_TOKEN"]="Gitea API access token"
|
|
["GITEA_API_TOKEN"]="Gitea API access token"
|
|
["GITEA_URL"]="Gitea server URL"
|
|
["CLOUDFLARE_API_TOKEN"]="Cloudflare API token for DNS management"
|
|
["CLOUDFLARE_ZONE_ID"]="Cloudflare zone ID for threesix.ai"
|
|
["WOODPECKER_URL"]="Woodpecker CI server URL"
|
|
["WOODPECKER_API_TOKEN"]="Woodpecker CI API token for repo activation"
|
|
["WOODPECKER_WEBHOOK_SECRET"]="HMAC secret for Woodpecker webhook verification"
|
|
["REGISTRY_URL"]="Container registry URL"
|
|
)
|
|
|
|
# Build JSON payload from secrets file
|
|
CREDENTIALS_JSON='{"credentials":['
|
|
FIRST=true
|
|
|
|
while IFS='=' read -r key value || [[ -n "$key" ]]; do
|
|
# Skip empty lines and comments
|
|
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
|
|
# Trim whitespace
|
|
key=$(echo "$key" | xargs)
|
|
value=$(echo "$value" | xargs)
|
|
|
|
# Skip if empty value
|
|
[[ -z "$value" ]] && continue
|
|
|
|
# Normalize key name (GITEA_API_TOKEN -> GITEA_TOKEN)
|
|
if [[ "$key" == "GITEA_API_TOKEN" ]]; then
|
|
key="GITEA_TOKEN"
|
|
fi
|
|
|
|
# Get category and description
|
|
category="${CATEGORIES[$key]:-other}"
|
|
description="${DESCRIPTIONS[$key]:-$key credential}"
|
|
|
|
# Add comma if not first
|
|
if [[ "$FIRST" == "true" ]]; then
|
|
FIRST=false
|
|
else
|
|
CREDENTIALS_JSON+=','
|
|
fi
|
|
|
|
# Escape value for JSON
|
|
escaped_value=$(echo -n "$value" | jq -Rs '.')
|
|
|
|
CREDENTIALS_JSON+="{\"key\":\"$key\",\"value\":$escaped_value,\"category\":\"$category\",\"description\":\"$description\"}"
|
|
|
|
log_info " Prepared: $key ($category)"
|
|
done < "$SECRETS_FILE"
|
|
|
|
CREDENTIALS_JSON+=']}'
|
|
|
|
# Count credentials
|
|
CRED_COUNT=$(echo "$CREDENTIALS_JSON" | jq '.credentials | length')
|
|
if [[ "$CRED_COUNT" == "0" ]]; then
|
|
log_warn "No credentials found in $SECRETS_FILE"
|
|
exit 0
|
|
fi
|
|
|
|
log_info "Uploading $CRED_COUNT credentials..."
|
|
|
|
# Upload to rdev-api
|
|
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$RDEV_API_URL/credentials/batch" \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-API-Key: $ADMIN_KEY" \
|
|
-d "$CREDENTIALS_JSON")
|
|
|
|
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
|
|
if [[ "$HTTP_CODE" == "201" ]]; then
|
|
log_info "Successfully uploaded credentials"
|
|
echo "$BODY" | jq -r '.keys[]' 2>/dev/null | while read -r k; do
|
|
echo " - $k"
|
|
done
|
|
else
|
|
log_error "Failed to upload credentials (HTTP $HTTP_CODE)"
|
|
echo "$BODY" | jq . 2>/dev/null || echo "$BODY"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Done! Credentials are now stored in rdev database."
|
|
log_info "Restart rdev-api to reload infrastructure adapters with new credentials."
|