feat: add Shipyard Neo quick-start script
Add scripts/start-with-neo.sh: one-click launcher that auto-generates Bay config.yaml (anonymous mode, host_port), pulls Ship image, starts Bay (port 8114) with health check, then starts AstrBot in foreground. Ctrl+C stops both services. Supports BAY_PORT env var override.
This commit is contained in:
Executable
+249
@@ -0,0 +1,249 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# start-with-neo.sh — 一键启动 Shipyard Neo Bay + AstrBot
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/start-with-neo.sh # 默认 Bay :8114
|
||||||
|
# BAY_PORT=9000 bash scripts/start-with-neo.sh # 自定义端口
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── 路径 ──────────────────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ASTRBOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
# shipyard-neo mono-repo root is one level above AstrBot
|
||||||
|
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=""
|
||||||
|
|
||||||
|
# ── 颜色 ──────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Check Docker access (try without sudo first, then with sudo)
|
||||||
|
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
|
||||||
|
|
||||||
|
# Check Bay venv
|
||||||
|
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."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 生成 Bay config.yaml(如不存在)────────────────────────────
|
||||||
|
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'
|
||||||
|
# Bay Local Development Config (auto-generated by start-with-neo.sh)
|
||||||
|
# For full reference see config.yaml.example
|
||||||
|
|
||||||
|
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:
|
||||||
|
api_key: null
|
||||||
|
allow_anonymous: true
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 拉取 Ship 镜像 ───────────────────────────────────────────
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动 Bay ──────────────────────────────────────────────────
|
||||||
|
start_bay() {
|
||||||
|
log "Starting Bay on :$BAY_PORT ..."
|
||||||
|
|
||||||
|
(cd "$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..."
|
||||||
|
|
||||||
|
# Wait for Bay to become healthy
|
||||||
|
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
|
||||||
|
# Check if process is still alive
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 打印 AstrBot 配置提示 ────────────────────────────────────
|
||||||
|
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 ""
|
||||||
|
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}"
|
||||||
|
echo -e " • Shipyard Neo Access Token → ${YELLOW}(留空,已开启匿名访问)${NC}"
|
||||||
|
echo -e " • Shipyard Neo Profile → ${YELLOW}python-default${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动 AstrBot ──────────────────────────────────────────────
|
||||||
|
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
|
||||||
|
print_astrbot_config_hint
|
||||||
|
start_astrbot
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user