A remote debugging SDK for WebView applications that cannot use Chrome/Safari DevTools.
┌─────────────────┐ WebSocket ┌─────────────────┐ WebSocket ┌─────────────────┐
│ WebView App │ ◄───────────────► │ Server │ ◄───────────────► │ Web Console │
│ (Client SDK) │ │ (Node.js) │ │ (React) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
- Client SDK: Intercepts console output, supports remote eval execution
- Server: WebSocket relay server with session management
- Web Console: Real-time log viewing and remote code execution
npm installnpm run build# Terminal 1: Start server
npm run dev:server
# Terminal 2: Start web console
npm run dev:console- Server:
ws://localhost:3000 - Web Console:
http://localhost:8080
部署分为两个部分:
- 服务部署 - 使用 Docker 运行服务
- 公网访问 - 选择 Nginx 或 Cloudflare Tunnel 暴露服务
# 构建并启动
docker-compose -f docker-compose.prod.yml up -d --build
# 查看日志
docker-compose -f docker-compose.prod.yml logs -f
# 停止服务
docker-compose -f docker-compose.prod.yml down服务端口:
- Server:
localhost:3003 - Web Console:
localhost:8082
选择以下任一方式将服务暴露到公网:
优点:无需公网 IP、自动 HTTPS、无需配置 Nginx
# 安装 cloudflared
# macOS
brew install cloudflared
# Linux
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
chmod +x cloudflared && sudo mv cloudflared /usr/local/bin/
# 一键暴露服务(会生成临时域名)
cloudflared tunnel --url http://localhost:3003
# 输出类似: https://xxx-xxx-xxx.trycloudflare.com
# 另开终端,暴露 Web Console
cloudflared tunnel --url http://localhost:8082# 登录
cloudflared tunnel login
# 创建隧道
cloudflared tunnel create remote-console
# 创建配置 ~/.cloudflared/config.yml
tunnel: <TUNNEL_ID>
credentials-file: ~/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: console.your-domain.com
service: http://localhost:8082
- hostname: ws.your-domain.com
service: http://localhost:3003
- service: http_status:404
# 添加 DNS 记录
cloudflared tunnel route dns remote-console console.your-domain.com
cloudflared tunnel route dns remote-console ws.your-domain.com
# 启动隧道
cloudflared tunnel run remote-console适用于有公网 IP 的服务器。
# Web Console
location /remote-console/ {
proxy_pass http://127.0.0.1:8082/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# WebSocket
location /remote-console/ws {
proxy_pass http://127.0.0.1:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# SDK 静态文件
location /remote-console/sdk/ {
alias /path/to/remote-console/sdk/;
add_header Access-Control-Allow-Origin *;
}nginx -t && systemctl reload nginx根据你选择的公网访问方式,配置 SDK 连接地址。
如果你沿用本文档的 nginx 方案,实际 URL 结构应为:
- SDK UMD(推荐兼容版):
https://your-domain.com/remote-console/sdk/remote-console.legacy.umd.js - 条件加载器:
https://your-domain.com/remote-console/sdk/conditional-loader.js - WebSocket:
wss://your-domain.com/remote-console/ws
<script src="https://your-domain.com/remote-console/sdk/remote-console.legacy.umd.js"></script>
<script>
RemoteConsole.init({
serverUrl: 'wss://your-domain.com/remote-console/ws',
name: 'My App'
});
</script>Remote Console 现在内置一层鉴权:Web Console(人类) 必须凭 loginserver 的账号登录,且邮箱在白名单中;MCP / 自动化 凭 API Token;SDK 上报日志不需要鉴权(自用场景)。
浏览器 (cookie: rc_token) ─┐
MCP / CI (rc-token.<TOKEN>) ─┼── nginx ─── remote-console-server ─── HTTPS ──> loginserver /api/auth/verify
设备端 SDK (无凭据) ─┘ │
└── 本地 JSON 白名单 + API tokens
- 登录:浏览器 →
POST /api/auth/login→ 服务端转发到 loginserver/api/auth/login→ 校验白名单 → 写 HttpOnly Cookie。 - WS 鉴权:
upgrade握手时读取 cookie 或子协议rc-token.<TOKEN>,未通过的连接只能注册为clientType: 'sdk'。 - 白名单:完全独立于 loginserver,由 admin 在 Web Console 「Admin」页面增删;磁盘存于
/data/auth.json(容器内)。 - API Token:每个白名单用户可在 Web Console 「Tokens」页面自助生成;明文只显示一次,存储为 SHA-256 哈希。
docker-compose.prod.yml 已加好持久化与环境变量。在仓库根目录用 .env 覆盖:
# .env (与 docker-compose.prod.yml 同目录)
ADMIN_EMAILS=you@example.com,partner@example.com
LOGIN_SERVER_URL=http://host.docker.internal:5001 # 同机 docker,loginserver 暴露 5001
COOKIE_SECURE=auto # auto / always / never; nginx 终止 TLS 时一般 auto 即可启动:
mkdir -p ./data/auth
docker-compose -f docker-compose.prod.yml up -d --build启动日志会打印 [auth] seeded admin emails: ...。这些邮箱会被添加到白名单且 role=admin,但密码仍然在 loginserver 那边(用 shinjiyu/loginserver 提供的注册页面提前注册即可)。
在原有 /remote-console/ 与 /remote-console/ws 的基础上,新增一行 /remote-console/api/:
location /remote-console/ {
proxy_pass http://127.0.0.1:8082/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 新增:HTTP API(cookie 同源生效)
location /remote-console/api/ {
proxy_pass http://127.0.0.1:3003/api/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /remote-console/ws {
proxy_pass http://127.0.0.1:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}为 Cursor 配置 MCP 时增加 RC_API_TOKEN(在 Web Console「Tokens」页面生成):
未配 RC_API_TOKEN 或 token 已被吊销时,MCP 端会收到 4401 关闭码并打印「token rejected; not reconnecting」。
SDK 行为完全不变;不需要 token。不要把 MCP 用的 rct_... 暴露到任何 SDK 端代码或 HTML 中。
如果你的另一个项目是浏览器页面、H5、WebView 容器,最简单的接法就是直接引线上 兼容性更好的 legacy UMD:
<script src="https://your-domain.com/remote-console/sdk/remote-console.legacy.umd.js"></script>
<script>
RemoteConsole.init({
// SDK 不需要登录,也不需要 token
serverUrl: 'wss://your-domain.com/remote-console/ws',
name: 'My Other Project'
});
</script>说明:
- SDK 角色是匿名
sdk,不走 loginserver,不需要白名单,不要塞任何 token - 真正需要登录的是 Web Console(人类)和 MCP(自动化)
name建议填项目名 / 页面名,方便在 Web Console 里区分sessionId可不填;SDK 会自动生成,并额外持久化一个deviceId到localStorage
如果你只想在测试环境开启,优先用下面的 conditional-loader.js 方案。
希望 只在测试/联调环境 加载 Remote Console,生产环境不加载、不连接。使用条件加载器即可,同一套 HTML/构建产物即可区分环境:
<!-- 使用条件加载器:仅当为测试环境时才加载并初始化 SDK -->
<script src="https://your-domain.com/remote-console/sdk/conditional-loader.js"
data-remote-console-src="https://your-domain.com/remote-console/sdk/remote-console.legacy.umd.js"
data-server-url="wss://your-domain.com/remote-console/ws"
data-name="My App"></script>测试环境判定(满足其一即加载):
window.REMOTE_CONSOLE_ENABLED === true(页面内可主动开启)- 当前 host 匹配:
localhost、127.0.0.1、含test-s.、alpha-s.、.test.、-test.、fproject
构建脚本会随 npm run build:sdk 将 conditional-loader.js、remote-console.legacy.umd.js 等产物输出到 client-sdk/dist/,部署到 CDN 或静态目录即可。
<script src="https://your-domain.com/remote-console/sdk/remote-console.legacy.umd.js"></script>
<script>
RemoteConsole.init({
serverUrl: 'wss://your-domain.com/remote-console/ws',
name: 'My App', // Optional: display name
sessionId: 'my-session', // Optional: custom session ID
autoConnect: true // Optional: auto-connect on init
});
</script>import { RemoteConsole } from 'remote-console-sdk';
RemoteConsole.init({
serverUrl: 'wss://your-domain.com/remote-console/ws',
name: 'My App'
});RemoteConsole.init(config); // Initialize
RemoteConsole.connect(); // Manual connect
RemoteConsole.disconnect(); // Disconnect
RemoteConsole.getSessionId(); // Get session ID
RemoteConsole.isConnected(); // Check status
RemoteConsole.destroy(); // Cleanup| Option | Type | Default | Description |
|---|---|---|---|
serverUrl |
string | required | WebSocket server URL |
sessionId |
string | auto-generated | Custom session identifier |
name |
string | - | Display name for the session |
autoConnect |
boolean | true | Auto-connect on initialization |
reconnectInterval |
number | 3000 | Reconnection interval (ms) |
maxReconnectAttempts |
number | 10 | Max reconnection attempts |
heartbeatInterval |
number | 30000 | Heartbeat interval (ms) |
如果你的运行环境已经确定是现代浏览器 / 新版 WebView,也可以继续用 remote-console.umd.js;但默认建议优先接 remote-console.legacy.umd.js,尤其是 Android 老 WebView、内嵌浏览器、历史业务壳环境。
remote_console/
├── client-sdk/ # Browser SDK
│ ├── src/
│ │ ├── index.ts
│ │ ├── console-interceptor.ts
│ │ ├── eval-executor.ts
│ │ └── websocket-client.ts
│ ├── loader/
│ │ └── conditional-loader.js # 仅测试环境加载 SDK,build 时拷贝到 dist/
│ └── package.json
├── server/ # Node.js WebSocket server
│ ├── src/
│ │ ├── index.ts
│ │ ├── session-manager.ts
│ │ └── message-handler.ts
│ ├── Dockerfile
│ └── package.json
├── web-console/ # React debugging interface
│ ├── src/
│ │ ├── App.tsx
│ │ └── components/
│ ├── Dockerfile
│ ├── nginx.conf
│ └── package.json
├── docker-compose.yml # Local development
├── docker-compose.prod.yml # Production deployment
└── package.json
npm run build:server # Build server
npm run build:sdk # Build client SDK
npm run build:console # Build web console- Start the server:
npm run dev:server - Start web console:
npm run dev:console - Open
test/index.htmlin a browser - Open web console at
http://localhost:8080 - Select the session and start debugging
MIT
{ "mcpServers": { "remote-console": { "command": "npx", "args": ["-y", "remote-console-mcp"], "env": { "RC_SERVER_URL": "wss://your-domain.com/remote-console/ws", "RC_API_TOKEN": "rct_<paste-token-here>" } } } }