一个轻量的远程管理平台,包含三部分:
agent:部署在目标机器上的 Python Agentserver:基于 FastAPI 的管理服务web:基于 Vue 3 + Vite + Ant Design Vue 的控制台
- 许可证:GPL-3.0-or-later,详见
LICENSE。 - 第三方许可:远程桌面 native 组件含 Aspia 派生代码,详见
NOTICE.md与remote-desktop/native-dll/NOTICE.md。 - 安全默认值:仓库不提供默认管理员口令或 Agent bootstrap token;公开部署前必须显式配置并妥善保管。
当前已具备:
- Agent 注册、心跳、自动重连
- 主机信息采集与性能监控
- 文件管理与独立数据通道传输
- 进程管理动态插件安装、卸载、依赖校验
- 交互式终端动态插件与 PTY 会话
- 远程桌面动态插件与 WebRTC 会话链路
- Agent / 插件运行日志查看
- Host Runtime 安装、切换、回滚
- 内置插件与动态插件统一的远程配置能力
apps/
agent/ Agent 入口、Host Runtime、内置插件、动态插件源码
plugins/ 动态插件源码与 manifest 主目录
server/ FastAPI 服务端、插件打包、静态资源
web/ Vue 3 控制台
docs/ 设计文档
- Python 3.11+
- uv
- FastAPI
- websockets
- PostgreSQL
- Tortoise ORM
- Vue 3
- Vite
- Ant Design Vue
- Ruff
- Pyright
Agent 当前是三层结构:
- Bootstrap 层
- capability:
bootstrap.runtime - 负责 Host 包安装、切换、回滚、执行 Python 代码
- capability:
- Host 层
- 由
HostRuntime承载 - 负责装配内置插件、恢复动态插件、提供健康检查
- 由
- 业务插件层
- 内置插件:
system.monitorfile.managerplugin.manager
- 动态插件:
- 服务端从
apps/agent/plugins按需打包 - Agent 通过
plugin.manager安装 / 卸载 / 配置
- 服务端从
- 内置插件:
控制面统一复用普通任务调度:
- 下发:
task.dispatch - 回传:
task.result
当前通信已按最小 plane 概念拆分:
control plane- 承载 Agent 注册、心跳、任务调度、能力同步
- 仍通过
ws://.../ws/agent维护
interactive plane- 承载插件会话、终端交互、远程桌面信令与小体积双向消息
- 当前通过
ws://.../ws/data/agent与ws://.../ws/data/client维护 - 已具备
last_seen / ping-pong / reconnect_attempt / close_reason最小健康状态
当前实现仍是“按 plane 管理的最小版本”,重点在于:
- 区分
control与interactive - 让
interactive plane可以独立保活和重连 - 让会话在打开前能检查所需 plane 是否可用
当前 interactive plane 仍基于 WebSocket。media / bulk 仍属于后续扩展方向,当前仓库尚未独立实现。
交互面当前使用独立 WebSocket:
- Agent:
/ws/data/agent - Browser:
/ws/data/client
当前用于:
- 文件上传 / 下载
- 交互式终端会话
- 远程桌面相关信令与 WebRTC 会话链路
- WebSocket 长连接接入服务端
- 注册、心跳、自动重连
- 主机静态信息采集:
- CPU / GPU / 主板
- 主网卡型号、名称、链路速率
- 磁盘分区清单
- 性能指标采集:
- CPU、内存、磁盘容量
- 磁盘 IO
- 网络速率 / 利用率 / 延迟
- 开机时间
- Host Runtime:
- 启动内置插件
- 恢复动态插件
- 健康检查
- Host 版本切换与回滚
- Agent 注册与在线状态维护
- 按 plane 维护 transport 状态
interactive planestale 连接剔除与快速失败- PostgreSQL 持久化
- 任务派发、结果回写
- 文件数据通道会话管理
- 动态插件按需打包
- Host 包按需打包
- Agent 运行时插件 inventory 同步
- Host Runtime 状态持久化
- 主机列表
- 可区分
ready / control_only / offline的最小在线状态 - 主机概览抽屉
- 文件管理抽屉
- 进程管理抽屉
- 服务管理抽屉
- 交互式终端抽屉
- 远程桌面抽屉与独立弹窗窗口
- 抽屉右上角二层管理面板
- 系统日志 / 插件日志查看
- 插件未安装时的抽屉级遮罩引导
- 监控插件设置面板
- 进程插件管理面板
- 终端插件管理面板
本轮已完成一轮针对 Agent / Server 事件循环阻塞的精简收口,目标是把会长时间占住主循环的同步系统调用、文件 I/O、打包解压、原生初始化统一移到线程池边界。
已完成:
system.monitor- 修复主网卡探测同步 PowerShell 阻塞导致的周期性卡顿
file_manager- 文件管理、目录遍历、压缩/解压、拷贝/移动、上传/下载会话已移出事件循环
plugin_manager- 插件安装、依赖安装/清理、插件包上传与校验已移出事件循环
bootstrap_runtime- Host 包安装、上传分片写盘、收尾 hash 校验已移出事件循环
service_manager- 服务列表、详情、启停控制已改为线程池执行同步 provider
startup_manager- 启动项列表、添加、启用、禁用、删除已改为线程池执行同步 provider
- Linux
systemdunit 读取已合并重复读盘
process_managerpsutil.process_iter()、进程详情采样、proc.wait()已移出事件循环
- Agent 注册握手
- 硬件身份采集、磁盘扫描已改成异步编排
- 静态身份信息增加一次性缓存,重连不再重复跑重探测
- Server 上传 / 打包
- 服务端上传 helper 的分片本地读文件已移出 FastAPI 事件循环
- 插件包 / Host 包的
copytree、make_archive、sha256等打包链路已移出 FastAPI 事件循环
remote_desktoppolicy / session_open / session_liststats- 会话关闭
poll_events()内的显示器拓扑探测- 上述 native 初始化与显示器探测边界均已移出事件循环
当前结论:
- 首轮排查中的高优先级阻塞路径已全部完成收口
- 远程桌面此前的主循环卡顿问题已完成收口;当前结论是“已解决事件循环阻塞导致的卡顿”,后续仍可能继续做体验级性能优化,但不再属于这一轮阻塞清理范围
- 服务端
pyright导入路径配置已对齐,uv run pyright apps\\server可直接通过 - 后续优化重点将转向更细粒度的性能与体验优化,而不是继续清理大块同步阻塞
目录:
apps/agent/host_plugins/
file_manager/
monitor/
plugin_manager/
说明:
- 由
HostRuntime直接加载 Python 类 - 每个目录可带
manifest.json,用于元数据描述与前端展示 - 运行时实际能力、默认配置、配置 schema 仍以插件类本身为准
动态插件统一在 Agent 侧目录维护,服务端负责读取源码目录并打包分发。
源码目录:
apps/agent/plugins/
process_manager/
remote_desktop/
service_manager/
terminal_manager/
约定:
apps/agent/plugins是动态插件源码和 manifest 的主维护位置- Agent 端负责安装、加载、执行和透出运行时 inventory
- 服务端只负责读取该目录、打包 zip、下发安装请求
最小格式:
your-plugin.zip
manifest.json
plugin.py
最小 manifest.json:
{
"plugin_id": "process_manager",
"version": "1.0.0",
"plugin_api_version": "1",
"min_agent_version": "0.1.0",
"entrypoint": "plugin:ProcessManagerPlugin",
"capability_name": "process.manager"
}如果依赖第三方库,可额外声明:
{
"python_dependencies": ["psutil>=6.0.0"]
}当前运行时行为:
- Agent 使用
uv pip install --target <plugin_dir>/_deps ...安装私有依赖 - 依赖状态写入插件目录下的
.dependency_state.json - 插件配置写入插件目录下的
.plugin_config.json - 运行时 inventory 会透出插件 manifest,供前端和后续服务侧复用
- 会话型能力可通过
data_session_handlers()注册到统一数据面
会话型插件不再要求修改 Agent 核心分发逻辑。
当前统一约定:
- Server 创建会话:
POST /api/agents/{agent_id}/sessions - Browser 数据面:
/ws/data/client - Agent 数据面:
/ws/data/agent - 插件通过
DataSessionHandler自注册stream_kind + resource_id
当前会话打开前会先检查 interactive plane 是否健康;如果 Agent 控制面在线但交互面不可用,会优先返回明确错误,而不是继续长时间等待超时。
当前已接入:
download + file.pathupload + file.pathduplex + terminal.shellsignal + desktop.webrtc
当前阶段插件 action 使用轻量契约,不要求一开始就补完整 JSON Schema。
推荐字段:
descriptiontimeoutread_onlydangerousparams
示例:
{
"actions": {
"list": {
"description": "List processes on the remote host.",
"timeout": 30,
"read_only": true,
"dangerous": false,
"params": {
"keyword": "string, optional",
"limit": "integer, optional; range 1-200"
}
}
}
}当前内置插件和动态插件都支持统一的远程配置协议。
插件基类支持:
default_config()config_schema()validate_config()current_config()on_config_updated()
- 动态插件的 manifest 主要来自
apps/agent/plugins - 内置插件的 schema 和默认值来自 Agent 侧插件类本身
- 动态插件:
<plugin_dir>/.plugin_config.json
- 内置插件:
<agent_plugin_root>/_builtin_configs/<plugin_id>.json
前端读取:
GET /api/agents/{agent_id}/plugins/{plugin_id}/{version}/config
服务端会转发给 Agent 上的 plugin.manager,由 Agent:
- 读取本地已保存配置
- 与
default_config()合并 - 通过
validate_config()规范化 - 返回
config + defaults + schema
所以前端拿到的是“当前最终生效值”,不是单纯的原始文件内容。
system_monitor:网络采样、磁盘 IO 采样、延迟探测、Top 进程、首选网卡process_manager:列表默认条数、进程详情开关remote_desktop:启用状态、查看/输入/文本剪贴板权限、默认显示器、采集后端、默认 FPS、默认码率、最大会话数service_manager:服务列表和控制策略terminal_manager:交互开关、自定义环境变量、默认 shell、允许 shell、目录策略、命令拦截、最大会话数
- Agent 使用
loguru输出系统日志和插件日志 - 默认目录:
apps/agent/runtime/logs - 系统日志:
apps/agent/runtime/logs/system/agent.jsonl - 插件日志:
apps/agent/runtime/logs/plugins/<plugin_id>.jsonl - 文件达到
5 MB时轮转,最多保留1份归档
插件管理与远程桌面插件当前会写入更完整的运行日志,包括:
- 插件安装 / 卸载 / 重装 / 启用
- 依赖安装 / 校验 / 清理
- 远程桌面策略读取、会话打开 / 关闭、连接状态变化、输入与剪贴板事件
前端当前支持:
- 日志源切换
- 级别过滤
- 关键字搜索
- 深色控制台查看
GET /api/agentsGET /api/agents/{agent_id}/metricsPOST /api/agents/{agent_id}/monitor/disksPOST /api/agents/{agent_id}/monitor/profilePOST /api/agents/{agent_id}/tasks/system.monitor/{action}snapshotdisks
POST /api/agents/{agent_id}/tasks/file.manager/{action}listmkdirrenamedeletecopymovebatch_deletebatch_copybatch_movecreate_archiveextract_archive
POST /api/agents/{agent_id}/sessionsupload + file.pathdownload + file.path
POST /api/agents/{agent_id}/tasks/terminal.manager/shellsPOST /api/agents/{agent_id}/tasks/terminal.manager/policyPOST /api/agents/{agent_id}/tasks/terminal.manager/historyPOST /api/agents/{agent_id}/tasks/terminal.manager/execPOST /api/agents/{agent_id}/sessionsduplex + terminal.shell
GET /api/pluginsGET /api/agents/{agent_id}/pluginsGET /api/agents/{agent_id}/plugins/runtimePOST /api/agents/{agent_id}/plugins/runtime/syncPOST /api/agents/{agent_id}/plugins/installPOST /api/agents/{agent_id}/plugins/uninstallGET /api/agents/{agent_id}/plugins/{plugin_id}/{version}/configPOST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/configGET /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependenciesPOST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies/installPOST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies/verifyPOST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies/cleanupGET /api/agents/{agent_id}/logs/sourcesGET /api/agents/{agent_id}/logs
GET /api/agents/{agent_id}/bootstrap/runtimeGET /api/agents/{agent_id}/bootstrap/runtime/stateGET /api/agents/{agent_id}/bootstrap/runtime/versionsPOST /api/agents/{agent_id}/bootstrap/runtime/installPOST /api/agents/{agent_id}/bootstrap/runtime/switchPOST /api/agents/{agent_id}/bootstrap/runtime/rollbackPOST /api/agents/{agent_id}/bootstrap/runtime/reloadPOST /api/agents/{agent_id}/bootstrap/runtime/exec-python
uv sync$env:SERVER_DATABASE_URL="postgres://postgres:postgres@127.0.0.1:5432/python_agent"
$env:SERVER_ADMIN_TOKEN="change-me-admin-token"
$env:SERVER_AGENT_SHARED_TOKEN="change-me-agent-bootstrap-token"
uv run .\apps\server\main.py默认地址:
- Web/API:
http://127.0.0.1:8000 - Agent 控制面:
ws://127.0.0.1:8000/ws/agent - Agent 数据面:
ws://127.0.0.1:8000/ws/data/agent - Browser 数据面:
ws://127.0.0.1:8000/ws/data/client
$env:AGENT_SERVER_URL="ws://127.0.0.1:8000/ws/agent"
$env:AGENT_ID="my-agent"
$env:AGENT_TOKEN="change-me-agent-bootstrap-token"
uv run .\apps\agent\main.pycd .\apps\web
npm install
npm run devcd .\apps\web
npm run build构建产物输出到:
apps/server/static
powershell -ExecutionPolicy Bypass -File .\remote-desktop\native-dll\scripts\build.ps1当前默认产物与同步位置:
remote-desktop/native-dll/build/Release/aspia_capture_probe.dllapps/agent/plugins/remote_desktop/native/bin/aspia_capture_probe.dll
本地直跑源码插件时,Agent 优先从 apps/agent/plugins/remote_desktop/native/bin/ 读取 DLL;
服务端在打包 remote_desktop 插件时,也会把当前构建出的 DLL 一并复制进插件包。
cd .\apps\web
npm run typecheck首页抽屉相关渐进式重构,优先以 vue-tsc 结果为准。
uv run ruff check .
uv run pyright常用配置项:
SERVER_DATABASE_URLSERVER_DB_AUTO_SCHEMASERVER_DB_CONNECT_TIMEOUTSERVER_HOSTSERVER_PORTSERVER_ADMIN_TOKENSERVER_AGENT_SHARED_TOKEN
SERVER_ADMIN_TOKEN 和 SERVER_AGENT_SHARED_TOKEN 默认为空。为空时,HTTP 管理面与新 Agent bootstrap 均会失败闭合。
常用环境变量:
AGENT_SERVER_URLAGENT_IDAGENT_TOKENAGENT_MONITOR_INTERVALAGENT_MAX_CONCURRENCYAGENT_PLUGIN_ROOTAGENT_LOG_DIRAGENT_CACHE_DIR
默认配置已内嵌在 Python 源码中:
apps/agent/core/config.py
如果需要做源码加密,可直接围绕这个模块处理;运行时仍保持“环境变量优先,内嵌默认值兜底”。
默认 schema:
agmg_app
主要表:
agentsagent_connectionstaskstask_eventsmetric_samplesfile_sessionsagent_pluginsagent_host_runtime_states
- 看不到数据库表:确认查看的是
agmg_app而不是public - Agent 无法接入:检查
server是否启动、AGENT_SERVER_URL、AGENT_TOKEN - HTTP 管理接口返回 503:检查是否已显式配置
SERVER_ADMIN_TOKEN - Agent 显示在线但终端打不开:优先看是否处于
control_only,通常表示interactive plane正在重连、已失活或被服务端摘除 - 动态插件安装失败:检查
apps/agent/plugins/<plugin_id>/manifest.json、plugin.py、Agent 侧uv - 重装插件后版本不对:先看插件管理面板里的“可用版本”和“已安装版本”是否一致
- 看不到插件设置:确认 Agent 版本包含配置接口,插件正确返回
default_config()与config_schema() - 日志面板缺少插件日志源:先确认插件已安装并实际触发过一次动作,再检查
apps/agent/runtime/logs/plugins/<plugin_id>.jsonl
- 当前默认使用
ws,生产环境建议切到wss - Git 历史中若曾提交真实凭据,公开仓库前必须先轮换凭据并清洗历史
- 当前只完成
control plane + interactive plane的第一阶段最小落地 - 文件传输当前仍复用
interactive plane,bulk plane仍未独立实现 media plane仍未独立实现- 文件数据面仍是最小可用实现,尚未做断点续传与并发分片
- 动态插件暂未实现签名校验、版本回滚
- 终端在 Windows PTY 下仍需继续优化宽高同步与回显稳定性
- 远程桌面当前最小闭环已打通:
- 首页抽屉可打开远程桌面入口
- 浏览器会弹出独立远程桌面窗口
- 当前主链路为
aspia_capture_probe.dll -> aiortc -> WebRTC -> 浏览器 - 浏览器端已支持远端光标渲染,并能消费 DLL 透传的
cursor position / shape / hotspot
- 远程桌面当前已补一版最小文本 clipboard 闭环:
- 会话打开时仍有显式
allow_clipboard开关;插件配置默认关闭,但当前首页远程桌面面板默认会以“双向同步”模式打开会话,如需禁用需显式选择“不同步” - 浏览器当前支持
双向同步 / 本机 -> 远程 / 远程 -> 本机 / 不同步四种文本剪贴板模式;启用同步后,会按模式自动读写浏览器侧系统剪贴板 - 远端纯文本剪贴板变化后会通知浏览器;若当前模式允许“远程 -> 本机”,前端会自动写回本地系统剪贴板
- 远程桌面弹窗内仍支持直接消费
paste事件,把本地纯文本立即发送到远端 - 当前文本剪贴板同步放在 Python 插件层,不走 native DLL ABI;这一取舍是因为 Aspia 的 clipboard 主体也更靠近会话/监控层,而不是捕获 DLL
- 会话打开时仍有显式
- 远程桌面当前已补最小
display metadata / monitor control闭环:- Agent
policy会返回更完整的显示器元数据,包括monitor_id / 坐标 / 分辨率 / primary / device_name / dpi / scale / desktop bounds - 首页远程桌面面板可显式选择目标显示器,并展示当前显示器和虚拟桌面边界
- 远程桌面弹窗窗口可再次切换目标显示器;当前策略是更新
monitor_id后重建会话,不强求首轮无缝热切换 - native DLL 继续复用现有
scp_list_monitors字段,不新增 monitor ABI;desktop bounds / scale当前在插件层基于现有元数据归一化
- Agent
- 远程桌面当前光标链路仍有已知边界:
- cursor 协议当前已正式拆成
desktop.cursor.position与desktop.cursor.shape shape已改为随 native cursor 事件内联传输,并带 cache slot;浏览器端已有正式 cursor cacheposition已走轻量高频查询路径,并在 Agent / Server 两段发送队列里启用“只保留最新值”的覆盖发送- 当前 native capture wrapper 已补齐更接近 Aspia 的
capture_cursor()/query_cursor_state()边界,但整体调度仍是本地 worker + clock,而不是 Aspia 原生的 Qt timer / scheduler 结构 - 浏览器侧当前直接渲染 native 位图 shape,并用
requestAnimationFrame驱动 overlay 更新;动画光标与更高层系统语义映射仍未单独处理
- cursor 协议当前已正式拆成
- 远程桌面当前 clipboard 仍有已知边界:
- 只支持纯文本,不支持文件、图片、富文本
- 文本同步当前基于会话级
allow_clipboard开关和浏览器侧模式选择,不是 Aspia 式的更完整原生 clipboard 会话协议 - 当前对文本大小做了保守上限,超过上限会跳过同步并回传状态
- 远程桌面 display / topology 当前仍有剩余边界:
- 还没有 Aspia 那种
ScreenList持续推送与current_screen / primary_screen / resolution的完整会话协议 - 还没有 full desktop 捕获、系统级分辨率切换、无缝热切换与多客户端协调
- 还没有 Aspia 那种
- 前端仍存在大包优化空间
- 首页抽屉新插件开发指南
- 首页抽屉插件架构说明
- 贡献指南
- 安全策略
- 第三方许可说明
docs/frontend-modules.mddocs/frontend-ux-design.mddocs/multi-transport-minimal-architecture-draft.mddocs/plugin-mcp-guidelines.md