diff --git a/cli/src/__tests__/security.test.ts b/cli/src/__tests__/security.test.ts index 36244ec86..97e9b90d1 100644 --- a/cli/src/__tests__/security.test.ts +++ b/cli/src/__tests__/security.test.ts @@ -182,8 +182,8 @@ wget http://example.com/install.sh | sh validatePrompt("Run $(echo test)"); throw new Error("Expected validatePrompt to throw"); } catch (e: any) { - expect(e.message).toContain("prompt-file"); - expect(e.message).toContain("spawn"); + expect(e.message).toContain("false positive"); + expect(e.message).toContain("rephrasing"); } }); diff --git a/cli/src/security.ts b/cli/src/security.ts index 2e0069a59..42fac8ed8 100644 --- a/cli/src/security.ts +++ b/cli/src/security.ts @@ -108,9 +108,8 @@ export function validatePrompt(prompt: string): void { throw new Error( `Prompt blocked: contains potentially dangerous pattern (${description}).\n` + `\n` + - `If this is a false positive, use --prompt-file instead:\n` + - ` echo "your prompt" > prompt.txt\n` + - ` spawn --prompt-file prompt.txt` + `If this is a false positive, try rephrasing to avoid shell-like syntax\n` + + `(e.g., describe the command instead of writing it literally).` ); } } diff --git a/digitalocean/claude.sh b/digitalocean/claude.sh index f0322bf8a..480b46e02 100755 --- a/digitalocean/claude.sh +++ b/digitalocean/claude.sh @@ -31,9 +31,9 @@ verify_server_connectivity "${DO_SERVER_IP}" wait_for_cloud_init "${DO_SERVER_IP}" 60 # 5. Verify Claude Code is installed (fallback to manual install) -log_warn "Verifying Claude Code installation..." +log_step "Verifying Claude Code installation..." if ! run_server "${DO_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$PATH && command -v claude" >/dev/null 2>&1; then - log_warn "Claude Code not found, installing manually..." + log_step "Claude Code not found, installing manually..." run_server "${DO_SERVER_IP}" "curl -fsSL https://claude.ai/install.sh | bash" fi @@ -54,7 +54,7 @@ else fi # 7. Inject environment variables into ~/.zshrc -log_warn "Setting up environment variables..." +log_step "Setting up environment variables..." inject_env_vars_ssh "${DO_SERVER_IP}" upload_file run_server \ "OPENROUTER_API_KEY=${OPENROUTER_API_KEY}" \ "ANTHROPIC_BASE_URL=https://openrouter.ai/api" \ @@ -74,7 +74,7 @@ log_info "Droplet: ${DROPLET_NAME} (ID: ${DO_DROPLET_ID}, IP: ${DO_SERVER_IP})" echo "" # 9. Start Claude Code interactively -log_warn "Starting Claude Code..." +log_step "Starting Claude Code..." sleep 1 clear interactive_session "${DO_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$PATH && source ~/.zshrc && claude" diff --git a/digitalocean/lib/common.sh b/digitalocean/lib/common.sh index e2e88243c..bb70e4d08 100755 --- a/digitalocean/lib/common.sh +++ b/digitalocean/lib/common.sh @@ -166,7 +166,7 @@ create_server() { validate_resource_name "$size" || { log_error "Invalid DO_DROPLET_SIZE"; return 1; } validate_region_name "$region" || { log_error "Invalid DO_REGION"; return 1; } - log_warn "Creating DigitalOcean droplet '$name' (size: $size, region: $region)..." + log_step "Creating DigitalOcean droplet '$name' (size: $size, region: $region)..." # Get all SSH key IDs local ssh_keys_response @@ -219,7 +219,7 @@ interactive_session() { ssh_interactive_session "$@"; } destroy_server() { local droplet_id="$1" - log_warn "Destroying droplet $droplet_id..." + log_step "Destroying droplet $droplet_id..." local response response=$(do_api DELETE "/droplets/$droplet_id") diff --git a/hetzner/claude.sh b/hetzner/claude.sh index de7690721..78346bf2b 100755 --- a/hetzner/claude.sh +++ b/hetzner/claude.sh @@ -28,9 +28,9 @@ verify_server_connectivity "${HETZNER_SERVER_IP}" wait_for_cloud_init "${HETZNER_SERVER_IP}" 60 # 5. Verify Claude Code is installed (fallback to manual install) -log_warn "Verifying Claude Code installation..." +log_step "Verifying Claude Code installation..." if ! run_server "${HETZNER_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$PATH && command -v claude" >/dev/null 2>&1; then - log_warn "Claude Code not found, installing manually..." + log_step "Claude Code not found, installing manually..." run_server "${HETZNER_SERVER_IP}" "curl -fsSL https://claude.ai/install.sh | bash" fi @@ -50,7 +50,7 @@ else OPENROUTER_API_KEY=$(get_openrouter_api_key_oauth 5180) fi -log_warn "Setting up environment variables..." +log_step "Setting up environment variables..." inject_env_vars_ssh "${HETZNER_SERVER_IP}" upload_file run_server \ "OPENROUTER_API_KEY=${OPENROUTER_API_KEY}" \ "ANTHROPIC_BASE_URL=https://openrouter.ai/api" \ @@ -70,7 +70,7 @@ log_info "Server: ${SERVER_NAME} (ID: ${HETZNER_SERVER_ID}, IP: ${HETZNER_SERVER echo "" # 9. Start Claude Code interactively -log_warn "Starting Claude Code..." +log_step "Starting Claude Code..." sleep 1 clear interactive_session "${HETZNER_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$PATH && source ~/.zshrc && claude" diff --git a/hetzner/lib/common.sh b/hetzner/lib/common.sh index bcc37ee1a..34363f2f6 100755 --- a/hetzner/lib/common.sh +++ b/hetzner/lib/common.sh @@ -244,7 +244,7 @@ create_server() { validate_resource_name "$server_type" || { log_error "Invalid HETZNER_SERVER_TYPE"; return 1; } validate_region_name "$location" || { log_error "Invalid HETZNER_LOCATION"; return 1; } - log_warn "Creating Hetzner server '$name' (type: $server_type, location: $location)..." + log_step "Creating Hetzner server '$name' (type: $server_type, location: $location)..." # Get all SSH key IDs local ssh_keys_response @@ -280,7 +280,7 @@ interactive_session() { ssh_interactive_session "$@"; } destroy_server() { local server_id="$1" - log_warn "Destroying server $server_id..." + log_step "Destroying server $server_id..." local response response=$(hetzner_api DELETE "/servers/$server_id") diff --git a/shared/common.sh b/shared/common.sh index 173afaae1..02a93abef 100644 --- a/shared/common.sh +++ b/shared/common.sh @@ -15,6 +15,7 @@ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' +CYAN='\033[0;36m' NC='\033[0m' # No Color # Print colored messages (to stderr so they don't pollute command substitution output) @@ -30,6 +31,11 @@ log_error() { printf '%b\n' "${RED}${1}${NC}" >&2 } +# Progress/status messages (use instead of log_warn for non-warning status updates) +log_step() { + printf '%b\n' "${CYAN}${1}${NC}" >&2 +} + # ============================================================ # Configurable timing constants # ============================================================ @@ -111,7 +117,7 @@ open_browser() { elif command -v xdg-open &> /dev/null; then xdg-open "${url}" /dev/null; then log_error "${agent_name} installation failed: command '${agent_cmd}' not found in PATH" @@ -1334,7 +1340,7 @@ generic_ssh_wait() { local max_interval=30 local elapsed_time=0 - log_warn "Waiting for ${description} to ${ip} (this usually takes 30-90 seconds)..." + log_step "Waiting for ${description} to ${ip} (this usually takes 30-90 seconds)..." while [[ "${attempt}" -le "${max_attempts}" ]]; do # shellcheck disable=SC2086 if ssh ${ssh_opts} "${username}@${ip}" "${test_cmd}" >/dev/null 2>&1; then @@ -1346,7 +1352,7 @@ generic_ssh_wait() { local jitter jitter=$(calculate_retry_backoff "${interval}" "${max_interval}") - log_warn "Waiting for ${description}... (attempt ${attempt}/${max_attempts}, elapsed ${elapsed_time}s, retry in ${jitter}s)" + log_step "Waiting for ${description}... (attempt ${attempt}/${max_attempts}, elapsed ${elapsed_time}s, retry in ${jitter}s)" sleep "${jitter}" elapsed_time=$((elapsed_time + jitter)) @@ -1452,7 +1458,7 @@ generic_wait_for_instance() { local poll_delay="${INSTANCE_STATUS_POLL_DELAY:-5}" local attempt=1 - log_warn "Waiting for ${description} to become ${target_status}..." + log_step "Waiting for ${description} to become ${target_status}..." while [[ "${attempt}" -le "${max_attempts}" ]]; do local response @@ -1472,7 +1478,7 @@ generic_wait_for_instance() { fi fi - log_warn "${description} status: ${status} (${attempt}/${max_attempts})" + log_step "${description} status: ${status} (${attempt}/${max_attempts})" sleep "${poll_delay}" attempt=$((attempt + 1)) done @@ -1583,8 +1589,8 @@ ensure_api_token_with_provider() { # Prompt for new token echo "" - log_warn "${provider_name} API Token Required" - log_warn "Get your token from: ${help_url}" + log_step "${provider_name} API Token Required" + log_step "Get your token from: ${help_url}" echo "" local token @@ -1708,7 +1714,7 @@ setup_claude_code_config() { local upload_callback="${2}" local run_callback="${3}" - log_warn "Configuring Claude Code..." + log_step "Configuring Claude Code..." # Create ~/.claude directory ${run_callback} "mkdir -p ~/.claude" @@ -1762,7 +1768,7 @@ setup_openclaw_config() { local upload_callback="${3}" local run_callback="${4}" - log_warn "Configuring openclaw..." + log_step "Configuring openclaw..." # Create ~/.openclaw directory ${run_callback} "rm -rf ~/.openclaw && mkdir -p ~/.openclaw" @@ -1804,7 +1810,7 @@ setup_continue_config() { local upload_callback="${2}" local run_callback="${3}" - log_warn "Configuring Continue..." + log_step "Configuring Continue..." # Create ~/.continue directory ${run_callback} "mkdir -p ~/.continue" @@ -1938,7 +1944,7 @@ ensure_ssh_key_with_provider() { fi # Register the key (provider-specific) - log_warn "Registering SSH key with ${provider_name}..." + log_step "Registering SSH key with ${provider_name}..." local key_name key_name="spawn-$(hostname)-$(date +%s)" diff --git a/sprite/lib/common.sh b/sprite/lib/common.sh index f502782d7..158652b36 100644 --- a/sprite/lib/common.sh +++ b/sprite/lib/common.sh @@ -53,7 +53,7 @@ ensure_sprite_installed() { done # sprite not found, install it - log_warn "Installing sprite CLI..." + log_step "Installing sprite CLI..." if ! curl -fsSL https://sprites.dev/install.sh | bash; then log_error "Failed to install sprite CLI" log_error "" @@ -80,7 +80,7 @@ ensure_sprite_installed() { # Check if already authenticated with sprite ensure_sprite_authenticated() { if ! sprite org list &> /dev/null; then - log_warn "Logging in to sprite..." + log_step "Logging in to sprite..." sprite login || true fi } @@ -107,10 +107,10 @@ ensure_sprite_exists() { return 0 fi - log_warn "Creating sprite '${sprite_name}'..." + log_step "Creating sprite '${sprite_name}'..." sprite create -skip-console "${sprite_name}" || true - log_warn "Waiting for sprite to be provisioned..." + log_step "Waiting for sprite to be provisioned..." local elapsed=0 while [[ "${elapsed}" -lt "${max_wait}" ]]; do if sprite list 2>/dev/null | grep -qE "^${sprite_name}( |$)"; then @@ -131,13 +131,13 @@ verify_sprite_connectivity() { local max_attempts=${2:-6} local attempt=1 - log_warn "Verifying sprite connectivity..." + log_step "Verifying sprite connectivity..." while [[ "${attempt}" -le "${max_attempts}" ]]; do if sprite exec -s "${sprite_name}" -- echo "ok" >/dev/null 2>&1; then log_info "Sprite '${sprite_name}' is ready" return 0 fi - log_warn "Sprite not ready, retrying (${attempt}/${max_attempts})..." + log_step "Sprite not ready, retrying (${attempt}/${max_attempts})..." sleep "${SPRITE_CONNECTIVITY_POLL_DELAY}" ((attempt++)) done @@ -166,7 +166,7 @@ run_sprite() { # Configure shell environment (PATH, zsh setup) setup_shell_environment() { local sprite_name=${1} - log_warn "Configuring shell environment..." + log_step "Configuring shell environment..." # Create temp file with path config local path_temp