| #!/usr/bin/env bash |
| |
| |
| |
| |
| |
| |
| |
| set -euo pipefail |
|
|
| |
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| ASTRBOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" |
| |
| NEO_ROOT="$(cd "$ASTRBOT_DIR/.." && pwd)" |
| BAY_DIR="$NEO_ROOT/pkgs/bay" |
|
|
| BAY_PORT="${BAY_PORT:-8114}" |
| BAY_HOST="0.0.0.0" |
| BAY_PID="" |
| BAY_API_KEY="" |
|
|
| |
| RED='\033[0;31m' |
| GREEN='\033[0;32m' |
| YELLOW='\033[1;33m' |
| CYAN='\033[0;36m' |
| NC='\033[0m' |
|
|
| log() { echo -e "${CYAN}[neo]${NC} $*"; } |
| ok() { echo -e "${GREEN}[neo]${NC} $*"; } |
| warn() { echo -e "${YELLOW}[neo]${NC} $*"; } |
| err() { echo -e "${RED}[neo]${NC} $*" >&2; } |
|
|
| |
| cleanup() { |
| log "Shutting down..." |
| if [[ -n "$BAY_PID" ]] && kill -0 "$BAY_PID" 2>/dev/null; then |
| log "Stopping Bay (PID $BAY_PID)..." |
| kill "$BAY_PID" 2>/dev/null || true |
| wait "$BAY_PID" 2>/dev/null || true |
| fi |
| ok "All services stopped." |
| } |
| trap cleanup EXIT INT TERM |
|
|
| |
| check_prerequisites() { |
| log "Checking prerequisites..." |
|
|
| if [[ ! -d "$BAY_DIR" ]]; then |
| err "Bay directory not found: $BAY_DIR" |
| err "Expected shipyard-neo mono-repo at: $NEO_ROOT" |
| exit 1 |
| fi |
|
|
| if ! command -v uv &>/dev/null; then |
| err "'uv' is not installed. Please install it first." |
| exit 1 |
| fi |
|
|
| |
| if docker info &>/dev/null 2>&1; then |
| ok "Docker is accessible." |
| elif sudo docker info &>/dev/null 2>&1; then |
| warn "Docker requires sudo. Bay may need socket permissions." |
| warn "If Bay fails to connect to Docker, run: sudo chmod 666 /var/run/docker.sock" |
| else |
| err "Docker is not accessible. Please install Docker or fix permissions." |
| exit 1 |
| fi |
|
|
| |
| if [[ ! -d "$BAY_DIR/.venv" ]]; then |
| log "Bay venv not found. Running 'uv sync' in $BAY_DIR ..." |
| (cd "$BAY_DIR" && uv sync) |
| fi |
|
|
| ok "Prerequisites OK." |
| } |
|
|
| |
| ensure_bay_config() { |
| local config_file="$BAY_DIR/config.yaml" |
|
|
| if [[ -f "$config_file" ]]; then |
| ok "Bay config.yaml already exists." |
| return |
| fi |
|
|
| log "Generating Bay config.yaml for local development..." |
|
|
| cat > "$config_file" << 'BAYCONFIG' |
| |
| |
|
|
| server: |
| host: "0.0.0.0" |
| port: 8114 |
|
|
| database: |
| url: "sqlite+aiosqlite:///./bay.db" |
| echo: false |
|
|
| driver: |
| type: docker |
| image_pull_policy: if_not_present |
| docker: |
| socket: "unix:///var/run/docker.sock" |
| connect_mode: host_port |
| host_address: "127.0.0.1" |
| publish_ports: true |
| host_port: null |
| network: null |
|
|
| cargo: |
| root_path: "/var/lib/bay/cargos" |
| default_size_limit_mb: 1024 |
| mount_path: "/workspace" |
|
|
| |
| |
| security: |
| allow_anonymous: false |
|
|
| profiles: |
| - id: python-default |
| description: "Standard Python sandbox" |
| image: "ghcr.io/astrbotdevs/shipyard-neo-ship:latest" |
| runtime_type: ship |
| runtime_port: 8123 |
| resources: |
| cpus: 1.0 |
| memory: "1g" |
| capabilities: |
| - filesystem |
| - shell |
| - python |
| idle_timeout: 1800 |
| env: {} |
|
|
| gc: |
| enabled: true |
| run_on_startup: true |
| interval_seconds: 300 |
| idle_session: |
| enabled: true |
| expired_sandbox: |
| enabled: true |
| orphan_cargo: |
| enabled: true |
| orphan_container: |
| enabled: false |
| BAYCONFIG |
|
|
| ok "Bay config.yaml created at $config_file" |
| } |
|
|
| |
| ensure_ship_image() { |
| local image="ghcr.io/astrbotdevs/shipyard-neo-ship:latest" |
| log "Checking Ship image: $image ..." |
|
|
| if docker image inspect "$image" &>/dev/null 2>&1 || \ |
| sudo docker image inspect "$image" &>/dev/null 2>&1; then |
| ok "Ship image is available locally." |
| else |
| log "Pulling Ship image (this may take a while)..." |
| if docker pull "$image" 2>/dev/null || sudo docker pull "$image" 2>/dev/null; then |
| ok "Ship image pulled successfully." |
| else |
| warn "Failed to pull Ship image. Bay will try to pull it on first sandbox creation." |
| fi |
| fi |
| } |
|
|
| |
| start_bay() { |
| log "Starting Bay on :$BAY_PORT ..." |
|
|
| (cd "$BAY_DIR" && BAY_DATA_DIR="$BAY_DIR" uv run uvicorn app.main:app \ |
| --host "$BAY_HOST" \ |
| --port "$BAY_PORT" \ |
| --reload \ |
| 2>&1 | sed "s/^/ ${CYAN}[bay]${NC} /") & |
| BAY_PID=$! |
|
|
| log "Bay started (PID $BAY_PID), waiting for health check..." |
|
|
| |
| local max_wait=30 |
| local waited=0 |
| while [[ $waited -lt $max_wait ]]; do |
| if curl -sf "http://127.0.0.1:$BAY_PORT/health" &>/dev/null; then |
| ok "Bay is healthy at http://127.0.0.1:$BAY_PORT" |
| return |
| fi |
| |
| if ! kill -0 "$BAY_PID" 2>/dev/null; then |
| err "Bay process died unexpectedly. Check the output above." |
| exit 1 |
| fi |
| sleep 1 |
| waited=$((waited + 1)) |
| done |
|
|
| err "Bay did not become healthy within ${max_wait}s." |
| err "It may still be starting โ check http://127.0.0.1:$BAY_PORT/health" |
| } |
|
|
| |
| read_bay_credentials() { |
| local cred_file="$BAY_DIR/credentials.json" |
|
|
| |
| local max_wait=5 |
| local waited=0 |
| while [[ $waited -lt $max_wait ]]; do |
| if [[ -f "$cred_file" ]]; then |
| break |
| fi |
| sleep 1 |
| waited=$((waited + 1)) |
| done |
|
|
| if [[ -f "$cred_file" ]]; then |
| |
| BAY_API_KEY=$(python3 -c " |
| import json, sys |
| try: |
| d = json.load(open('$cred_file')) |
| print(d.get('api_key', '')) |
| except Exception: |
| print('') |
| " 2>/dev/null || echo "") |
|
|
| if [[ -n "$BAY_API_KEY" ]]; then |
| ok "Auto-provisioned API key loaded from credentials.json" |
| else |
| warn "credentials.json found but api_key is empty" |
| fi |
| else |
| warn "credentials.json not found โ Bay may be using an existing key or anonymous mode" |
| warn "Check Bay logs above for the API key, or look at: $cred_file" |
| fi |
| } |
|
|
| |
| print_astrbot_config_hint() { |
| echo "" |
| echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}" |
| echo -e "${GREEN} Shipyard Neo Bay is running at http://127.0.0.1:$BAY_PORT ${NC}" |
| echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}" |
| echo "" |
| if [[ -n "$BAY_API_KEY" ]]; then |
| echo -e " ${CYAN}Bay API Key (auto-generated):${NC}" |
| echo -e " ${YELLOW}$BAY_API_KEY${NC}" |
| echo "" |
| fi |
| echo -e " ${CYAN}AstrBot Dashboard ้
็ฝฎๆๅผ๏ผ${NC}" |
| echo -e " 1. AI ้
็ฝฎ โ Agent Computer Use" |
| echo -e " โข Computer Use Runtime โ ${YELLOW}ๆฒ็ฎฑ${NC}" |
| echo -e " โข ๆฒ็ฎฑ็ฏๅข้ฉฑๅจๅจ โ ${YELLOW}Shipyard Neo${NC}" |
| echo -e " โข Shipyard Neo API Endpoint โ ${YELLOW}http://127.0.0.1:$BAY_PORT${NC}" |
| if [[ -n "$BAY_API_KEY" ]]; then |
| echo -e " โข Shipyard Neo Access Token โ ${YELLOW}$BAY_API_KEY${NC}" |
| else |
| echo -e " โข Shipyard Neo Access Token โ ${YELLOW}๏ผๆฅ็ Bay ๆฅๅฟ่ทๅ key๏ผ${NC}" |
| fi |
| echo -e " โข Shipyard Neo Profile โ ${YELLOW}python-default${NC}" |
| echo "" |
| } |
|
|
| |
| start_astrbot() { |
| log "Starting AstrBot..." |
| cd "$ASTRBOT_DIR" |
| uv run main.py |
| } |
|
|
| |
| main() { |
| echo "" |
| echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}" |
| echo -e "${CYAN}โ Shipyard Neo + AstrBot Quick Start โ${NC}" |
| echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}" |
| echo "" |
|
|
| check_prerequisites |
| ensure_bay_config |
| ensure_ship_image |
| start_bay |
| read_bay_credentials |
| print_astrbot_config_hint |
| start_astrbot |
| } |
|
|
| main "$@" |
|
|