console-tunnel.bat
Rama main · réplica de lectura del repositorio original.
@echo off
chcp 65001 >nul
title QQ Agent 控制台 - SSH 隧道
setlocal enabledelayedexpansion
rem ===========================================================================
rem 一键打开 QQ Agent 控制台:建 SSH 隧道 + 自动免登录打开浏览器。
rem 不需要记服务器地址、不需要在服务器上开任何公网端口。
rem
rem 用法(在本文件所在目录):
rem console-tunnel.bat 首次运行会提示输入 用户@服务器,之后自动记住
rem console-tunnel.bat user@host 指定服务器并记住它
rem console-tunnel.bat test 只验证隧道与控制台连通,不开浏览器(供脚本调用)
rem console-tunnel.bat forget 忘记已记住的服务器地址
rem
rem 依赖:Windows 10+ 自带的 ssh 与 curl。
rem (ssh 若缺失:设置 → 应用 → 可选功能 → 添加“OpenSSH 客户端”)
rem 服务器侧默认端口:3210 控制台 / 5099 SnowLuma WebUI / 6081 QQ 扫码登录;
rem 可用环境变量 QQ_AGENT_CONSOLE_PORT / QQ_AGENT_WEBUI_PORT / QQ_AGENT_VNC_PORT 覆盖。
rem
rem 等价工具(本机已装 Node 时):node src/ops.js console --open
rem ===========================================================================
set "CFG=%~dp0.console-tunnel.cfg"
if "%QQ_AGENT_STACK_DIR%"=="" (set "STACK_DIR=/mnt/data/qq-agent") else (set "STACK_DIR=%QQ_AGENT_STACK_DIR%")
set "SSHOPTS=-o"BatchMode=yes" -o"ServerAliveInterval=30" -o"ServerAliveCountMax=3" -o"ConnectTimeout=15" -o"ExitOnForwardFailure=yes""
set "SSH_READ=-o"BatchMode=yes" -o"ConnectTimeout=10""
if "%QQ_AGENT_CONSOLE_PORT%"=="" (set "CONSOLE_PORT=3210") else (set "CONSOLE_PORT=%QQ_AGENT_CONSOLE_PORT%")
if "%QQ_AGENT_WEBUI_PORT%"=="" (set "WEBUI_PORT=5099") else (set "WEBUI_PORT=%QQ_AGENT_WEBUI_PORT%")
if "%QQ_AGENT_VNC_PORT%"=="" (set "VNC_PORT=6081") else (set "VNC_PORT=%QQ_AGENT_VNC_PORT%")
rem ── 解析参数、读取/记住服务器地址 ─────────────────────────────────────────
set "MODE=run"
set "SRV="
if /i "%~1"=="forget" goto forget
if /i "%~1"=="test" (
set "MODE=test"
if not "%~2"=="" set "SRV=%~2"
) else if not "%~1"=="" set "SRV=%~1"
if "%SRV%"=="" if exist "%CFG%" set /p SRV=<"%CFG%"
if "%SRV%"=="" (
echo 首次使用:请输入服务器地址(格式 user@host,例如 ubuntu@203.0.113.10)
set /p "SRV=服务器: "
)
if "%SRV%"=="" goto nohost
echo %SRV%>"%CFG%"
rem ── 认证方式:优先密钥;没有密钥就退回普通 SSH,让用户输密码 ─────────────
ssh %SSH_READ% %SRV% "exit 0" >nul 2>&1
if errorlevel 1 (
set "SSH_READ=-o"ConnectTimeout=10""
set "SSHOPTS=-o"ServerAliveInterval=30" -o"ServerAliveCountMax=3" -o"ConnectTimeout=15" -o"ExitOnForwardFailure=yes""
set "PASSWORD_MODE=1"
)
rem ── 从服务器读取控制台 Token,用于打开浏览器时免登录 ─────────────────────
set "TOKEN="
set "REMOTE_CMD=for f in %STACK_DIR%/data/console-access.txt /data/qq-agent/data/console-access.txt; do if [ -f $f ]; then awk '/^Token/{print $2; exit}' $f; break; fi; done"
rem 注意:ssh 的 -o 选项必须用「贴紧」写法(-o"K=V"),带空格的写法在 for /f 里会被拆坏。
rem 令牌可能含 !,这一段必须关掉 delayed expansion;用临时文件把值带回外层作用域。
set "TOKEN_FILE=%TEMP%\qq-agent-token-%RANDOM%-%RANDOM%.txt"
setlocal disabledelayedexpansion
for /f "usebackq delims=" %%t in (`ssh %SSH_READ% %SRV% "%REMOTE_CMD%"`) do >>"%TOKEN_FILE%" echo %%t
endlocal
if exist "%TOKEN_FILE%" set /p TOKEN=<"%TOKEN_FILE%"
del "%TOKEN_FILE%" >nul 2>&1
if "%TOKEN%"=="" (set "URL=http://127.0.0.1:%CONSOLE_PORT%/") else (set "URL=http://127.0.0.1:%CONSOLE_PORT%/?token=%TOKEN%")
where ssh >nul 2>&1 || goto nossh
echo ============================================================
echo QQ Agent 控制台(SSH 隧道到 %SRV%)
echo ============================================================
echo.
echo 正在建立 SSH 隧道(密钥登录不会提示;密码登录最多可能提示三次)...
echo.
echo QQ Agent 控制台 ....... http://127.0.0.1:%CONSOLE_PORT%
echo SnowLuma WebUI ........ http://127.0.0.1:%WEBUI_PORT%
echo QQ 远程桌面 / 扫码 .... http://127.0.0.1:%VNC_PORT%
echo.
rem ── 从服务器读 SnowLuma / noVNC 的登录凭据 ───────────────────────────────
rem 只给 URL 不给密码,打开 SnowLuma WebUI 会卡在登录页(那份凭证在服务器上的
rem deployment-access.txt 里,0600 权限;顺手取一次,省得用户再 ssh 去找)。
set "CRED_CMD=for f in %STACK_DIR%/deployment-access.txt /data/qq-agent/deployment-access.txt; do if [ -f $f ]; then grep -E '^(SnowLuma password|noVNC password): ' $f; break; fi; done"
rem ⚠️ 这一段临时关掉 delayed expansion:密码里可能带 ! ,开着会被 cmd 吃掉
rem (实测踩过:Sl-xxxx!Aa 打印成 Sl-xxxxAa,用户照抄就登不上)
setlocal disabledelayedexpansion
for /f "usebackq delims=" %%c in (`ssh %SSH_READ% %SRV% "%CRED_CMD%"`) do (
echo %%c
set "GOT_CRED=1"
)
if defined GOT_CRED echo (SnowLuma 登录账号固定 admin,只用密码;第二行是 QQ 扫码/远程桌面密码)
endlocal
echo.
echo 关闭本窗口 = 断开隧道;服务器上的机器人照常运行。
if "%TOKEN%"=="" (
echo.
echo 没自动读到控制台令牌,打开控制台后手动登录即可。令牌取法:
echo ssh %SRV% "cat /data/qq-agent/data/console-access.txt"
) else (
echo 打开控制台时将自动登录(令牌已通过 SSH 现场读取,不落盘)。
)
echo ============================================================
echo.
rem ── 启动隧道:正常运行挂在当前窗口;测试模式用独立窗口以便收尾清理 ──
if "%MODE%"=="test" (
start "QQ Agent Tunnel TEST" /min cmd /c ssh %SSHOPTS% -N -L %CONSOLE_PORT%:127.0.0.1:%CONSOLE_PORT% -L %WEBUI_PORT%:127.0.0.1:%WEBUI_PORT% -L %VNC_PORT%:127.0.0.1:%VNC_PORT% %SRV%
) else (
start /b ssh %SSHOPTS% -N -L %CONSOLE_PORT%:127.0.0.1:%CONSOLE_PORT% -L %WEBUI_PORT%:127.0.0.1:%WEBUI_PORT% -L %VNC_PORT%:127.0.0.1:%VNC_PORT% %SRV%
)
where curl >nul 2>&1
if errorlevel 1 goto nocurl
echo 等待控制台就绪(最多 45 秒)...
set /a tries=0
:waitloop
set /a tries+=1
curl -s -o nul --max-time 2 "http://127.0.0.1:%CONSOLE_PORT%/" >nul 2>&1
if not errorlevel 1 goto ready
if %tries% geq 45 goto stalled
%SystemRoot%\System32\timeout.exe /t 1 >nul
goto waitloop
:ready
if "%MODE%"=="test" (
taskkill /f /t /fi "WINDOWTITLE eq QQ Agent Tunnel TEST" >nul 2>&1
echo TEST_OK: 隧道与控制台均就绪(测试模式,未打开浏览器,隧道已清理)
exit /b 0
)
echo 隧道就绪(用了 %tries% 秒),正在打开控制台...
start "" "%URL%"
echo.
echo 控制台已打开。若浏览器没弹出,把下面这行整条粘进地址栏:
echo %URL%
echo.
pause >nul
exit /b 0
:nocurl
echo (本机没有 curl,无法验证控制台响应)
if "%MODE%"=="test" (
taskkill /f /t /fi "WINDOWTITLE eq QQ Agent Tunnel TEST" >nul 2>&1
echo TEST_FAIL: 本机没有 curl,无法验证隧道/控制台
exit /b 1
)
%SystemRoot%\System32\timeout.exe /t 8 >nul
start "" "%URL%"
echo 控制台已打开。若浏览器没弹出,把下面这行整条粘进地址栏:
echo %URL%
pause >nul
exit /b 0
:stalled
echo.
echo !!45 秒内没能连上控制台。
echo 可能原因:网络不通 / SSH 密钥失效 / 服务器地址不对 / 服务器上控制台没在跑。
echo 可在另一个窗口手动试:ssh %SRV%
if "%MODE%"=="test" (
taskkill /f /t /fi "WINDOWTITLE eq QQ Agent Tunnel TEST" >nul 2>&1
exit /b 1
)
pause >nul
exit /b 1
:nossh
echo 本机缺少 ssh 命令。请在“设置 → 应用 → 可选功能”里添加 OpenSSH 客户端后重试。
pause
exit /b 1
:nohost
echo 没有服务器地址,已退出。
echo 可以直接把地址作为参数:console-tunnel.bat user@host
pause
exit /b 1
:forget
if exist "%CFG%" del "%CFG%"
echo 已忘记记住的服务器地址(%CFG% 已删除)。
exit /b 0