Remote Workspace 远程工作区
让用户在浏览器中选择远程机器,启动 AI 编码会话,AI CLI 直接运行在远程机器上——无需 SSH,无需重复配置。
目录
概述
解决什么问题
Open ACE 工作区默认只能在服务器本机运行。用户需要操作远程机器(如开发/测试服务器)时,必须每次在会话中指示 AI 通过 SSH 连接远程机器,频繁提供凭据且容易遇到 SSH 故障。
如何解决
远程工作区功能让用户在创建会话时选择远程机器。AI CLI 直接运行在远程机器的 Agent 上,通过服务端代理访问 LLM API。所有 API Key 永远不会离开服务器。
核心特性
| 特性 | 说明 |
|---|---|
| 多 CLI 支持 | Qwen Code、Claude Code、OpenClaw 等 |
| API Key 代理 | Key 加密存储在服务器,远程 Agent 只拿到短期代理令牌 |
| 一行安装 | curl ... | bash 完成远程机器部署 |
| 统一配额 | 本地和远程会话共享同一套配额体系 |
| 自动重连 | Agent 断线后指数退避自动重连 |
架构
[浏览器 UI] <--HTTP/WS--> [Open ACE 服务器] <--HTTP 轮询--> [远程 Agent]
| |
[API Key 加密存储] [qwen-code-cli]
[配额管理器] [claude-code]
[会话管理器] [openclaw]
消息流
- 用户在浏览器输入消息 →
POST /api/remote/sessions/{id}/chat - 服务器将命令排队,等待 Agent 通过 HTTP 轮询拉取
- Agent 将消息喂给 CLI 子进程
- CLI 需要 LLM 调用 → 请求发往
POST /api/remote/llm-proxy - 服务器校验配额、注入真实 API Key、转发到 LLM 提供商
- LLM 流式响应:提供商 → 服务器 → Agent → 服务器 → 浏览器
- 服务器记录 Token 用量
通信方式
Agent 支持两种与服务器的通信方式:
| 方式 | 状态 | 适用场景 | 特点 |
|---|---|---|---|
| HTTP 轮询 | 已实现,推荐使用 | 所有场景 | Agent 主动 POST,服务器返回待执行命令,兼容性好 |
| WebSocket | 计划中 | 实时性要求高 | 需 gevent/websocket worker 支持,当前返回 501 |
Agent 优先尝试 WebSocket 连接,失败时自动降级为 HTTP 轮询。当前版本建议直接使用 HTTP 轮询模式。
部署检查清单
从零到可用,按顺序完成以下步骤:
服务端(Open ACE 服务器)
# 1. 确认代码已更新到包含远程工作区模块
ls app/modules/workspace/api_key_proxy.py \
app/modules/workspace/remote_agent_manager.py \
app/modules/workspace/remote_session_manager.py \
app/routes/remote.py
# 2. 运行数据库迁移(创建 remote_machines、machine_assignments、api_key_store 表)
cd /path/to/open-ace
alembic upgrade head
# 3. 设置加密密钥(生产环境强烈推荐)
export OPENACE_ENCRYPTION_KEY="<your-random-32byte-key>"
# 或使用 openssl 生成:openssl rand -hex 32
# 4. 重启服务
sudo systemctl restart open-ace # 或你的启动方式
# 5. 验证 API 可访问
curl -s http://localhost:5000/api/remote/agent/install.sh | head -5
# 应该输出安装脚本内容
管理员操作
# 1. 管理员登录
curl -c cookies.txt -X POST http://<server>:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 2. 存储 LLM API Key(远程会话必需)
curl -b cookies.txt -X POST http://<server>:5000/api/remote/api-keys \
-H "Content-Type: application/json" \
-d '{
"provider": "openai",
"key_name": "production",
"api_key": "sk-xxx...",
"base_url": "https://api.openai.com/v1"
}'
# 3. 生成注册令牌
curl -b cookies.txt -X POST http://<server>:5000/api/remote/machines/register \
-H "Content-Type: application/json" \
-d '{"tenant_id": 1}'
# → {"registration_token": "abc123..."}
远程机器
# 在远程机器上执行一行安装(将 <token> 替换为上一步获取的注册令牌)
curl -fsSL http://<server>:5000/api/remote/agent/install.sh | \
bash -s -- --server http://<server>:5000 --token <token>
# 如果 curl 404,说明服务器缺少安装脚本路由,请使用手动安装(见下方)
分配用户
# 获取 machine_id(从安装输出或管理界面获取)
curl -b cookies.txt http://<server>:5000/api/remote/machines
# 分配用户(user_id 从用户管理页面获取)
curl -b cookies.txt -X POST \
http://<server>:5000/api/remote/machines/<machine_id>/assign \
-H "Content-Type: application/json" \
-d '{"user_id": <user_id>, "permission": "user"}'
完成以上步骤后,用户即可在浏览器工作区中选择远程机器创建会话。
快速开始
前提条件
- Open ACE 服务器已部署并运行
- 远程机器可访问服务器的 HTTP 端口
- 远程机器已安装 Python 3.8+
- 远程机器已安装 Node.js(用于安装 CLI 工具)
三步完成
第一步:管理员生成注册令牌
在 Open ACE 管理界面(管理模式 → 远程工作区 → 远程机器 → 生成注册令牌),或通过 API:
# 管理员登录获取 session_token
curl -c cookies.txt -X POST http://<server>:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 生成注册令牌
curl -b cookies.txt -X POST http://<server>:5000/api/remote/machines/register \
-H "Content-Type: application/json" \
-d '{"tenant_id": 1}'
返回:
{"registration_token": "a1b2c3d4e5f6..."}
第二步:在远程机器上安装 Agent
curl -fsSL http://<server>:5000/api/remote/agent/install.sh | \
bash -s -- --server http://<server>:5000 --token <registration-token>
注意:如果此命令返回 404,说明服务器尚未部署安装脚本路由。请先确认服务器代码已更新到最新版本并重启。临时替代方案见手动安装。
安装脚本会自动:
- 从服务器下载 Agent 文件到
~/.open-ace-agent/ - 安装 Python 依赖(websocket-client、requests)
- 安装 CLI 工具(默认 qwen-code-cli,可选 claude-code)
- 生成 machine_id 并注册到服务器
- 安装为系统服务(Linux: systemd,macOS: launchd)
第三步:分配用户
管理员在管理界面(远程机器详情弹窗 → 分配用户),或通过 API:
curl -b cookies.txt -X POST \
http://<server>:5000/api/remote/machines/<machine_id>/assign \
-H "Content-Type: application/json" \
-d '{"user_id": <user_id>, "permission": "user"}'
用户即可在浏览器中看到该机器并创建远程会话。
服务端配置
数据库迁移
远程工作区使用 Alembic 迁移 20260417_033_add_remote_workspace_tables.py,会自动创建以下表:
| 表名 | 用途 |
|---|---|
remote_machines | 注册的远程机器信息 |
machine_assignments | 机器与用户的分配关系 |
api_key_store | 加密存储的 LLM API Key |
同时为 agent_sessions 表增加两列:
workspace_type—local或remoteremote_machine_id— 关联的远程机器 ID
环境变量
| 变量 | 必需 | 说明 | 默认值 |
|---|---|---|---|
OPENACE_ENCRYPTION_KEY | 推荐 | API Key 加密密钥。未设置时从 SECRET_KEY 派生 | SECRET_KEY 的 SHA-256 |
SECRET_KEY | 是 | Flask 会话密钥,也影响 API Key 加密 | dev-secret-key |
API Key 管理
在管理界面(管理模式 → 远程工作区 → API 密钥 → 添加 API 密钥),或通过 API:
# 存储 OpenAI API Key
curl -b cookies.txt -X POST http://<server>:5000/api/remote/api-keys \
-H "Content-Type: application/json" \
-d '{
"provider": "openai",
"key_name": "production",
"api_key": "sk-xxx...",
"base_url": "https://api.openai.com/v1"
}'
支持的 provider:
openai— OpenAI / 通义千问等 OpenAI 兼容 APIanthropic— Anthropic / Claude APIgoogle— Google Gemini API
管理页面 API 密钥 vs 环境变量 API 密钥
本系统存在两套独立的 API 密钥机制,分别服务不同场景,互不共享,互不影响:
环境变量密钥(如 auth.env.OPENAI_API_KEY) | 管理页面 API 密钥 | |
|---|---|---|
| 用途 | 本地工作区(iframe 中的 qwen-code-webui) | 远程工作区(远程机器上的 Agent) |
| 存储位置 | 服务器配置文件 / 环境变量 | 数据库 api_key_store 表(AES-256-GCM 加密) |
| 谁拿到真实 Key | 本地 CLI 直接使用 | 只有服务器知道,远程 Agent 只拿短期代理令牌 |
| 管理方式 | 修改配置文件,重启服务 | 管理页面 UI 操作,无需重启 |
| 管理权限 | 服务器运维人员 | 系统管理员(Web 管理界面) |
重要:如果未在管理页面添加 API 密钥,远程机器将无法调用 LLM。远程工作区不会 fallback 使用环境变量中的 OPENAI_API_KEY。远程 Agent 调用 LLM 时,服务器仅从 api_key_store 表中查找密钥,找不到则返回错误:
{"error": {"message": "No API key configured for provider 'openai'", "type": "config_error"}}
因此,启用远程工作区功能后,管理员必须在管理页面为所需的 LLM 提供商分别添加 API 密钥。
远程 Agent 安装
一行安装(推荐)
Linux / macOS:
curl -fsSL http://<server>:5000/api/remote/agent/install.sh | \
bash -s -- --server http://<server>:5000 --token <token>
Windows (PowerShell):
Invoke-WebRequest -Uri "http://<server>:5000/api/remote/agent/install.ps1" | Invoke-Expression
安装参数
| 参数 | 必需 | 说明 | 默认值 |
|---|---|---|---|
--server URL | 是 | Open ACE 服务器地址 | - |
--token TOKEN | 是 | 管理员生成的注册令牌 | - |
--name NAME | 否 | 机器显示名称 | hostname |
--install-cli TOOL | 否 | 要安装的 CLI 工具 | qwen-code-cli |
--dir DIR | 否 | 安装目录 | ~/.open-ace-agent |
示例 — 安装 Claude Code:
curl -fsSL http://<server>:5000/api/remote/agent/install.sh | \
bash -s -- --server https://ace.example.com \
--token abc123... \
--install-cli claude-code \
--name "Production Server"
手动安装
如果无法使用一键脚本(例如服务器缺少安装脚本路由),可以手动安装:
# 1. 复制 remote-agent/ 目录到远程机器
scp -r remote-agent/ user@remote:~/.open-ace-agent/
# 2. 安装依赖
cd ~/.open-ace-agent
pip3 install -r requirements.txt
# 3. 安装 CLI 工具
npm install -g @qwen-code/qwen-code@latest
# 4. 创建配置文件
cat > config.json << 'EOF'
{
"server_url": "https://ace.example.com",
"machine_id": "",
"machine_name": "My Server",
"registration_token": "<从管理员获取>",
"cli_tool": "qwen-code-cli"
}
EOF
# 5. 注册机器(也可以直接运行 agent.py 让其自动注册)
# 手动注册:
MACHINE_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
curl -X POST "${SERVER_URL}/api/remote/agent/register" \
-H "Content-Type: application/json" \
-d "{
\"registration_token\": \"<token>\",
\"machine_id\": \"${MACHINE_ID}\",
\"machine_name\": \"$(hostname)\",
\"hostname\": \"$(hostname)\",
\"os_type\": \"$(uname -s | tr '[:upper:]' '[:lower:]')\",
\"os_version\": \"$(uname -r)\",
\"capabilities\": {},
\"agent_version\": \"1.0.0\"
}"
# 6. 更新 config.json 中的 machine_id
# 7. 运行 Agent
python3 agent.py
# 8. (可选)安装为系统服务,参见下方"服务管理"
Agent 目录结构
~/.open-ace-agent/
├── agent.py # 主守护进程
├── config.py # 配置管理
├── config.json # 配置文件
├── executor.py # CLI 子进程管理
├── system_info.py # 系统信息收集
├── requirements.txt # Python 依赖
├── machine_id # 机器唯一标识(自动生成)
├── agent.log # 运行日志
├── agent-error.log # 错误日志
└── cli_adapters/
├── __init__.py # 适配器注册中心
├── base.py # 适配器基类
├── qwen_code.py # Qwen Code 适配器
├── claude_code.py # Claude Code 适配器
└── openclaw.py # OpenClaw 适配器
服务管理
Linux (systemd):
# 查看状态
sudo systemctl status open-ace-agent
# 查看日志
sudo journalctl -u open-ace-agent -f
# 重启
sudo systemctl restart open-ace-agent
# 停止
sudo systemctl stop open-ace-agent
macOS (launchd):
# 查看日志
tail -f ~/.open-ace-agent/agent.log
# 停止
launchctl unload ~/Library/LaunchAgents/com.open-ace.agent.plist
# 启动
launchctl load ~/Library/LaunchAgents/com.open-ace.agent.plist
手动运行(调试):
cd ~/.open-ace-agent
python3 agent.py
管理远程机器
注册新机器
在管理界面点击"生成注册令牌"按钮,或通过 API:
# 1. 管理员登录
curl -c cookies.txt -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 2. 生成注册令牌
curl -b cookies.txt -X POST http://localhost:5000/api/remote/machines/register \
-H "Content-Type: application/json" \
-d '{"tenant_id": 1}'
# → {"registration_token": "abc123..."}
查看所有机器
curl -b cookies.txt http://localhost:5000/api/remote/machines
响应示例:
{
"machines": [
{
"machine_id": "uuid-xxx",
"machine_name": "Production Server",
"hostname": "prod-01.example.com",
"os_type": "linux",
"os_version": "Ubuntu 22.04",
"status": "online",
"connected": true,
"capabilities": {"cpu_cores": 16, "memory_gb": 64},
"agent_version": "1.0.0"
}
]
}
分配用户
# 给用户分配机器使用权
curl -b cookies.txt -X POST \
http://localhost:5000/api/remote/machines/<machine_id>/assign \
-H "Content-Type: application/json" \
-d '{"user_id": <user_id>, "permission": "user"}'
权限级别:
user— 可以使用机器创建会话admin— 可以使用机器,且可以管理本机器的用户和会话(机器级管理员)
权限模型
| 操作 | 系统管理员 | 机器管理员 | 普通用户 |
|---|---|---|---|
| 创建会话 | ✅ | ✅ | ✅ |
| 管理自己的会话 | ✅ | ✅ | ✅ |
| 查看/停止他人会话(本机器) | ✅ | ✅ | ❌ |
| 浏览机器文件 | ✅ | ✅ | ✅ |
| 分配/撤销用户(本机器) | ✅ | ✅ | ❌ |
| 注销机器 | ✅ | ❌ | ❌ |
| 生成注册令牌 | ✅ | ❌ | ❌ |
| 管理 API Key | ✅ | ❌ | ❌ |
机器管理员可以:
- 分配普通用户到本机器(不能授权 admin 权限)
- 撤销普通用户的访问权限(不能撤销 admin 用户)
- 查看和停止本机器上其他用户的会话
机器管理员不能:
- 注销机器
- 生成注册令牌
- 管理 API Key
- 授权其他用户为 admin
撤销用户权限
curl -b cookies.txt -X DELETE \
http://localhost:5000/api/remote/machines/<machine_id>/assign/<user_id>
注销机器
curl -b cookies.txt -X DELETE \
http://localhost:5000/api/remote/machines/<machine_id>
管理界面
系统管理员可以通过 Open ACE Web 管理界面完成所有操作。管理页面(/manage/*)仅对系统管理员开放。机器管理员通过 API 管理本机器的用户和会话。
侧边栏 "远程工作区" 分组下的远程机器和 API Key 页面仅系统管理员可见。机器管理员通过 API 接口执行用户管理操作(见下方 API 参考)。
远程机器管理(系统管理员)
路径:管理模式 → 远程工作区 → 远程机器(/manage/remote/machines)
功能:
| 操作 | 说明 |
|---|---|
| 生成注册令牌 | 点击按钮生成一次性注册 token,弹窗显示 token 值、复制按钮和安装命令 |