diff --git a/README.md b/README.md index dd44c9c..4fd2987 100644 --- a/README.md +++ b/README.md @@ -106,18 +106,56 @@ python3 /tmp/bolt_http_test.py # HTTP 端到端 - Windows(基础功能:`cmd /C`、`taskkill /T` 杀进程树;无信号优雅退出) ## 在其他设备上部署(最快方式) -单文件二进制已发布到 Gitea Release,配合一键脚本,目标设备(Linux amd64 + systemd)只需两条命令: +一键脚本支持 Linux(systemd)和 macOS(launchd),自动识别 amd64 / arm64;Apple Silicon 即使在 Rosetta 终端中也优先选择 arm64。安装需要 root 权限。 + +默认下载 `v0.1.0` 的 `bolt-<平台>-<架构>` 附件,例如 `bolt-linux-amd64`、`bolt-darwin-arm64`。对应附件需要事先发布到 Gitea Release;可通过 `BOLT_VERSION` 指定版本,或通过 `BOLT_DL_URL` / `--binary` 指定其他来源。 ```bash # 1. 下载并执行安装脚本(私有仓库下载附件需先 export 下载令牌) export BOLT_DL_TOKEN=你的Gitea令牌 -curl -fsSL https://git.jocay.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sh -s -- --http 0.0.0.0:8080 +curl -fsSL -H "Authorization: token $BOLT_DL_TOKEN" https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh -o install.sh +sudo BOLT_DL_TOKEN="$BOLT_DL_TOKEN" sh install.sh --http 127.0.0.1:8080 # 2. 完成(脚本自动:下载二进制 → 装到 /usr/local/bin → 写 /etc/bolt.env 与 # systemd 单元 → 开机自启 → 启动服务)。看状态: systemctl status bolt ``` +### macOS arm64(Apple Silicon) + +在 Mac 上运行上面的安装命令,脚本会下载 `bolt-darwin-arm64`,安装到 `/usr/local/bin/bolt`,并创建以 root 运行的 launchd 系统服务 `io.jocay.bolt`。 + +`v0.1.0` Release 提供 `bolt-darwin-arm64` 附件。也可以将该二进制下载或拷到 Mac 后安装: + +```bash +sudo sh deploy/install.sh --binary ./bolt-darwin-arm64 --http 127.0.0.1:8080 --token 你的令牌 +``` + +配置保存在 `/Library/Application Support/bolt/io.jocay.bolt.plist`,包含监听地址、认证令牌、命令规则和超时。启用开机自启时,脚本还会复制配置到 `/Library/LaunchDaemons/io.jocay.bolt.plist`。两份配置均仅 root 可读写;更改参数建议重新运行安装脚本,以保持一致。 + +`--no-enable` 只保留手动启动配置,取消本服务的开机配置,仍可立即启动;`--no-start` 本次不加载服务,已运行的本服务也会卸载,下次开机是否启动由 `--no-enable` 决定。两个参数可一起使用。 + +```bash +# 查看服务 / 重启已加载的服务 / 停止并卸载 +sudo launchctl print system/io.jocay.bolt +sudo launchctl kickstart -k system/io.jocay.bolt +sudo launchctl bootout system/io.jocay.bolt + +# 停止后或使用 --no-start 安装后,手动加载并启动 +sudo launchctl bootstrap system '/Library/Application Support/bolt/io.jocay.bolt.plist' + +# 查看日志 +sudo tail -f /var/log/bolt/stdout.log /var/log/bolt/stderr.log +``` + +macOS 服务的工作目录为 `/var/root`,PATH 包含 `/opt/homebrew/bin` 和 `/opt/homebrew/sbin`,不会加载个人 shell 配置。认证下载使用 `BOLT_DL_TOKEN`,HTTP 服务认证使用 `--token`,两者独立。 + +该附件交叉编译为 `aarch64-apple-darwin`,最低目标系统为 macOS 11.0,未进行 Mac 实机运行验证。SHA-256: + +```text +39274c7d1f965ae924ee51d4e9e4d8a260a84d083971cd50dc6ce645245cc4b8 bolt-darwin-arm64 +``` + 常用安装参数(全部可选): | 参数 | 含义 | diff --git a/deploy/install.sh b/deploy/install.sh index 83c3ad4..f3a50fe 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -1,10 +1,10 @@ #!/bin/sh -# bolt 一键安装脚本(POSIX sh,需 root + systemd) +# bolt 一键安装脚本(POSIX sh,需 root; Linux 使用 systemd,macOS 使用 launchd) # # 用法(在目标设备上): # 方式 A - 从 Gitea Release 下载二进制并安装: -# curl -fsSL https://git.jocay.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sh -s -- --http 0.0.0.0:8080 --token 你的令牌 -# (私有仓库下载附件需要认证: 脚本会用 --token 同时作为下载凭据;或先 export BOLT_DL_TOKEN=xxx) +# curl -fsSL https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sh -s -- --http 0.0.0.0:8080 --token 你的令牌 +# (私有仓库下载附件需要认证:先 export BOLT_DL_TOKEN=xxx,与 --token 分开配置) # 方式 B - 已把二进制拷到目标设备(如 scp): # ./install.sh --binary ./bolt --http 0.0.0.0:8080 --token xxx --allow "git,docker" # @@ -17,7 +17,7 @@ # --binary 二进制来源:本地路径 或 http(s) URL(默认从 Gitea Release 下载) # --no-start 只安装不启动 # --no-enable 不设置开机自启 -set -u +set -eu HTTP_ADDR="${HTTP_ADDR:-127.0.0.1:8080}" TOKEN="${BOLT_TOKEN:-}" @@ -45,13 +45,36 @@ done BIN_PATH=/usr/local/bin/bolt ENV_FILE=/etc/bolt.env UNIT_FILE=/etc/systemd/system/bolt.service -DL_URL="${BOLT_DL_URL:-https://git.jocay.qzz.io/jocay/bolt/releases/download/v0.1.0/bolt-linux-amd64}" say() { printf '\033[1;32m[bolt]\033[0m %s\n' "$*"; } die() { printf '\033[1;31m[bolt] 错误:\033[0m %s\n' "$*"; exit 1; } [ "$(id -u)" -eq 0 ] || die "需要 root 权限" -command -v systemctl >/dev/null 2>&1 || die "未检测到 systemd" +OS=$(uname -s) +ARCH=$(uname -m) +case "$OS" in + Linux) + command -v systemctl >/dev/null 2>&1 || die "未检测到 systemd" + PLATFORM=linux + ;; + Darwin) + command -v launchctl >/dev/null 2>&1 || die "未检测到 launchd" + PLATFORM=darwin + # Rosetta 下 uname 可能返回 x86_64,优先识别实际的 Apple Silicon 硬件。 + if [ "$(sysctl -n hw.optional.arm64 2>/dev/null || true)" = 1 ]; then + ARCH=arm64 + fi + ;; + *) die "不支持的操作系统: $OS" ;; +esac +case "$ARCH" in + x86_64|amd64) ARCH=amd64 ;; + aarch64|arm64) ARCH=arm64 ;; + *) die "不支持的架构: $ARCH" ;; +esac +DL_URL="${BOLT_DL_URL:-https://git.draws.qzz.io/jocay/bolt/releases/download/${BOLT_VERSION:-v0.1.0}/bolt-$PLATFORM-$ARCH}" +say "目标平台: $PLATFORM/$ARCH" +mkdir -p /usr/local/bin # ---------- 1. 获取二进制 ---------- if [ -n "$BINARY_SRC" ]; then @@ -81,9 +104,9 @@ else rm -f /tmp/bolt-dl.$$ if command -v curl >/dev/null 2>&1; then if [ -n "${BOLT_DL_TOKEN:-}" ]; then - curl -fsSL --retry 3 -H "Authorization:token ${BOLT_DL_TOKEN:-}" -o /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败(私有仓库需 export BOLT_DL_TOKEN 或 --token)" + curl -fsSL --retry 3 -H "Authorization:token ${BOLT_DL_TOKEN:-}" -o /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败,请确认 Release 附件存在且 BOLT_DL_TOKEN 有效" else - curl -fsSL --retry 3 -o /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败(私有仓库需 export BOLT_DL_TOKEN 或 --token)" + curl -fsSL --retry 3 -o /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败,请确认 Release 附件存在;私有仓库需设置 BOLT_DL_TOKEN" fi elif command -v wget >/dev/null 2>&1; then wget -qO /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败" @@ -95,9 +118,79 @@ fi "$BIN_PATH" --help >/dev/null 2>&1 || die "二进制不可执行: $BIN_PATH" say "二进制就绪: $BIN_PATH ($("$BIN_PATH" --help | head -1))" +# macOS 将环境配置直接写入 plist,不通过 shell 解释令牌或命令规则。 +if [ "$OS" = Darwin ]; then + LABEL=io.jocay.bolt + PLIST_DIR="/Library/Application Support/bolt" + PLIST_FILE="$PLIST_DIR/$LABEL.plist" + AUTO_PLIST="/Library/LaunchDaemons/$LABEL.plist" + xml_escape() { + printf '%s' "$1" | sed 's/&/\&/g; s//\>/g; s/"/\"/g; s/'"'"'/\'/g' + } + mkdir -p "$PLIST_DIR" /Library/LaunchDaemons /var/log/bolt + chmod 700 "$PLIST_DIR" /var/log/bolt + # 令牌所在配置仅供 root 读取。 + umask 077 + say "写入 launchd 配置: $PLIST_FILE" + cat > "$PLIST_FILE" < + + + + Label$LABEL + ProgramArguments + + $BIN_PATH + --http$(xml_escape "$HTTP_ADDR") + + EnvironmentVariables + + PATH/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + BOLT_TOKEN$(xml_escape "$TOKEN") + BOLT_ALLOW$(xml_escape "$ALLOW") + BOLT_DENY$(xml_escape "$DENY") + BOLT_TIMEOUT_MS$(xml_escape "$TIMEOUT_MS") + + WorkingDirectory/var/root + RunAtLoad + KeepAliveSuccessfulExit + ThrottleInterval2 + ExitTimeOut10 + StandardOutPath/var/log/bolt/stdout.log + StandardErrorPath/var/log/bolt/stderr.log + + +PLISTEOF + chown root:wheel "$PLIST_FILE" + chmod 600 "$PLIST_FILE" + if [ "$NO_ENABLE" -eq 0 ]; then + cp "$PLIST_FILE" "$AUTO_PLIST" + chown root:wheel "$AUTO_PLIST" + chmod 600 "$AUTO_PLIST" + say "已设置开机自启" + else + # 仅移除本脚本管理的开机配置,保留手动启动配置。 + rm -f "$AUTO_PLIST" + fi + launchctl enable "system/$LABEL" + if launchctl print "system/$LABEL" >/dev/null 2>&1; then + launchctl bootout "system/$LABEL" + fi + if [ "$NO_START" -eq 0 ]; then + launchctl bootstrap system "$PLIST_FILE" || die "服务注册失败" + say "已提交服务启动: http://$HTTP_ADDR/mcp" + else + say "已安装(未启动),执行: sudo launchctl bootstrap system '$PLIST_FILE'" + fi + say "管理: sudo launchctl print system/$LABEL | sudo launchctl kickstart -k system/$LABEL" + say "日志: /var/log/bolt/stdout.log 与 /var/log/bolt/stderr.log" + exit 0 +fi + # ---------- 2. 写入环境配置 ---------- say "写入配置: $ENV_FILE" printf '# bolt 服务配置,修改后 systemctl restart bolt\n' > "$ENV_FILE" +printf 'BOLT_TIMEOUT_MS=%s\n' "$TIMEOUT_MS" >> "$ENV_FILE" if [ -n "$TOKEN" ]; then printf 'BOLT_TOKEN=%s\n' "$TOKEN" >> "$ENV_FILE" fi @@ -144,8 +237,8 @@ if [ "$NO_START" -eq 0 ]; then sleep 1 systemctl is-active bolt >/dev/null 2>&1 || { systemctl status bolt --no-pager -l | tail -15; die "服务启动失败"; } say "服务运行中: http://$HTTP_ADDR/mcp $(systemctl show -p MainPID --value bolt)" - [ -n "$TOKEN" ] && say "认证: Authorization: Bearer $TOKEN" + [ -z "$TOKEN" ] || say "已启用 Bearer 认证" else say "已安装(未启动),执行 systemctl start bolt 启动" fi -say "完成!管理: systemctl status bolt | journalctl -u bolt -f | systemctl restart bolt" \ No newline at end of file +say "完成!管理: systemctl status bolt | journalctl -u bolt -f | systemctl restart bolt"