#!/bin/bash set -euo pipefail # ───────────────────────────────────────────────────────────── # Nodgate Linux Executor — 一键安装 # # 执行器不保存任何密钥;它只把「带占位符的请求模板」发给你的钥匙柜设备, # 钥匙柜审批后回一个一次性加密授权,执行器带着密钥完成这一次调用。 # # 安装钥匙柜是另一步:在手机上装 App(nodgate.aidancer.net/download/android)。 # # 用法: curl -fsSL https://nodgate.aidancer.net/install-executor.sh | bash # ───────────────────────────────────────────────────────────── RELAY_URL="${NODGATE_RELAY_URL:-https://nodgate.aidancer.net}" BIN_URL="$RELAY_URL/download/nodgated-linux" BIN_DIR="$HOME/.local/bin" UNIT_DIR="$HOME/.config/systemd/user" UNIT_NAME="nodgate-executor.service" # 守护进程监听 unix socket,不占端口、不对同机其他用户开放,SSRF 也够不着。 # 无需任何 agent 凭据:配对成功即代表这台机器被授权。 SOCK="$HOME/.nodgate/nodgated.sock" API="http://localhost" avcurl() { curl --unix-socket "$SOCK" "$@"; } say() { printf '%s\n' "$*"; } say "🔒 Nodgate 执行器安装" say "========================" # 0. 平台检查 if [ "$(uname -s)" != "Linux" ] || [ "$(uname -m)" != "x86_64" ]; then say "❌ 目前仅支持 Linux x86_64(当前:$(uname -s) $(uname -m))" >&2 exit 1 fi command -v systemctl >/dev/null 2>&1 || { say "❌ 需要 systemd(systemctl 未找到)" >&2; exit 1; } command -v curl >/dev/null 2>&1 || { say "❌ 需要 curl" >&2; exit 1; } # 1. 下载二进制 say "" say "[1/4] 下载执行器…" mkdir -p "$BIN_DIR" curl -fsSL "$BIN_URL" -o "$BIN_DIR/nodgated.tmp" chmod 755 "$BIN_DIR/nodgated.tmp" mv -f "$BIN_DIR/nodgated.tmp" "$BIN_DIR/nodgated" say " ✅ $BIN_DIR/nodgated" # 2. systemd 用户服务(执行器模式,不持密钥) say "" say "[2/4] 安装并启动服务…" mkdir -p "$UNIT_DIR" cat > "$UNIT_DIR/$UNIT_NAME" </dev/null | grep -q "Linger=yes"; then say " ⚠️ 注销后自动停止。开机常驻请执行: sudo loginctl enable-linger $USER" fi # 3. 等待就绪 say "" say "[3/4] 校验…" HEALTH="" for _ in $(seq 1 15); do sleep 1 HEALTH="$(avcurl -s "$API/api/health" || true)" printf '%s' "$HEALTH" | grep -q '"mode":"executor"' && break done if ! printf '%s' "$HEALTH" | grep -q '"mode":"executor"'; then say "❌ 执行器未就绪。日志: journalctl --user -u ${UNIT_NAME%.service} -f" >&2 exit 1 fi say " ✅ 执行器运行中:$SOCK" # 4. 发起配对,打印配对码 say "" say "[4/4] 生成配对码…" avcurl -fsS -X POST "$API/api/settings/remote" \ -H 'Content-Type: application/json' \ -d "{\"relay_url\":\"$RELAY_URL\"}" >/dev/null PAIR="$(avcurl -fsS -X POST "$API/api/settings/remote/pair" || true)" CODE="$(printf '%s' "$PAIR" | sed -n 's/.*"code":"\([^"]*\)".*/\1/p')" say "" say "======================================" if [ -n "$CODE" ]; then say "✅ 执行器已安装。配对码:" say "" say " 👉 $CODE" say "" say "在手机钥匙柜 App →「连接 / 执行器」→「配对执行器」输入上面的码,即可绑定。" say "(配对码约 10 分钟内有效;过期后重新执行: curl --unix-socket $SOCK -fsS -X POST $API/api/settings/remote/pair)" else say "✅ 执行器已安装,但自动生成配对码失败。手动生成:" say " curl --unix-socket $SOCK -fsS -X POST $API/api/settings/remote/pair" fi say "======================================" say "" # 接入只有 MCP 这一条路径;旧版那套 NODGATE_AGENT_ID / SECRET 已经随本机鉴权一起删掉了, # 别再往这儿加「二选一」。命令一律写绝对路径:MCP 客户端自己 spawn 子进程,读不到你 shell 的 PATH。 say "接入 Agent —— 把执行器配成 MCP server(选你用的客户端):" say "" say " OpenClaw: openclaw mcp add nodgate --command \"$BIN_DIR/nodgated\" --arg mcp" say " 然后必须 openclaw gateway restart —— 光写配置不重启,已开的会话拿不到工具" say " Claude Code: claude mcp add nodgate -- \"$BIN_DIR/nodgated\" mcp" say " 其他客户端: 在各自的 mcpServers 里加 {\"command\":\"$BIN_DIR/nodgated\",\"args\":[\"mcp\"]},然后重启客户端" say "" say "验证:在 agent 里看工具列表,出现 list_credentials / use_credential / poll_result 三个就通了。" say "" say "排错:stdio server 挂掉是静默的,给 mcp 加上 --log-file /tmp/nodgate-mcp.log 再看日志。" say "用法:$BIN_DIR/nodgated --help" say "" say "服务: systemctl --user status ${UNIT_NAME%.service}" say "日志: journalctl --user -u ${UNIT_NAME%.service} -f"