|
#!/bin/bash |
|
|
|
# Ensure we’re running on Bash 4+ for associative array functionality (on macOS default bash is 3.2). |
|
if [ -n "$BASH_VERSION" ] && [ "${BASH_VERSINFO[0]}" -lt 4 ]; then |
|
if command -v bash >/dev/null 2>&1 && bash -c '[ ${BASH_VERSINFO[0]} -ge 4 ]'; then |
|
exec bash "$0" "$@" |
|
fi |
|
echo "[ERROR] Bash 4+ is required. Install with: brew install bash and rerun via /opt/homebrew/bin/bash $0" |
|
exit 1 |
|
fi |
|
|
|
################################################################################ |
|
# Claude Code Developer Setup Script |
|
# Purpose: Onboard new developers with Claude Code, AWS Bedrock, and cost tracking |
|
# Requirements: Ubuntu/Linux, AWS CLI installed, AWS profile "dev" configured |
|
################################################################################ |
|
|
|
set -e |
|
|
|
# Colors for output — use $'...' so variables hold real ANSI escape bytes |
|
RED=$'\033[0;31m' |
|
GREEN=$'\033[0;32m' |
|
YELLOW=$'\033[1;33m' |
|
BLUE=$'\033[0;34m' |
|
NC=$'\033[0m' |
|
|
|
# Configuration |
|
AWS_REGION="${AWS_REGION:-us-east-1}" |
|
BEDROCK_REGION="" # resolved dynamically |
|
AWS_PROFILE="dev" |
|
ACTION="" |
|
DEVELOPER_NAME="" |
|
while [ $# -gt 0 ]; do |
|
case "$1" in |
|
--create|--delete|--update) ACTION="$1" ; shift ;; |
|
--developer) DEVELOPER_NAME="${2:-}" ; shift 2 ;; |
|
*) echo "[ERROR] Unknown argument: $1" ; exit 1 ;; |
|
esac |
|
done |
|
AWS_ACCOUNT_ID="" |
|
INSTALL_DIR="${HOME}/.claude-code" |
|
|
|
BEDROCK_CANDIDATE_REGIONS=( |
|
"$AWS_REGION" |
|
us-east-1 |
|
us-west-2 |
|
eu-west-1 |
|
eu-central-1 |
|
ap-northeast-1 |
|
ap-southeast-1 |
|
ap-south-1 |
|
) |
|
|
|
CREATED_PROFILES=() |
|
UPDATED_NEW_PROFILES=() # profiles created during --update |
|
UPDATED_KEPT_PROFILES=() # profiles that already existed, kept during --update |
|
# macOS bash (3.2) does not support associative arrays. Use explicit model vars. |
|
PROFILE_ARN_OPUS46="" |
|
PROFILE_ARN_OPUS47="" |
|
PROFILE_ARN_SONNET="" |
|
PROFILE_ARN_HAIKU="" |
|
PROFILE_ARN_QWEN3_NEXT="" |
|
PROFILE_ARN_QWEN3_CODER="" |
|
|
|
################################################################################ |
|
# Helper Functions |
|
################################################################################ |
|
|
|
log_info() { |
|
echo -e "${BLUE}[INFO]${NC} $1" |
|
} |
|
|
|
log_success() { |
|
echo -e "${GREEN}[SUCCESS]${NC} $1" |
|
} |
|
|
|
log_error() { |
|
echo -e "${RED}[ERROR]${NC} $1" |
|
} |
|
|
|
log_warning() { |
|
echo -e "${YELLOW}[WARNING]${NC} $1" |
|
} |
|
|
|
sanitize_bedrock_description() { |
|
local raw="$1" |
|
local cleaned |
|
cleaned=$(echo "$raw" | tr -cd '[:alnum:]:._ -' | sed -E 's/[ _-]+/ /g; s/^ +//; s/ +$//') |
|
if [ -z "$cleaned" ]; then |
|
cleaned="Claude Code profile" |
|
fi |
|
echo "$cleaned" |
|
} |
|
|
|
model_type_hint() { |
|
local model_type="$1" |
|
case "$model_type" in |
|
opus46) echo "claude-opus-4-6" ;; |
|
opus47) echo "claude-opus-4-7" ;; |
|
sonnet) echo "claude-sonnet" ;; |
|
haiku) echo "claude-haiku" ;; |
|
*) return 1 ;; |
|
esac |
|
} |
|
|
|
model_foundation_id() { |
|
local model_type="$1" |
|
case "$model_type" in |
|
opus46) echo "anthropic.claude-opus-4-6" ;; |
|
opus47) echo "anthropic.claude-opus-4-7" ;; |
|
sonnet) echo "anthropic.claude-sonnet-4-6" ;; |
|
haiku) echo "anthropic.claude-haiku-4-5" ;; |
|
*) return 1 ;; |
|
esac |
|
} |
|
|
|
################################################################################ |
|
# Bedrock Region Discovery |
|
################################################################################ |
|
|
|
region_has_anthropic_models() { |
|
local region="$1" |
|
local count |
|
count=$(aws --profile "$AWS_PROFILE" bedrock list-foundation-models \ |
|
--region "$region" \ |
|
--by-provider anthropic \ |
|
--query "length(modelSummaries)" \ |
|
--output text 2>/dev/null || echo "0") |
|
if [ "$count" != "0" ] && [ "$count" != "None" ]; then |
|
return 0 |
|
fi |
|
# Fallback: accounts that expose Anthropic models only via cross-region |
|
# inference profiles return 0 from list-foundation-models; check system-defined |
|
# profiles instead. |
|
local profile_count |
|
profile_count=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$region" \ |
|
--type-equals SYSTEM_DEFINED \ |
|
--query "length(inferenceProfileSummaries[?contains(inferenceProfileId, 'anthropic') || contains(inferenceProfileId, 'claude')])" \ |
|
--output text 2>/dev/null || echo "0") |
|
[ "$profile_count" != "0" ] && [ "$profile_count" != "None" ] |
|
} |
|
|
|
discover_bedrock_region() { |
|
log_info "Discovering Bedrock region with Anthropic model support..." |
|
|
|
for candidate in "${BEDROCK_CANDIDATE_REGIONS[@]}"; do |
|
log_info " Checking region: $candidate" |
|
if region_has_anthropic_models "$candidate"; then |
|
BEDROCK_REGION="$candidate" |
|
log_success "Found Anthropic models in region: $BEDROCK_REGION" |
|
return 0 |
|
fi |
|
done |
|
|
|
log_error "No Bedrock region with Anthropic models found. Tried: ${BEDROCK_CANDIDATE_REGIONS[*]}" |
|
log_error "Ensure your AWS account has Bedrock Anthropic model access enabled in at least one region." |
|
exit 1 |
|
} |
|
|
|
################################################################################ |
|
# Dynamic Model Resolution (all queries target BEDROCK_REGION) |
|
################################################################################ |
|
|
|
resolve_model_source_arn() { |
|
local model_type="$1" |
|
local model_hint |
|
model_hint=$(model_type_hint "$model_type") || return 1 |
|
|
|
local result |
|
result=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$BEDROCK_REGION" \ |
|
--type-equals SYSTEM_DEFINED \ |
|
--query "reverse(sort_by(inferenceProfileSummaries[?contains(inferenceProfileId, '${model_hint}')], &inferenceProfileId))[0].inferenceProfileArn" \ |
|
--output text 2>/dev/null) |
|
|
|
if [ -n "$result" ] && [ "$result" != "None" ]; then |
|
log_info " Resolved $model_type -> $result (system-defined profile)" >&2 |
|
echo "$result" |
|
return 0 |
|
fi |
|
|
|
# Fallback: cross-region inference profile may not exist; try foundation model directly. |
|
local foundation_id |
|
foundation_id=$(model_foundation_id "$model_type") || return 1 |
|
result=$(resolve_foundation_model_arn "$foundation_id") |
|
if [ -n "$result" ] && [ "$result" != "None" ]; then |
|
log_info " Resolved $model_type -> $result (foundation model fallback)" >&2 |
|
echo "$result" |
|
return 0 |
|
fi |
|
|
|
return 1 |
|
} |
|
|
|
resolve_foundation_model_arn() { |
|
local model_id="$1" |
|
local result |
|
result=$(aws --profile "$AWS_PROFILE" bedrock list-foundation-models \ |
|
--region "$BEDROCK_REGION" \ |
|
--query "modelSummaries[?modelId=='${model_id}'].modelArn | [0]" \ |
|
--output text 2>/dev/null) |
|
if [ -n "$result" ] && [ "$result" != "None" ]; then |
|
log_info " Resolved $model_id -> $result" >&2 |
|
echo "$result" |
|
return 0 |
|
fi |
|
return 1 |
|
} |
|
|
|
################################################################################ |
|
# Validation |
|
################################################################################ |
|
|
|
validate_inputs() { |
|
log_info "Validating setup requirements..." |
|
|
|
if [ -z "$DEVELOPER_NAME" ]; then |
|
log_error "Developer name is required" |
|
echo "Usage: ./setup-claude-code-developer.sh --create --developer <name>" |
|
echo " ./setup-claude-code-developer.sh --delete --developer <name>" |
|
echo " ./setup-claude-code-developer.sh --update --developer <name>" |
|
exit 1 |
|
fi |
|
|
|
if ! command -v aws &> /dev/null; then |
|
log_error "AWS CLI not found. Please install AWS CLI v2" |
|
exit 1 |
|
fi |
|
|
|
if ! command -v node &> /dev/null; then |
|
log_warning "Node.js not found. Claude Code requires Node.js 18+. Installing..." |
|
install_nodejs |
|
fi |
|
|
|
if [ "$AWS_PROFILE" != "dev" ]; then |
|
log_error "AWS_PROFILE must be 'dev' for this setup script. Current: '$AWS_PROFILE'" |
|
exit 1 |
|
fi |
|
|
|
if ! aws --profile "dev" sts get-caller-identity &> /dev/null; then |
|
log_error "AWS profile 'dev' not found or not configured correctly" |
|
exit 1 |
|
fi |
|
|
|
log_success "All validation checks passed" |
|
} |
|
|
|
install_nodejs() { |
|
log_info "Installing Node.js 18+..." |
|
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - |
|
sudo apt-get install -y nodejs |
|
log_success "Node.js installed" |
|
} |
|
|
|
################################################################################ |
|
# AWS Setup |
|
################################################################################ |
|
|
|
setup_aws_credentials() { |
|
log_info "Setting up AWS credentials for developer: $DEVELOPER_NAME" |
|
|
|
AWS_ACCOUNT_ID=$(aws --profile "$AWS_PROFILE" sts get-caller-identity --query Account --output text) |
|
log_success "AWS Account ID: $AWS_ACCOUNT_ID" |
|
|
|
mkdir -p "$INSTALL_DIR" |
|
log_success "AWS credentials configured" |
|
} |
|
|
|
write_aws_config() { |
|
log_info "Writing AWS config files..." |
|
|
|
cat > "$INSTALL_DIR/aws-config.sh" << EOF |
|
#!/bin/bash |
|
# Auto-generated AWS configuration for $DEVELOPER_NAME |
|
export AWS_PROFILE="$AWS_PROFILE" |
|
export AWS_REGION="$AWS_REGION" |
|
export BEDROCK_REGION="$BEDROCK_REGION" |
|
export AWS_ACCOUNT_ID="$AWS_ACCOUNT_ID" |
|
EOF |
|
|
|
chmod +x "$INSTALL_DIR/aws-config.sh" |
|
log_success "AWS config written (BEDROCK_REGION=$BEDROCK_REGION)" |
|
} |
|
|
|
################################################################################ |
|
# Application Inference Profiles Setup |
|
################################################################################ |
|
|
|
create_inference_profiles() { |
|
log_info "Creating Application Inference Profiles for cost tracking..." |
|
log_info "Bedrock region: $BEDROCK_REGION" |
|
|
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
for model_type in sonnet haiku opus46 opus47; do |
|
case "$model_type" in |
|
opus46) profile_name="claude-opus-4-6-${PROFILE_PREFIX}" ;; |
|
opus47) profile_name="claude-opus-4-7-${PROFILE_PREFIX}" ;; |
|
*) profile_name="claude-${model_type}-${PROFILE_PREFIX}" ;; |
|
esac |
|
|
|
log_info "Creating inference profile: $profile_name" |
|
model_source_arn=$(resolve_model_source_arn "$model_type" || true) |
|
if [ -z "$model_source_arn" ]; then |
|
log_warning "Skipping '$model_type': no compatible source found in $BEDROCK_REGION." |
|
continue |
|
fi |
|
|
|
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles --region "$BEDROCK_REGION" \ |
|
--type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \ |
|
--output text 2>/dev/null) |
|
|
|
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then |
|
log_warning "Profile $profile_name already exists. Deleting..." |
|
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-identifier "$existing_arn" > /dev/null |
|
log_success "Deleted old profile: $profile_name" |
|
fi |
|
|
|
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $model_type model") |
|
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-name "$profile_name" \ |
|
--description "$profile_description" \ |
|
--model-source "copyFrom=${model_source_arn}" \ |
|
--tags \ |
|
"key=developer,value=${PROFILE_PREFIX}" \ |
|
"key=model,value=${model_type}" \ |
|
"key=cost-tracking,value=enabled" \ |
|
"key=created-date,value=$(date +%Y-%m-%d)" \ |
|
--query "inferenceProfileArn" \ |
|
--output text) |
|
|
|
log_success "Created inference profile: $profile_name ($created_arn)" |
|
CREATED_PROFILES+=("$profile_name") |
|
case "$model_type" in |
|
opus46) PROFILE_ARN_OPUS46="$created_arn" ;; |
|
opus47) PROFILE_ARN_OPUS47="$created_arn" ;; |
|
sonnet) PROFILE_ARN_SONNET="$created_arn" ;; |
|
haiku) PROFILE_ARN_HAIKU="$created_arn" ;; |
|
esac |
|
done |
|
|
|
QWEN3_KEYS=("qwen3-next-80b-a3b" "qwen3-coder-next") |
|
QWEN3_IDS=("qwen.qwen3-next-80b-a3b" "qwen.qwen3-coder-next") |
|
|
|
for i in 0 1; do |
|
qwen_key="${QWEN3_KEYS[$i]}" |
|
qwen_model_id="${QWEN3_IDS[$i]}" |
|
profile_name="${qwen_key}-${PROFILE_PREFIX}" |
|
|
|
log_info "Creating inference profile: $profile_name" |
|
model_source_arn=$(resolve_foundation_model_arn "$qwen_model_id" || true) |
|
if [ -z "$model_source_arn" ]; then |
|
log_warning "Skipping '$qwen_key': model '$qwen_model_id' not found in $BEDROCK_REGION." |
|
continue |
|
fi |
|
|
|
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles --region "$BEDROCK_REGION" \ |
|
--type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \ |
|
--output text 2>/dev/null) |
|
|
|
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then |
|
log_warning "Profile $profile_name already exists. Deleting..." |
|
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-identifier "$existing_arn" > /dev/null |
|
log_success "Deleted old profile: $profile_name" |
|
fi |
|
|
|
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $qwen_key model") |
|
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-name "$profile_name" \ |
|
--description "$profile_description" \ |
|
--model-source "copyFrom=${model_source_arn}" \ |
|
--tags \ |
|
"key=developer,value=${PROFILE_PREFIX}" \ |
|
"key=model,value=${qwen_key}" \ |
|
"key=cost-tracking,value=enabled" \ |
|
"key=created-date,value=$(date +%Y-%m-%d)" \ |
|
--query "inferenceProfileArn" \ |
|
--output text) |
|
|
|
log_success "Created inference profile: $profile_name ($created_arn)" |
|
CREATED_PROFILES+=("$profile_name") |
|
case "$qwen_key" in |
|
qwen3-next-80b-a3b) PROFILE_ARN_QWEN3_NEXT="$created_arn" ;; |
|
qwen3-coder-next) PROFILE_ARN_QWEN3_CODER="$created_arn" ;; |
|
esac |
|
done |
|
|
|
if [ ${#CREATED_PROFILES[@]} -eq 0 ]; then |
|
log_error "No inference profiles were created. Cannot continue." |
|
exit 1 |
|
fi |
|
|
|
log_success "${#CREATED_PROFILES[@]} inference profile(s) created/verified: ${CREATED_PROFILES[*]}" |
|
} |
|
|
|
################################################################################ |
|
# Update Application Inference Profiles (additive-only, no delete) |
|
################################################################################ |
|
|
|
update_inference_profiles() { |
|
log_info "Syncing Application Inference Profiles (additive only)..." |
|
log_info "Bedrock region: $BEDROCK_REGION" |
|
|
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
for model_type in sonnet haiku opus46 opus47; do |
|
case "$model_type" in |
|
opus46) profile_name="claude-opus-4-6-${PROFILE_PREFIX}" ;; |
|
opus47) profile_name="claude-opus-4-7-${PROFILE_PREFIX}" ;; |
|
*) profile_name="claude-${model_type}-${PROFILE_PREFIX}" ;; |
|
esac |
|
log_info "Checking inference profile: $profile_name" |
|
|
|
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$BEDROCK_REGION" --type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \ |
|
--output text 2>/dev/null) |
|
|
|
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then |
|
log_info " Profile already exists (keeping): $profile_name" |
|
UPDATED_KEPT_PROFILES+=("$profile_name") |
|
case "$model_type" in |
|
opus46) PROFILE_ARN_OPUS46="$existing_arn" ;; |
|
opus47) PROFILE_ARN_OPUS47="$existing_arn" ;; |
|
sonnet) PROFILE_ARN_SONNET="$existing_arn" ;; |
|
haiku) PROFILE_ARN_HAIKU="$existing_arn" ;; |
|
esac |
|
continue |
|
fi |
|
|
|
model_source_arn=$(resolve_model_source_arn "$model_type" || true) |
|
if [ -z "$model_source_arn" ]; then |
|
log_warning "Skipping '$model_type': no compatible source found in $BEDROCK_REGION." |
|
continue |
|
fi |
|
|
|
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $model_type model") |
|
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-name "$profile_name" \ |
|
--description "$profile_description" \ |
|
--model-source "copyFrom=${model_source_arn}" \ |
|
--tags \ |
|
"key=developer,value=${PROFILE_PREFIX}" \ |
|
"key=model,value=${model_type}" \ |
|
"key=cost-tracking,value=enabled" \ |
|
"key=created-date,value=$(date +%Y-%m-%d)" \ |
|
--query "inferenceProfileArn" --output text) |
|
|
|
log_success "Created missing profile: $profile_name ($created_arn)" |
|
UPDATED_NEW_PROFILES+=("$profile_name") |
|
CREATED_PROFILES+=("$profile_name") |
|
case "$model_type" in |
|
opus46) PROFILE_ARN_OPUS46="$created_arn" ;; |
|
opus47) PROFILE_ARN_OPUS47="$created_arn" ;; |
|
sonnet) PROFILE_ARN_SONNET="$created_arn" ;; |
|
haiku) PROFILE_ARN_HAIKU="$created_arn" ;; |
|
esac |
|
done |
|
|
|
QWEN3_KEYS=("qwen3-next-80b-a3b" "qwen3-coder-next") |
|
QWEN3_IDS=("qwen.qwen3-next-80b-a3b" "qwen.qwen3-coder-next") |
|
|
|
for i in 0 1; do |
|
qwen_key="${QWEN3_KEYS[$i]}" |
|
profile_name="${qwen_key}-${PROFILE_PREFIX}" |
|
log_info "Checking inference profile: $profile_name" |
|
|
|
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$BEDROCK_REGION" --type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \ |
|
--output text 2>/dev/null) |
|
|
|
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then |
|
log_info " Profile already exists (keeping): $profile_name" |
|
UPDATED_KEPT_PROFILES+=("$profile_name") |
|
case "$qwen_key" in |
|
qwen3-next-80b-a3b) PROFILE_ARN_QWEN3_NEXT="$existing_arn" ;; |
|
qwen3-coder-next) PROFILE_ARN_QWEN3_CODER="$existing_arn" ;; |
|
esac |
|
continue |
|
fi |
|
|
|
model_source_arn=$(resolve_foundation_model_arn "${QWEN3_IDS[$i]}" || true) |
|
if [ -z "$model_source_arn" ]; then |
|
log_warning "Skipping '$qwen_key': model '${QWEN3_IDS[$i]}' not found in $BEDROCK_REGION." |
|
continue |
|
fi |
|
|
|
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $qwen_key model") |
|
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-name "$profile_name" \ |
|
--description "$profile_description" \ |
|
--model-source "copyFrom=${model_source_arn}" \ |
|
--tags \ |
|
"key=developer,value=${PROFILE_PREFIX}" \ |
|
"key=model,value=${qwen_key}" \ |
|
"key=cost-tracking,value=enabled" \ |
|
"key=created-date,value=$(date +%Y-%m-%d)" \ |
|
--query "inferenceProfileArn" --output text) |
|
|
|
log_success "Created missing profile: $profile_name ($created_arn)" |
|
UPDATED_NEW_PROFILES+=("$profile_name") |
|
CREATED_PROFILES+=("$profile_name") |
|
case "$qwen_key" in |
|
qwen3-next-80b-a3b) PROFILE_ARN_QWEN3_NEXT="$created_arn" ;; |
|
qwen3-coder-next) PROFILE_ARN_QWEN3_CODER="$created_arn" ;; |
|
esac |
|
done |
|
|
|
log_success "Sync complete: ${#UPDATED_NEW_PROFILES[@]} created, ${#UPDATED_KEPT_PROFILES[@]} kept" |
|
} |
|
|
|
################################################################################ |
|
# Update Summary |
|
################################################################################ |
|
|
|
print_update_summary() { |
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
echo "" |
|
echo -e "${GREEN}================================${NC}" |
|
echo -e "${GREEN}Update Complete!${NC}" |
|
echo -e "${GREEN}================================${NC}" |
|
echo "" |
|
echo "Developer: $DEVELOPER_NAME" |
|
echo "AWS Region: $AWS_REGION" |
|
echo "Bedrock Region: $BEDROCK_REGION" |
|
echo "AWS Account: $AWS_ACCOUNT_ID" |
|
echo "Profile Prefix: $PROFILE_PREFIX" |
|
echo "" |
|
|
|
if [ ${#UPDATED_NEW_PROFILES[@]} -gt 0 ]; then |
|
echo -e "${BLUE}Newly Created Profiles (${#UPDATED_NEW_PROFILES[@]}):${NC}" |
|
for p in "${UPDATED_NEW_PROFILES[@]}"; do |
|
echo -e " ${GREEN}+${NC} $p" |
|
done |
|
echo "" |
|
else |
|
echo -e "${BLUE}Newly Created Profiles:${NC} none (all profiles already existed)" |
|
echo "" |
|
fi |
|
|
|
if [ ${#UPDATED_KEPT_PROFILES[@]} -gt 0 ]; then |
|
echo -e "${BLUE}Existing Profiles Kept (${#UPDATED_KEPT_PROFILES[@]}):${NC}" |
|
for p in "${UPDATED_KEPT_PROFILES[@]}"; do |
|
echo " $p" |
|
done |
|
echo "" |
|
fi |
|
|
|
echo -e "${BLUE}Regenerated Config Files:${NC}" |
|
echo " - $INSTALL_DIR/claude-code-config.json" |
|
echo " - $INSTALL_DIR/activate.sh" |
|
echo "" |
|
echo -e "${BLUE}Next Steps:${NC}" |
|
echo " 1. Reload your shell to pick up updated ARNs:" |
|
echo " source ~/.zshrc (or ~/.bashrc for bash)" |
|
echo "" |
|
echo " 2. Verify the setup:" |
|
echo " env | grep ANTHROPIC" |
|
echo "" |
|
echo " 3. Monitor costs in AWS Console:" |
|
echo " Cost Explorer -> Filter by tags -> developer = ${PROFILE_PREFIX}" |
|
echo "" |
|
echo -e "${GREEN}Happy coding!${NC}" |
|
echo "" |
|
} |
|
|
|
################################################################################ |
|
# Delete Application Inference Profiles |
|
################################################################################ |
|
|
|
delete_inference_profiles() { |
|
log_info "Deleting Application Inference Profiles for developer: $DEVELOPER_NAME" |
|
|
|
local profile_prefix |
|
profile_prefix=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
for profile_name in \ |
|
"claude-opus-4-6-${profile_prefix}" \ |
|
"claude-opus-4-7-${profile_prefix}" \ |
|
"claude-sonnet-${profile_prefix}" \ |
|
"claude-haiku-${profile_prefix}"; do |
|
log_info " Looking for profile: $profile_name" |
|
|
|
local profile_arn |
|
profile_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$BEDROCK_REGION" \ |
|
--type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='${profile_name}'].inferenceProfileArn | [0]" \ |
|
--output text 2>/dev/null || true) |
|
|
|
if [ -z "$profile_arn" ] || [ "$profile_arn" = "None" ]; then |
|
log_warning " Profile not found (skipping): $profile_name" |
|
continue |
|
fi |
|
|
|
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-identifier "$profile_arn" > /dev/null |
|
log_success " Deleted profile: $profile_name ($profile_arn)" |
|
done |
|
|
|
for qwen_key in "qwen3-next-80b-a3b" "qwen3-coder-next"; do |
|
local profile_name="${qwen_key}-${profile_prefix}" |
|
log_info " Looking for profile: $profile_name" |
|
|
|
local profile_arn |
|
profile_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$BEDROCK_REGION" \ |
|
--type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='${profile_name}'].inferenceProfileArn | [0]" \ |
|
--output text 2>/dev/null || true) |
|
|
|
if [ -z "$profile_arn" ] || [ "$profile_arn" = "None" ]; then |
|
log_warning " Profile not found (skipping): $profile_name" |
|
continue |
|
fi |
|
|
|
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \ |
|
--region "$BEDROCK_REGION" \ |
|
--inference-profile-identifier "$profile_arn" > /dev/null |
|
log_success " Deleted profile: $profile_name ($profile_arn)" |
|
done |
|
} |
|
|
|
################################################################################ |
|
# Claude Code Configuration |
|
################################################################################ |
|
|
|
setup_claude_code_config() { |
|
log_info "Setting up Claude Code configuration..." |
|
|
|
local opus46_arn="$PROFILE_ARN_OPUS46" |
|
local opus47_arn="$PROFILE_ARN_OPUS47" |
|
local sonnet_arn="$PROFILE_ARN_SONNET" |
|
local haiku_arn="$PROFILE_ARN_HAIKU" |
|
local qwen3_next_arn="$PROFILE_ARN_QWEN3_NEXT" |
|
local qwen3_coder_arn="$PROFILE_ARN_QWEN3_CODER" |
|
|
|
local qwen3_overrides="" |
|
if [ -n "$qwen3_next_arn" ]; then |
|
qwen3_overrides="${qwen3_overrides}, |
|
\"qwen3-next-80b-a3b\": \"$qwen3_next_arn\"" |
|
fi |
|
if [ -n "$qwen3_coder_arn" ]; then |
|
qwen3_overrides="${qwen3_overrides}, |
|
\"qwen3-coder-next\": \"$qwen3_coder_arn\"" |
|
fi |
|
|
|
cat > "$INSTALL_DIR/claude-code-config.json" << EOF |
|
{ |
|
"env": { |
|
"CLAUDE_CODE_USE_BEDROCK": "1", |
|
"AWS_REGION": "$BEDROCK_REGION", |
|
"AWS_PROFILE": "$AWS_PROFILE", |
|
"AWS_ACCOUNT_ID": "$AWS_ACCOUNT_ID", |
|
"DEVELOPER_NAME": "$DEVELOPER_NAME" |
|
}, |
|
"modelOverrides": { |
|
"claude-opus-4-6": "$opus46_arn", |
|
"claude-opus-4-7": "$opus47_arn", |
|
"claude-sonnet-4-6": "$sonnet_arn", |
|
"claude-haiku-4-5": "$haiku_arn"${qwen3_overrides} |
|
}, |
|
"defaultModel": "claude-sonnet-4-6", |
|
"defaultFastModel": "claude-haiku-4-5", |
|
"defaultHeavyModel": "claude-opus-4-7" |
|
} |
|
EOF |
|
|
|
log_success "Claude Code configuration created" |
|
|
|
log_info "Updating ~/.claude/settings.json with customApiKeyModels..." |
|
local settings_file="$HOME/.claude/settings.json" |
|
mkdir -p "$HOME/.claude" |
|
|
|
# Read existing settings or start fresh, then inject customApiKeyModels and model |
|
if [ -f "$settings_file" ]; then |
|
local tmp |
|
tmp=$(mktemp) |
|
# Use python3 to safely merge JSON (available on all target platforms) |
|
python3 - "$settings_file" "$sonnet_arn" "$opus46_arn" "$opus47_arn" "$haiku_arn" > "$tmp" << 'PYEOF' |
|
import json, sys |
|
with open(sys.argv[1]) as f: |
|
cfg = json.load(f) |
|
cfg["model"] = sys.argv[2] |
|
cfg["customApiKeyModels"] = [sys.argv[3], sys.argv[4], sys.argv[2], sys.argv[5]] |
|
print(json.dumps(cfg, indent=2)) |
|
PYEOF |
|
mv "$tmp" "$settings_file" |
|
else |
|
cat > "$settings_file" << EOF |
|
{ |
|
"model": "$sonnet_arn", |
|
"customApiKeyModels": [ |
|
"$opus46_arn", |
|
"$opus47_arn", |
|
"$sonnet_arn", |
|
"$haiku_arn" |
|
] |
|
} |
|
EOF |
|
fi |
|
|
|
log_success "$HOME/.claude/settings.json updated with all model ARNs" |
|
} |
|
|
|
################################################################################ |
|
# Environment Setup |
|
################################################################################ |
|
|
|
setup_environment() { |
|
log_info "Setting up shell environment..." |
|
|
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
local opus46_arn="$PROFILE_ARN_OPUS46" |
|
local opus47_arn="$PROFILE_ARN_OPUS47" |
|
local sonnet_arn="$PROFILE_ARN_SONNET" |
|
local haiku_arn="$PROFILE_ARN_HAIKU" |
|
local qwen3_next_arn="$PROFILE_ARN_QWEN3_NEXT" |
|
local qwen3_coder_arn="$PROFILE_ARN_QWEN3_CODER" |
|
|
|
local qwen3_exports="" |
|
if [ -n "$qwen3_next_arn" ]; then |
|
qwen3_exports="${qwen3_exports} |
|
export ANTHROPIC_DEFAULT_QWEN3_NEXT_MODEL=\"${qwen3_next_arn}\"" |
|
fi |
|
if [ -n "$qwen3_coder_arn" ]; then |
|
qwen3_exports="${qwen3_exports} |
|
export ANTHROPIC_DEFAULT_QWEN3_CODER_MODEL=\"${qwen3_coder_arn}\"" |
|
fi |
|
|
|
cat > "$INSTALL_DIR/activate.sh" << EOF |
|
#!/bin/bash |
|
CLAUDE_CODE_DIR="\${HOME}/.claude-code" |
|
source "\$CLAUDE_CODE_DIR/aws-config.sh" |
|
|
|
export ANTHROPIC_DEFAULT_OPUS_46_MODEL="${opus46_arn}" |
|
export ANTHROPIC_DEFAULT_OPUS_47_MODEL="${opus47_arn}" |
|
export ANTHROPIC_DEFAULT_SONNET_MODEL="${sonnet_arn}" |
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL="${haiku_arn}"${qwen3_exports} |
|
|
|
export ANTHROPIC_MODEL="\$ANTHROPIC_DEFAULT_SONNET_MODEL" |
|
EOF |
|
|
|
chmod +x "$INSTALL_DIR/activate.sh" |
|
|
|
SHELL_RC="$HOME/.bashrc" |
|
if [ -f "$HOME/.zshrc" ]; then |
|
SHELL_RC="$HOME/.zshrc" |
|
fi |
|
|
|
if ! grep -q "claude-code" "$SHELL_RC"; then |
|
cat >> "$SHELL_RC" << 'EOF' |
|
|
|
# Claude Code environment |
|
export CLAUDE_CODE_USE_BEDROCK=1 |
|
export CLAUDE_CODE_DIR="${HOME}/.claude-code" |
|
if [ -f "$CLAUDE_CODE_DIR/activate.sh" ]; then |
|
source "$CLAUDE_CODE_DIR/activate.sh" |
|
fi |
|
EOF |
|
log_success "Added Claude Code to $SHELL_RC" |
|
fi |
|
|
|
if ! grep -q "CLAUDE_CODE_USE_BEDROCK" "$SHELL_RC"; then |
|
sed -i '/# Claude Code environment/a export CLAUDE_CODE_USE_BEDROCK=1' "$SHELL_RC" |
|
log_success "Added CLAUDE_CODE_USE_BEDROCK=1 to $SHELL_RC" |
|
fi |
|
|
|
log_success "Shell environment configured" |
|
} |
|
|
|
################################################################################ |
|
# Install Claude Code CLI |
|
################################################################################ |
|
|
|
install_claude_code_cli() { |
|
log_info "Installing Claude Code CLI..." |
|
|
|
if npm list -g @anthropic-ai/claude-code &> /dev/null; then |
|
log_warning "Claude Code CLI already installed" |
|
else |
|
npm install -g @anthropic-ai/claude-code |
|
log_success "Claude Code CLI installed" |
|
fi |
|
} |
|
|
|
################################################################################ |
|
# Cost Tracking Setup |
|
################################################################################ |
|
|
|
setup_cost_tracking() { |
|
log_info "Setting up cost tracking configuration..." |
|
|
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
cat > "$INSTALL_DIR/cost-tracking-queries.txt" << EOF |
|
# CloudWatch Insights Queries for Cost Tracking |
|
|
|
## Query 1: Total costs by model for this developer |
|
fields @timestamp, model, inputTokens, outputTokens |
|
| filter tags.developer = "$PROFILE_PREFIX" |
|
| stats sum(inputTokens) as total_input, sum(outputTokens) as total_output by model |
|
|
|
## Query 2: Daily costs breakdown |
|
fields @timestamp, model, inputTokens, outputTokens |
|
| filter tags.developer = "$PROFILE_PREFIX" |
|
| stats sum(inputTokens) as input, sum(outputTokens) as output by datefloor(@timestamp, 1d), model |
|
|
|
## Query 3: Real-time usage (last hour) |
|
fields @timestamp, model, inputTokens, outputTokens |
|
| filter tags.developer = "$PROFILE_PREFIX" and @timestamp > ago(1h) |
|
| stats count() as requests, sum(inputTokens) as input, sum(outputTokens) as output by model |
|
EOF |
|
|
|
cat > "$INSTALL_DIR/COST_TRACKING_GUIDE.md" << EOF |
|
# Cost Tracking Guide for $DEVELOPER_NAME |
|
|
|
## Overview |
|
Your Claude Code usage is tracked using AWS Application Inference Profiles with cost allocation tags. |
|
|
|
## Viewing Your Costs |
|
|
|
### Method 1: AWS Cost Explorer (Recommended) |
|
1. Log in to AWS Console |
|
2. Navigate to **Cost Explorer** |
|
3. Select **Filters** -> **Tags** -> **developer** = **${PROFILE_PREFIX}** |
|
4. Group by **Tags** -> **model** to see costs per model |
|
5. Set date range to see trends |
|
|
|
### Method 2: CloudWatch Logs (Real-time) |
|
1. Go to **CloudWatch** -> **Logs** |
|
2. Search for logs from Bedrock model invocations |
|
3. Use Insights queries from \`cost-tracking-queries.txt\` |
|
|
|
### Method 3: AWS Billing Dashboard |
|
1. Navigate to **Billing** -> **Bills** |
|
2. Filter by service: **Bedrock** |
|
3. Filter by tags matching your profile |
|
|
|
## Cost Breakdown by Model |
|
- **Opus 4.7**: Latest, highest capability - use for the most complex tasks |
|
- **Opus 4.6**: Strong reasoning and heavy lifting |
|
- **Sonnet (4-6)**: Balanced - recommended for most tasks |
|
- **Haiku (4-5)**: Fast, cheap - use for simple queries |
|
- **Qwen3 Next 80B A3B**: Large open-weight reasoning and coding alternative |
|
- **Qwen3 Coder Next**: Coding-specialized Qwen3 model |
|
|
|
## Optimizing Your Costs |
|
1. Use Haiku for simple tasks (significantly cheaper) |
|
2. Start with Sonnet, upgrade to Opus only if needed |
|
3. Monitor your usage with Cost Explorer weekly |
|
4. Review inference profile tags: developer=$PROFILE_PREFIX, model= |
|
|
|
## Troubleshooting |
|
|
|
### Costs not appearing? |
|
- Wait 24 hours for AWS Cost Explorer to update |
|
- Check that tags are applied correctly: |
|
\`\`\` |
|
aws bedrock describe-inference-profile \\ |
|
--inference-profile-arn arn:aws:bedrock:${BEDROCK_REGION}:${AWS_ACCOUNT_ID}:application-inference-profile/claude-sonnet-${PROFILE_PREFIX} |
|
\`\`\` |
|
|
|
### Using wrong model? |
|
- Check environment variables: \`env | grep ANTHROPIC\` |
|
- Use \`/model\` command in Claude Code to switch models |
|
- Default model: Sonnet (set in configuration) |
|
|
|
EOF |
|
|
|
log_success "Cost tracking configuration created" |
|
} |
|
|
|
################################################################################ |
|
# Remove Shell RC Block |
|
################################################################################ |
|
|
|
remove_shell_rc_block() { |
|
log_info "Removing Claude Code block from shell RC file(s)..." |
|
|
|
local removed_any=false |
|
|
|
for rc_file in "$HOME/.bashrc" "$HOME/.zshrc"; do |
|
if [ ! -f "$rc_file" ]; then |
|
continue |
|
fi |
|
|
|
if ! grep -q "# Claude Code environment" "$rc_file"; then |
|
continue |
|
fi |
|
|
|
# Use Python3 for portable multi-line block removal. |
|
# BSD sed (macOS) and GNU sed (Linux) differ enough that a single sed |
|
# one-liner is not reliable across both. Python3 is available on both. |
|
# Removes: blank line + "# Claude Code environment" block through "fi". |
|
python3 << PYEOF |
|
fname = "$rc_file" |
|
with open(fname) as f: |
|
lines = f.readlines() |
|
|
|
result = [] |
|
i = 0 |
|
while i < len(lines): |
|
if (lines[i].strip() == '' and |
|
i + 1 < len(lines) and |
|
lines[i + 1].rstrip('\n') == '# Claude Code environment'): |
|
# Skip past the comment then consume lines until the closing 'fi' |
|
i += 2 |
|
while i < len(lines) and lines[i].rstrip('\n') != 'fi': |
|
i += 1 |
|
i += 1 # skip the 'fi' line itself |
|
else: |
|
result.append(lines[i]) |
|
i += 1 |
|
|
|
with open(fname, 'w') as f: |
|
f.writelines(result) |
|
PYEOF |
|
|
|
log_success " Removed Claude Code block from $rc_file" |
|
removed_any=true |
|
done |
|
|
|
if [ "$removed_any" = false ]; then |
|
log_warning "No Claude Code block found in ~/.bashrc or ~/.zshrc — nothing to remove" |
|
fi |
|
} |
|
|
|
################################################################################ |
|
# Delete Setup |
|
################################################################################ |
|
|
|
delete_setup() { |
|
log_info "Starting Claude Code developer teardown..." |
|
log_info "Developer: $DEVELOPER_NAME" |
|
|
|
discover_bedrock_region |
|
|
|
delete_inference_profiles |
|
|
|
if [ -d "$INSTALL_DIR" ]; then |
|
rm -rf "$INSTALL_DIR" |
|
log_success "Removed directory: $INSTALL_DIR" |
|
else |
|
log_warning "Directory not found (skipping): $INSTALL_DIR" |
|
fi |
|
|
|
remove_shell_rc_block |
|
|
|
log_success "Teardown complete for developer: $DEVELOPER_NAME" |
|
} |
|
|
|
################################################################################ |
|
# Verification |
|
################################################################################ |
|
|
|
verify_setup() { |
|
log_info "Verifying setup..." |
|
|
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
if aws --profile "$AWS_PROFILE" sts get-caller-identity &> /dev/null; then |
|
log_success "AWS credentials verified" |
|
else |
|
log_error "AWS credentials verification failed" |
|
return 1 |
|
fi |
|
|
|
log_info "Checking inference profiles..." |
|
for profile_name in "${CREATED_PROFILES[@]}"; do |
|
if aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \ |
|
--region "$BEDROCK_REGION" --type-equals APPLICATION \ |
|
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name']" \ |
|
--output text | grep -q "$profile_name"; then |
|
log_success "Inference profile found: $profile_name" |
|
else |
|
log_error "Inference profile not found: $profile_name" |
|
return 1 |
|
fi |
|
done |
|
|
|
if npm list -g @anthropic-ai/claude-code &> /dev/null; then |
|
log_success "Claude Code CLI verified" |
|
else |
|
log_error "Claude Code CLI not found" |
|
return 1 |
|
fi |
|
|
|
if [ -f "$INSTALL_DIR/claude-code-config.json" ]; then |
|
log_success "Configuration files created" |
|
else |
|
log_error "Configuration files missing" |
|
return 1 |
|
fi |
|
|
|
return 0 |
|
} |
|
|
|
################################################################################ |
|
# Summary |
|
################################################################################ |
|
|
|
print_summary() { |
|
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
echo "" |
|
echo -e "${GREEN}================================${NC}" |
|
echo -e "${GREEN}Setup Complete!${NC}" |
|
echo -e "${GREEN}================================${NC}" |
|
echo "" |
|
echo "Developer: $DEVELOPER_NAME" |
|
echo "AWS Region: $AWS_REGION" |
|
echo "Bedrock Region: $BEDROCK_REGION" |
|
echo "AWS Account: $AWS_ACCOUNT_ID" |
|
echo "Profile Prefix: $PROFILE_PREFIX" |
|
echo "" |
|
echo -e "${BLUE}Configuration Files:${NC}" |
|
echo " - $INSTALL_DIR/aws-config.sh" |
|
echo " - $INSTALL_DIR/claude-code-config.json" |
|
echo " - $INSTALL_DIR/activate.sh" |
|
echo " - $INSTALL_DIR/COST_TRACKING_GUIDE.md" |
|
echo "" |
|
echo -e "${BLUE}Inference Profiles (${#CREATED_PROFILES[@]}):${NC}" |
|
for p in "${CREATED_PROFILES[@]}"; do |
|
echo " - $p" |
|
done |
|
echo "" |
|
echo -e "${BLUE}Next Steps:${NC}" |
|
echo " 1. Reload your shell:" |
|
echo " source ~/.zshrc (or ~/.bashrc for bash)" |
|
echo "" |
|
echo " 2. Start using Claude Code:" |
|
echo " claude" |
|
echo "" |
|
echo " 3. Switch models in Claude Code with:" |
|
echo " /model" |
|
echo "" |
|
echo " 4. Monitor costs in AWS Console:" |
|
echo " Cost Explorer -> Filter by tags -> developer = ${PROFILE_PREFIX}" |
|
echo "" |
|
echo " 5. View cost tracking guide:" |
|
echo " cat $INSTALL_DIR/COST_TRACKING_GUIDE.md" |
|
echo "" |
|
echo -e "${BLUE}Useful Commands:${NC}" |
|
echo " # View your inference profiles" |
|
echo " aws --profile dev bedrock list-inference-profiles --region $BEDROCK_REGION" |
|
echo "" |
|
echo " # Check costs via CloudWatch" |
|
echo " cat $INSTALL_DIR/cost-tracking-queries.txt" |
|
echo "" |
|
echo -e "${GREEN}Happy coding!${NC}" |
|
echo "" |
|
} |
|
|
|
################################################################################ |
|
# Delete Summary |
|
################################################################################ |
|
|
|
print_delete_summary() { |
|
local profile_prefix |
|
profile_prefix=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') |
|
|
|
echo "" |
|
echo -e "${GREEN}================================${NC}" |
|
echo -e "${GREEN}Teardown Complete!${NC}" |
|
echo -e "${GREEN}================================${NC}" |
|
echo "" |
|
echo "Developer: $DEVELOPER_NAME" |
|
echo "Bedrock Region: $BEDROCK_REGION" |
|
echo "" |
|
echo -e "${BLUE}What was removed:${NC}" |
|
echo " - Application Inference Profiles (if they existed):" |
|
echo " claude-opus-${profile_prefix}" |
|
echo " claude-opus-47-${profile_prefix}" |
|
echo " claude-sonnet-${profile_prefix}" |
|
echo " claude-haiku-${profile_prefix}" |
|
echo " qwen3-next-80b-a3b-${profile_prefix}" |
|
echo " qwen3-coder-next-${profile_prefix}" |
|
echo " - Local directory: $INSTALL_DIR" |
|
echo " - Claude Code block from ~/.bashrc and/or ~/.zshrc" |
|
echo "" |
|
echo -e "${BLUE}What was NOT removed:${NC}" |
|
echo " - Claude Code CLI (npm package @anthropic-ai/claude-code)" |
|
echo " To uninstall manually: npm uninstall -g @anthropic-ai/claude-code" |
|
echo "" |
|
echo -e "${YELLOW}Note:${NC} Reload your shell to clear leftover environment variables:" |
|
echo " source ~/.bashrc (or ~/.zshrc for zsh)" |
|
echo "" |
|
} |
|
|
|
################################################################################ |
|
# Update Orchestration |
|
################################################################################ |
|
|
|
run_update() { |
|
log_info "Starting Claude Code developer update (additive sync)..." |
|
log_info "Developer: $DEVELOPER_NAME" |
|
log_info "AWS Region: $AWS_REGION" |
|
|
|
validate_inputs |
|
setup_aws_credentials |
|
discover_bedrock_region |
|
|
|
if [ "$BEDROCK_REGION" != "$AWS_REGION" ]; then |
|
log_warning "Anthropic models not available in $AWS_REGION. Using Bedrock region: $BEDROCK_REGION" |
|
fi |
|
|
|
update_inference_profiles |
|
setup_claude_code_config |
|
setup_environment |
|
|
|
if verify_setup; then |
|
log_success "All update steps completed successfully" |
|
print_update_summary |
|
else |
|
log_error "Verification failed. Please check the errors above." |
|
exit 1 |
|
fi |
|
} |
|
|
|
################################################################################ |
|
# Main Execution |
|
################################################################################ |
|
|
|
main() { |
|
if [ "$ACTION" != "--create" ] && [ "$ACTION" != "--delete" ] && [ "$ACTION" != "--update" ]; then |
|
log_error "A valid action flag is required." |
|
echo "Usage: ./setup-claude-code-developer.sh --create --developer <name>" |
|
echo " ./setup-claude-code-developer.sh --delete --developer <name>" |
|
echo " ./setup-claude-code-developer.sh --update --developer <name>" |
|
exit 1 |
|
fi |
|
|
|
if [ "$ACTION" = "--create" ]; then |
|
log_info "Starting Claude Code developer setup..." |
|
log_info "Developer: $DEVELOPER_NAME" |
|
log_info "AWS Region: $AWS_REGION" |
|
|
|
validate_inputs |
|
setup_aws_credentials |
|
discover_bedrock_region |
|
|
|
if [ "$BEDROCK_REGION" != "$AWS_REGION" ]; then |
|
log_warning "Anthropic models not available in $AWS_REGION. Using Bedrock region: $BEDROCK_REGION" |
|
fi |
|
|
|
write_aws_config |
|
create_inference_profiles |
|
setup_claude_code_config |
|
setup_environment |
|
install_claude_code_cli |
|
setup_cost_tracking |
|
|
|
if verify_setup; then |
|
log_success "All setup steps completed successfully" |
|
print_summary |
|
else |
|
log_error "Verification failed. Please check the errors above." |
|
exit 1 |
|
fi |
|
|
|
elif [ "$ACTION" = "--delete" ]; then |
|
validate_inputs |
|
delete_setup |
|
print_delete_summary |
|
|
|
elif [ "$ACTION" = "--update" ]; then |
|
run_update |
|
fi |
|
} |
|
|
|
main "$@" |