#!/usr/bin/env bash # DETA 产品插件的本机 MCP stdio 启动器(唯一入口,多产品共用) # # 两个宿主(Codex 插件 / dsh 组合包)都通过本脚本拉起同一个领域服务; # 产品差异只由 --profile 决定(工作区默认位置、工具面),宿主侧只声明"调哪个脚本、带哪个档位"。 # # 用法:deta-mcp.sh [--profile ] # # 依赖:node + 已构建的领域服务运行时 # # 环境变量: # DETA_SERVER_ROOT 领域服务运行时根目录(未设时自动查找,见下) # DETA_WORKSPACE AML 工作目录(引擎侧工作区真值;缺省 ~/.deta//workspace) # ANT_HEADLESS_WORKSPACE 同上(兼容旧名,DETA_WORKSPACE 优先) # DETA_TOKEN deta.city 门户令牌(可选;不注入则云类工具返回 auth_required) # ANT_WORKSPACE_BRIDGE_URL 网关基址(可选;与项目 ID 一起启用 Studio 执行器中继) # DETA_SURFACE 工具面覆盖(缺省 full) set -euo pipefail # ── 产品档位:宿主在声明里传 --profile;缺省 studio ── PROFILE="${DETA_PROFILE:-studio}" while [ $# -gt 0 ]; do case "$1" in --profile) PROFILE="${2:?--profile 需要一个值}"; shift 2 ;; *) break ;; esac done # ── 运行时解析:显式 DETA_SERVER_ROOT → 本机引擎包安装位置 → 本机开发检出(打印提示)── ROOT="${DETA_SERVER_ROOT:-}" if [ -z "$ROOT" ]; then for cand in "$HOME/.deta/runtime" "$HOME/Dev/ant/ant-copilot"; do if [ -f "$cand/dist/deta-mcp.mjs" ] || [ -f "$cand/dist/src/headless/mcp-server.js" ]; then ROOT="$cand"; break; fi done fi if ! command -v node >/dev/null 2>&1; then echo "[deta] 未找到 node,请先安装 Node.js" >&2 exit 1 fi # 入口优先取 esbuild 打包形态:tsc 产物在 Node ESM 下会因依赖包的目录 import 直接失败 ENTRY="" if [ -n "$ROOT" ]; then if [ -f "$ROOT/dist/deta-mcp.mjs" ]; then ENTRY="$ROOT/dist/deta-mcp.mjs" elif [ -f "$ROOT/dist/src/headless/mcp-server.js" ]; then ENTRY="$ROOT/dist/src/headless/mcp-server.js" echo "[deta] 警告:回落到未打包产物,请先在该运行时目录执行 npm run build:mcp" >&2 fi fi if [ -z "$ENTRY" ]; then echo "[deta] 未找到领域服务运行时(已尝试:DETA_SERVER_ROOT、\$HOME/.deta/runtime、\$HOME/Dev/ant/ant-copilot)" >&2 echo "[deta] 请安装本机引擎包,或设置 DETA_SERVER_ROOT 指向已构建的领域服务仓库" >&2 exit 1 fi if [ "$ROOT" = "$HOME/Dev/ant/ant-copilot" ]; then echo "[deta] 提示:使用本机开发检出 ${ROOT}(非打包运行时)" >&2 fi # ── 工作目录:宿主不会传项目目录(cwd 通常是插件根),按产品档位定稳定位置 ── WS="${DETA_WORKSPACE:-${ANT_HEADLESS_WORKSPACE:-$HOME/.deta/$PROFILE/workspace}}" mkdir -p "$WS" export ANT_HEADLESS_WORKSPACE="$WS" # ── 工具面:产品档位缺省全量面;DETA_SURFACE 可覆盖(如 engine)── export DETA_TOOL_SURFACE="${DETA_SURFACE:-full}" # ── 云令牌:显式环境变量 → ~/.deta/env → ~/.deta/credentials.json ── # 桌面应用(Finder/Dock 启动)不会继承终端的 export,也不是每个人都有 launchctl 注入, # 因此登录助手写下的凭据必须由启动器自己解析——这是"登录一次即可用"的关键路径。 TOKEN_SOURCE="" if [ -n "${DETA_TOKEN:-}" ]; then TOKEN_SOURCE="环境变量" fi if [ -z "${DETA_TOKEN:-}" ] && [ -f "$HOME/.deta/env" ]; then # shellcheck disable=SC1090 . "$HOME/.deta/env" >/dev/null 2>&1 || true if [ -n "${DETA_TOKEN:-}" ]; then TOKEN_SOURCE="$HOME/.deta/env"; fi fi if [ -z "${DETA_TOKEN:-}" ] && [ -f "$HOME/.deta/credentials.json" ]; then DETA_TOKEN="$(node -e "try{const c=JSON.parse(require('node:fs').readFileSync(process.env.HOME+'/.deta/credentials.json','utf8'));if(c&&typeof c.token==='string')process.stdout.write(c.token)}catch(e){}" 2>/dev/null || true)" export DETA_TOKEN if [ -n "${DETA_TOKEN:-}" ]; then TOKEN_SOURCE="$HOME/.deta/credentials.json"; fi fi if [ -n "${DETA_TOKEN:-}" ]; then CLOUD_STATE="已注入(来源=${TOKEN_SOURCE})" else CLOUD_STATE="未注入——云类工具将返回 auth_required:运行 bin/deta-login.mjs 登录后开新会话" fi echo "[deta] 档位=${PROFILE} 运行时=${ROOT} 工作区=${WS} 工具面=${DETA_TOOL_SURFACE} 云令牌=${CLOUD_STATE}" >&2 exec node "$ENTRY" "$@"