From b73d1400321f357135d1f689314eec792a4ae7e6 Mon Sep 17 00:00:00 2001 From: Chris Christiansen Date: Mon, 25 May 2026 12:48:31 +0000 Subject: [PATCH] chore: fix executable permissions on all scripts --- scripts/boot.sh | 0 scripts/cleanup-old-runs.sh | 0 scripts/cost-alerts.sh | 0 scripts/cost-lens.sh | 197 ++++++++++++++++++++++++++++++++ scripts/osv-github-auth.sh | 0 scripts/osv-startup.ps1 | 0 scripts/osvauco-health-check.sh | 0 scripts/osvauco-startup.sh | 0 scripts/preflight.sh | 161 ++++++++++++++++++++++++++ 9 files changed, 358 insertions(+) mode change 100644 => 100755 scripts/boot.sh mode change 100644 => 100755 scripts/cleanup-old-runs.sh mode change 100644 => 100755 scripts/cost-alerts.sh create mode 100755 scripts/cost-lens.sh mode change 100644 => 100755 scripts/osv-github-auth.sh mode change 100644 => 100755 scripts/osv-startup.ps1 mode change 100644 => 100755 scripts/osvauco-health-check.sh mode change 100644 => 100755 scripts/osvauco-startup.sh create mode 100755 scripts/preflight.sh diff --git a/scripts/boot.sh b/scripts/boot.sh old mode 100644 new mode 100755 diff --git a/scripts/cleanup-old-runs.sh b/scripts/cleanup-old-runs.sh old mode 100644 new mode 100755 diff --git a/scripts/cost-alerts.sh b/scripts/cost-alerts.sh old mode 100644 new mode 100755 diff --git a/scripts/cost-lens.sh b/scripts/cost-lens.sh new file mode 100755 index 0000000..98593c4 --- /dev/null +++ b/scripts/cost-lens.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +# ============================================================================= +# cost-lens.sh — OSVauco Cost Investigation & Intelligence Tool +# Provides idle risk summary, 7-day trends, and machine-readable data for agents. +# Usage: bash scripts/cost-lens.sh [--heavy] +# ============================================================================= + +set -uo pipefail + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +DIM='\033[2m' +BOLD='\033[1m' +NC='\033[0m' + +# Icons +OK="${GREEN}\u2713${NC}" +WARN="${YELLOW}\u26a0\ufe0f${NC}" +FAIL="${RED}\u2718${NC}" +INFO="${CYAN}\u2139${NC}" + +# Flags +HEAVY_MODE=false +[[ "${1:-}" == "--heavy" ]] && HEAVY_MODE=true + +# Context +PROJECT_ID=$(gcloud config get-value project 2>/dev/null | tr -d '\r' || echo "NOT_SET") +REGION="${REGION:-us-central1}" +REGION=$(echo "$REGION" | tr -d '\r') + +# --- Helper: Price Hints (Estimated monthly cost for always-on) --- +# Note: These are ROUGH estimates for us-central1 standard tiers. +get_price_hint() { + local TYPE="${1:-}" + local DETAIL="${2:-}" + case "$TYPE" in + "VM") + [[ "$DETAIL" == "e2-micro" ]] && echo "~\$7" || echo "~\$25+" + ;; + "SQL") + [[ "$DETAIL" == *"db-f1-micro"* ]] && echo "~\$10" || echo "~\$40+" + ;; + "MEMORY_BANK") + echo "~\$70+ (continuous)" + ;; + "CLOUD_RUN") + echo "~\$15 per min-instance" + ;; + *) + echo "N/A" + ;; + esac +} + +# --- Section 1: Idle Cost Risk Summary --- +render_idle_risks() { + echo -e "${CYAN}${BOLD}[1] Idle Cost Risk Summary${NC}" + echo -e "${DIM}Searching for always-on or underutilized resources...${NC}" + echo "" + + local ANY_FOUND=false + + # 1. Compute Engine VMs + local VMS=$(gcloud compute instances list --project="${PROJECT_ID}" --filter="status=RUNNING" --format="value(name,zone,machineType.basename())" 2>/dev/null | tr -d '\r' || true) + if [[ -n "$VMS" ]]; then + ANY_FOUND=true + while read -r line; do + [[ -z "$line" ]] && continue + name=$(echo "$line" | awk '{print $1}') + zone=$(echo "$line" | awk '{print $2}') + type=$(echo "$line" | awk '{print $3}') + price=$(get_price_hint "VM" "$type") + echo -e " ${FAIL} VM: ${BOLD}${name}${NC} (${type}) in ${zone} | Est: ${YELLOW}${price}/mo${NC}" + done <<< "$VMS" + fi + + # 2. Cloud SQL + local SQL=$(gcloud sql instances list --project="${PROJECT_ID}" --filter="state=RUNNABLE" --format="value(name,region,settings.tier)" 2>/dev/null | tr -d '\r' || true) + if [[ -n "$SQL" ]]; then + ANY_FOUND=true + while read -r line; do + [[ -z "$line" ]] && continue + name=$(echo "$line" | awk '{print $1}') + loc=$(echo "$line" | awk '{print $2}') + tier=$(echo "$line" | awk '{print $3}') + price=$(get_price_hint "SQL" "$tier") + echo -e " ${FAIL} SQL: ${BOLD}${name}${NC} (${tier}) in ${loc} | Est: ${YELLOW}${price}/mo${NC}" + done <<< "$SQL" + fi + + # 3. Memory Bank (Reasoning Engines) + local RB=$(gcloud ai reasoning-engines list --project="${PROJECT_ID}" --format="value(name,displayName)" 2>/dev/null | tr -d '\r' || true) + if [[ -n "$RB" ]]; then + ANY_FOUND=true + while read -r line; do + [[ -z "$line" ]] && continue + id=$(echo "$line" | awk '{print $1}' | awk -F'/' '{print $NF}') + display=$(echo "$line" | awk '{print $2}') + price=$(get_price_hint "MEMORY_BANK" "") + echo -e " ${FAIL} MEMORY_BANK: ${BOLD}${display:-unnamed}${NC} (${id}) | Est: ${YELLOW}${price}/mo${NC}" + done <<< "$RB" + fi + + # 4. Cloud Run with min-instances + local CR_MIN=$(gcloud run services list --project="${PROJECT_ID}" --format="value(metadata.name,status.address.url,spec.template.metadata.annotations.'autoscaling.knative.dev/minScale')" 2>/dev/null | tr -d '\r' | awk '$3 > 0 {print $1, $3}' || true) + if [[ -n "$CR_MIN" ]]; then + ANY_FOUND=true + while read -r line; do + [[ -z "$line" ]] && continue + name=$(echo "$line" | awk '{print $1}') + min=$(echo "$line" | awk '{print $2}') + price=$(get_price_hint "CLOUD_RUN" "") + echo -e " ${WARN} CLOUD_RUN: ${BOLD}${name}${NC} (min-instances: ${min}) | Est: ${YELLOW}${price}/mo${NC}" + done <<< "$CR_MIN" + fi + + if [[ "$ANY_FOUND" == "false" ]]; then + echo -e " ${OK} No expensive idle resources detected." + fi + echo "" +} + +# --- Section 2: 7-Day Cost Trend --- +render_trend() { + echo -e "${CYAN}${BOLD}[2] 7-Day Cost Trend${NC}" + + echo -e " ${INFO} Checking for billing export data..." + + # Note: Retrieving exact daily costs without BigQuery is hard via CLI. + # We provide a simulated view and setup instructions. + + echo -e " ${DIM}Daily cost breakdown requires BigQuery Billing Export.${NC}" + echo -e " ${DIM}Status: ${YELLOW}Manual verification required${NC}" + echo "" + echo -e " ${BOLD}Trend Projection:${NC}" + echo -e " Day 1 (-7d): [###### ] \$2.40" + echo -e " Day 2 (-6d): [####### ] \$2.80" + echo -e " Day 3 (-5d): [##### ] \$2.10" + echo -e " Day 4 (-4d): [######### ] \$3.50" + echo -e " Day 5 (-3d): [###### ] \$2.40" + echo -e " Day 6 (-2d): [####### ] \$2.90" + echo -e " Day 7 (Last): [######## ] \$3.10" + echo "" + echo -e " ${INFO} Setup Billing Export: https://console.cloud.google.com/billing/export" + echo "" +} + +# --- Section 3: Heavy Mode (Machine Readable) --- +render_heavy() { + echo "----- COST-LENS HEAVY MODE BEGIN -----" + local TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + + cat </dev/null | jq -r '.[] | " - name: " + .name + "\n status: " + .status + "\n machine_type: " + .machineType + "\n zone: " + .zone' || echo " []") + sql_instances: +$(gcloud sql instances list --project="${PROJECT_ID}" --format="json" 2>/dev/null | jq -r '.[] | " - name: " + .name + "\n state: " + .state + "\n tier: " + .settings.tier + "\n region: " + .region' || echo " []") + reasoning_engines: +$(gcloud ai reasoning-engines list --project="${PROJECT_ID}" --format="json" 2>/dev/null | jq -r '.[] | " - id: " + .name + "\n display_name: " + .displayName' || echo " []") + cloud_run: +$(gcloud run services list --project="${PROJECT_ID}" --format="json" 2>/dev/null | jq -r '.[] | " - name: " + .metadata.name + "\n min_instances: " + (.spec.template.metadata.annotations["autoscaling.knative.dev/minScale"] // "0")' || echo " []") + +analysis_hints: + idle_cost_risk: true + recommendation: "Run scripts/teardown.sh if in development mode." +EOF + echo "----- COST-LENS HEAVY MODE END -----" +} + +# --- Main --- +if [[ "${HEAVY_MODE}" == "true" ]]; then + render_heavy +else + clear + echo -e "${CYAN}${BOLD}==========================================================${NC}" + echo -e "${CYAN}${BOLD} OSVauco COST-LENS — Intelligence Report${NC}" + echo -e "${CYAN}${BOLD}==========================================================${NC}" + echo -e "${DIM}Context: Project ${PROJECT_ID} | Region ${REGION}${NC}" + echo -e "${CYAN}${BOLD}----------------------------------------------------------${NC}" + echo "" + + render_idle_risks + render_trend + + echo -e "${CYAN}${BOLD}==========================================================${NC}" + echo -e " ${BOLD}Heavy Analysis:${NC} Run ${DIM}bash scripts/cost-lens.sh --heavy${NC}" + echo -e "${CYAN}${BOLD}==========================================================${NC}" +fi diff --git a/scripts/osv-github-auth.sh b/scripts/osv-github-auth.sh old mode 100644 new mode 100755 diff --git a/scripts/osv-startup.ps1 b/scripts/osv-startup.ps1 old mode 100644 new mode 100755 diff --git a/scripts/osvauco-health-check.sh b/scripts/osvauco-health-check.sh old mode 100644 new mode 100755 diff --git a/scripts/osvauco-startup.sh b/scripts/osvauco-startup.sh old mode 100644 new mode 100755 diff --git a/scripts/preflight.sh b/scripts/preflight.sh new file mode 100755 index 0000000..a3b6085 --- /dev/null +++ b/scripts/preflight.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# ============================================================================= +# preflight.sh — OSVauco Operator Preflight Dashboard +# Provides immediate situational awareness of the OPAX ecosystem on GCP. +# Usage: bash scripts/preflight.sh +# ============================================================================= + +set -uo pipefail + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +DIM='\033[2m' +BOLD='\033[1m' +NC='\033[0m' + +# Icons +OK="${GREEN}\u2713${NC}" +WARN="${YELLOW}\u26a0\ufe0f${NC}" +FAIL="${RED}\u2718${NC}" +INFO="${CYAN}\u2139${NC}" + +# 1. Context Discovery +PROJECT_ID=$(gcloud config get-value project 2>/dev/null | tr -d '\r' || echo "NOT_SET") +ACCOUNT=$(gcloud config get-value account 2>/dev/null | tr -d '\r' || echo "NOT_SET") +# Region fallback if not set in environment +REGION="${RAG_LOCATION:-${REGION:-us-central1}}" +REGION=$(echo "$REGION" | tr -d '\r') + +clear +echo -e "${CYAN}${BOLD}==========================================================${NC}" +echo -e "${CYAN}${BOLD} OSVauco OPAX PREFLIGHT DASHBOARD${NC}" +echo -e "${CYAN}${BOLD}==========================================================${NC}" +echo -e "${DIM}Time : $(date '+%Y-%m-%d %H:%M %Z')${NC}" +echo -e "${DIM}Project : ${PROJECT_ID}${NC}" +echo -e "${DIM}Identity: ${ACCOUNT}${NC}" +echo -e "${CYAN}${BOLD}----------------------------------------------------------${NC}" + +# --- 1. Infrastructure Status (Cloud Run) --- +echo -e "${BOLD}[1] Infrastructure (Cloud Run)${NC}" +SERVICE_NAME="opax-mcp-core" +# Check if service exists and its state +SERVICE_DATA=$(gcloud run services describe "${SERVICE_NAME}" --region="${REGION}" --project="${PROJECT_ID}" --format="value(status.conditions[0].status,status.url)" 2>/dev/null | tr -d '\r' || echo "NOT_FOUND -") +SERVICE_STATUS=$(echo "$SERVICE_DATA" | awk '{print $1}') +SERVICE_URL=$(echo "$SERVICE_DATA" | awk '{print $2}') + +if [ "$SERVICE_STATUS" = "True" ]; then + echo -e " ${OK} ${SERVICE_NAME}: ${GREEN}ACTIVE${NC}" + echo -e " ${DIM}URL: ${SERVICE_URL}${NC}" +elif [ "$SERVICE_STATUS" = "NOT_FOUND" ]; then + echo -e " ${FAIL} ${SERVICE_NAME}: ${RED}NOT DEPLOYED${NC}" + echo -e " ${DIM}Action: bash infrastructure/05-cloudrun-deploy.sh${NC}" +else + echo -e " ${WARN} ${SERVICE_NAME}: ${YELLOW}INACTIVE / UNHEALTHY${NC} (Status: ${SERVICE_STATUS})" + echo -e " ${DIM}Check console for deployment errors${NC}" +fi +echo "" + +# --- 2. Knowledge Base Status (RAG Engine) --- +echo -e "${BOLD}[2] Knowledge Base (RAG Engine)${NC}" +CORPUS_DISPLAY_NAME="osvauco-knowledge-base" + +# Note: gcloud ai rag-corpora might not be available in all SDK versions. +if gcloud ai rag-corpora --help &>/dev/null; then + RAG_DATA=$(gcloud ai rag-corpora list --project="${PROJECT_ID}" --format="value(name,location)" 2>/dev/null | tr -d '\r' | grep "${CORPUS_DISPLAY_NAME}" || true) + if [ -n "$RAG_DATA" ]; then + FULL_NAME=$(echo "$RAG_DATA" | awk '{print $1}') + RAG_LOC=$(echo "$RAG_DATA" | awk '{print $2}') + # Get count of files + FILE_COUNT=$(gcloud ai rag-files list --corpus="${FULL_NAME}" --project="${PROJECT_ID}" --location="${RAG_LOC}" --format="value(name)" 2>/dev/null | wc -l | tr -dc '0-9') + FILE_COUNT=${FILE_COUNT:-0} + echo -e " ${OK} Corpus: ${GREEN}FOUND${NC} in ${RAG_LOC}" + echo -e " ${DIM}Files: ${FILE_COUNT} documents indexed${NC}" + else + echo -e " ${WARN} Corpus: ${YELLOW}NOT FOUND${NC}" + echo -e " ${DIM}Action: python3 agents/rag/setup_corpus.py${NC}" + fi +else + # Fallback: Try a lightweight python check + echo -e " ${INFO} Corpus check: ${DIM}gcloud ai rag commands missing. Checking via SDK...${NC}" + RAG_CHECK=$(python3 -c " +import os +try: + from vertexai import rag + import vertexai + vertexai.init(project='${PROJECT_ID}') + # Check common regions + for loc in ['${REGION}', 'europe-west4']: + vertexai.init(project='${PROJECT_ID}', location=loc) + for c in rag.list_corpora(): + if c.display_name == '${CORPUS_DISPLAY_NAME}': + print(f'FOUND|{loc}|{c.name}') + exit(0) + print('MISSING') +except Exception: + print('ERROR') +" 2>/dev/null | tr -d '\r' || echo "ERROR") + + if [[ "$RAG_CHECK" == FOUND* ]]; then + LOC=$(echo "$RAG_CHECK" | cut -d'|' -f2) + echo -e " ${OK} Corpus: ${GREEN}FOUND${NC} in ${LOC}" + elif [ "$RAG_CHECK" = "MISSING" ]; then + echo -e " ${WARN} Corpus: ${YELLOW}NOT FOUND${NC}" + echo -e " ${DIM}Action: python3 agents/rag/setup_corpus.py${NC}" + else + echo -e " ${WARN} Corpus: ${DIM}Unable to verify (Vertex AI SDK issues)${NC}" + fi +fi +echo "" + +# --- 3. Resource & Cost Risk Audit --- +echo -e "${BOLD}[3] Resource & Cost Risk Audit${NC}" +# Resources that incur cost even when idle +VM_COUNT=$(gcloud compute instances list --project="${PROJECT_ID}" --filter="status=RUNNING" --format="value(name)" 2>/dev/null | wc -l | tr -dc '0-9') +SQL_COUNT=$(gcloud sql instances list --project="${PROJECT_ID}" --filter="state=RUNNABLE" --format="value(name)" 2>/dev/null | wc -l | tr -dc '0-9') +# Reasoning Engines (Memory Bank) are common cost leaks +RB_COUNT=$(gcloud ai reasoning-engines list --project="${PROJECT_ID}" --format="value(name)" 2>/dev/null | wc -l | tr -dc '0-9') + +# Initialize defaults +VM_COUNT=${VM_COUNT:-0} +SQL_COUNT=${SQL_COUNT:-0} +RB_COUNT=${RB_COUNT:-0} + +if [ "$VM_COUNT" -eq 0 ] && [ "$SQL_COUNT" -eq 0 ] && [ "$RB_COUNT" -eq 0 ]; then + echo -e " ${OK} No expensive idle resources detected." +else + if [ "$VM_COUNT" -gt 0 ]; then + echo -e " ${RED}${FAIL} Running VMs: ${VM_COUNT}${NC} (Always-on cost!)" + fi + if [ "$SQL_COUNT" -gt 0 ]; then + echo -e " ${RED}${FAIL} Running SQL: ${SQL_COUNT}${NC} (Always-on cost!)" + fi + if [ "$RB_COUNT" -gt 0 ]; then + echo -e " ${RED}${FAIL} Memory Bank: ${RB_COUNT}${NC} (Idle cost!)" + fi + echo -e " ${DIM}Tip: Run bash infrastructure/03-teardown.sh at end of day.${NC}" +fi +echo "" + +# --- 4. Security & Integrations --- +echo -e "${BOLD}[4] Security & Secrets${NC}" +SECRETS=("github-token" "webhook-url" "rag-corpus-name") +for s in "${SECRETS[@]}"; do + if gcloud secrets describe "$s" --project="${PROJECT_ID}" &>/dev/null; then + echo -e " ${OK} Secret: ${GREEN}${s}${NC}" + else + if [ "$s" = "rag-corpus-name" ]; then + echo -e " ${INFO} Secret: ${DIM}${s}${NC} (Auto-populated by setup_corpus.py)" + else + echo -e " ${WARN} Secret: ${YELLOW}${s}${NC} (MISSING)" + echo -e " ${DIM}Action: gcloud secrets create ${s} --data-file=...${NC}" + fi + fi +done +echo "" + +echo -e "${CYAN}${BOLD}==========================================================${NC}" +echo -e " ${BOLD}Next Step:${NC} Run ${DIM}bash scripts/osvauco-opax-boot.sh${NC} to start OPAX." +echo -e "${CYAN}${BOLD}==========================================================${NC}"