#!/bin/zsh set -euo pipefail PUBLIC_BASE="${CODEX_PILOT_PUBLIC_BASE:-https://pilot.qiliuai.com}" PUBLIC_BASE="${PUBLIC_BASE%/}" WORK_ROOT="${TMPDIR:-/tmp}/codex-pilot-self-deploy" ZIP_PATH="$WORK_ROOT/codex-pilot-external-deploy.zip" EXTRACT_DIR="$WORK_ROOT/package" REPORT_DIR="$HOME/.codex-pilot" REPORT_PATH="$REPORT_DIR/self-deploy-report.txt" INSTALL_DIR="$HOME/.codex-pilot/mobile-control" PORT="${CODEX_DROPBOX_PORT:-8787}" step() { echo "[Codex Pilot] $1" } json_field() { /usr/bin/python3 - "$1" "$2" <<'PY' import json import sys from pathlib import Path data = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) value = data for part in sys.argv[2].split("."): value = value[part] print(value) PY } service_ready() { local token_file="$INSTALL_DIR/.token" [ -f "$token_file" ] || return 1 local token encoded token="$(tr -d '\n' < "$token_file")" encoded="$(/usr/bin/python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip(), safe=""))' <<< "$token")" for attempt in {1..20}; do if curl -fsS --max-time 3 "http://127.0.0.1:$PORT/api/bootstrap?token=$encoded" >/dev/null 2>&1; then return 0 fi sleep 1 done return 1 } mkdir -p "$WORK_ROOT" "$REPORT_DIR" rm -rf "$EXTRACT_DIR" mkdir -p "$EXTRACT_DIR" INFO_PATH="$WORK_ROOT/app-info.json" step "读取公开版本信息" curl -fsS --connect-timeout 15 --max-time 30 "$PUBLIC_BASE/app-info.json" -o "$INFO_PATH" VERSION="$(json_field "$INFO_PATH" "version_name")" DEPLOY_FILE="$(json_field "$INFO_PATH" "deploy_package.file")" EXPECTED_SHA="$(json_field "$INFO_PATH" "deploy_package.sha256" | tr '[:upper:]' '[:lower:]')" step "下载外部部署包 $VERSION" curl -fL --connect-timeout 15 --max-time 180 "$PUBLIC_BASE/$DEPLOY_FILE" -o "$ZIP_PATH" ACTUAL_SHA="$(shasum -a 256 "$ZIP_PATH" | awk '{print tolower($1)}')" if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then echo "部署包 SHA256 校验失败:期望 $EXPECTED_SHA,实际 $ACTUAL_SHA。" >&2 exit 3 fi step "解压部署包" /usr/bin/unzip -q "$ZIP_PATH" -d "$EXTRACT_DIR" PACKAGE_ROOT="$(find "$EXTRACT_DIR" -type f -name install-mac.command | head -1 | xargs dirname)" if [ -z "$PACKAGE_ROOT" ] || [ ! -f "$PACKAGE_ROOT/install-mac.command" ]; then echo "解压后没有找到 Codex Pilot 部署包根目录。" >&2 exit 4 fi chmod +x "$PACKAGE_ROOT/install-mac.command" "$PACKAGE_ROOT/verify-mac.command" "$PACKAGE_ROOT/uninstall-mac.command" 2>/dev/null || true step "执行 install-mac.command" "$PACKAGE_ROOT/install-mac.command" READY="未通过,请运行 verify-mac.command" if service_ready; then READY="本机健康检查通过" fi CONNECTION_INFO="$INSTALL_DIR/connection-info.txt" { echo "Codex Pilot AI 自部署报告" echo "版本:$VERSION" echo "系统:macOS" echo "部署脚本:install-mac.command" echo "部署包 SHA256:$ACTUAL_SHA" echo "服务状态:$READY" echo "连接信息文件:$CONNECTION_INFO" echo "扫码页面:http://127.0.0.1:$PORT/connect" echo echo "注意:报告不会写出访问口令。需要连接手机时,请在本机 connection-info.txt 查看 token 或使用扫码页面。" } > "$REPORT_PATH" chmod 600 "$REPORT_PATH" echo echo "Codex Pilot AI 自部署完成" echo "版本:$VERSION" echo "服务状态:$READY" echo "部署报告:$REPORT_PATH" echo "扫码页面:http://127.0.0.1:$PORT/connect" echo if [ "$READY" = "本机健康检查通过" ]; then open "http://127.0.0.1:$PORT/connect" >/dev/null 2>&1 || true fi