Skip to content

Latest commit

 

History

History
658 lines (506 loc) · 21.2 KB

README.md

File metadata and controls

658 lines (506 loc) · 21.2 KB

Python Agent Platform

一个轻量的远程管理平台,包含三部分:

  • agent:部署在目标机器上的 Python Agent
  • server:基于 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 当前是三层结构:

  1. Bootstrap 层
    • capability:bootstrap.runtime
    • 负责 Host 包安装、切换、回滚、执行 Python 代码
  2. Host 层
    • 由 HostRuntime 承载
    • 负责装配内置插件、恢复动态插件、提供健康检查
  3. 业务插件层
    • 内置插件:
      • system.monitor
      • file.manager
      • plugin.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 会话链路

当前能力

Agent

  • WebSocket 长连接接入服务端
  • 注册、心跳、自动重连
  • 主机静态信息采集:
    • CPU / GPU / 主板
    • 主网卡型号、名称、链路速率
    • 磁盘分区清单
  • 性能指标采集:
    • CPU、内存、磁盘容量
    • 磁盘 IO
    • 网络速率 / 利用率 / 延迟
    • 开机时间
  • Host Runtime:
    • 启动内置插件
    • 恢复动态插件
    • 健康检查
    • Host 版本切换与回滚

Server

  • Agent 注册与在线状态维护
  • 按 plane 维护 transport 状态
  • interactive plane stale 连接剔除与快速失败
  • PostgreSQL 持久化
  • 任务派发、结果回写
  • 文件数据通道会话管理
  • 动态插件按需打包
  • Host 包按需打包
  • Agent 运行时插件 inventory 同步
  • Host Runtime 状态持久化

Web

  • 主机列表
  • 可区分 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 systemd unit 读取已合并重复读盘
  • process_manager
    • psutil.process_iter()、进程详情采样、proc.wait() 已移出事件循环
  • Agent 注册握手
    • 硬件身份采集、磁盘扫描已改成异步编排
    • 静态身份信息增加一次性缓存,重连不再重复跑重探测
  • Server 上传 / 打包
    • 服务端上传 helper 的分片本地读文件已移出 FastAPI 事件循环
    • 插件包 / Host 包的 copytree、make_archive、sha256 等打包链路已移出 FastAPI 事件循环
  • remote_desktop
    • policy / session_open / session_list
    • stats
    • 会话关闭
    • poll_events() 内的显示器拓扑探测
    • 上述 native 初始化与显示器探测边界均已移出事件循环

当前结论:

  • 首轮排查中的高优先级阻塞路径已全部完成收口
  • 远程桌面此前的主循环卡顿问题已完成收口;当前结论是“已解决事件循环阻塞导致的卡顿”,后续仍可能继续做体验级性能优化,但不再属于这一轮阻塞清理范围
  • 服务端 pyright 导入路径配置已对齐,uv run pyright apps\\server 可直接通过
  • 后续优化重点将转向更细粒度的性能与体验优化,而不是继续清理大块同步阻塞

插件系统

1. 内置插件

目录:

apps/agent/host_plugins/
  file_manager/
  monitor/
  plugin_manager/

说明:

  • 由 HostRuntime 直接加载 Python 类
  • 每个目录可带 manifest.json,用于元数据描述与前端展示
  • 运行时实际能力、默认配置、配置 schema 仍以插件类本身为准

2. 动态插件

动态插件统一在 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() 注册到统一数据面

3. 通用插件会话

会话型插件不再要求修改 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.path
  • upload + file.path
  • duplex + terminal.shell
  • signal + desktop.webrtc

4. 轻量 manifest 约定

当前阶段插件 action 使用轻量契约,不要求一开始就补完整 JSON Schema。

推荐字段:

  • description
  • timeout
  • read_only
  • dangerous
  • params

示例:

{
  "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:

  1. 读取本地已保存配置
  2. 与 default_config() 合并
  3. 通过 validate_config() 规范化
  4. 返回 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 份归档

插件管理与远程桌面插件当前会写入更完整的运行日志,包括:

  • 插件安装 / 卸载 / 重装 / 启用
  • 依赖安装 / 校验 / 清理
  • 远程桌面策略读取、会话打开 / 关闭、连接状态变化、输入与剪贴板事件

前端当前支持:

  • 日志源切换
  • 级别过滤
  • 关键字搜索
  • 深色控制台查看

常用 API

Agent 与监控

  • GET /api/agents
  • GET /api/agents/{agent_id}/metrics
  • POST /api/agents/{agent_id}/monitor/disks
  • POST /api/agents/{agent_id}/monitor/profile
  • POST /api/agents/{agent_id}/tasks/system.monitor/{action}
    • snapshot
    • disks

文件管理

  • POST /api/agents/{agent_id}/tasks/file.manager/{action}
    • list
    • mkdir
    • rename
    • delete
    • copy
    • move
    • batch_delete
    • batch_copy
    • batch_move
    • create_archive
    • extract_archive
  • POST /api/agents/{agent_id}/sessions
    • upload + file.path
    • download + file.path

终端

  • POST /api/agents/{agent_id}/tasks/terminal.manager/shells
  • POST /api/agents/{agent_id}/tasks/terminal.manager/policy
  • POST /api/agents/{agent_id}/tasks/terminal.manager/history
  • POST /api/agents/{agent_id}/tasks/terminal.manager/exec
  • POST /api/agents/{agent_id}/sessions
    • duplex + terminal.shell

插件管理

  • GET /api/plugins
  • GET /api/agents/{agent_id}/plugins
  • GET /api/agents/{agent_id}/plugins/runtime
  • POST /api/agents/{agent_id}/plugins/runtime/sync
  • POST /api/agents/{agent_id}/plugins/install
  • POST /api/agents/{agent_id}/plugins/uninstall
  • GET /api/agents/{agent_id}/plugins/{plugin_id}/{version}/config
  • POST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/config
  • GET /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies
  • POST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies/install
  • POST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies/verify
  • POST /api/agents/{agent_id}/plugins/{plugin_id}/{version}/dependencies/cleanup
  • GET /api/agents/{agent_id}/logs/sources
  • GET /api/agents/{agent_id}/logs

Host Runtime

  • GET /api/agents/{agent_id}/bootstrap/runtime
  • GET /api/agents/{agent_id}/bootstrap/runtime/state
  • GET /api/agents/{agent_id}/bootstrap/runtime/versions
  • POST /api/agents/{agent_id}/bootstrap/runtime/install
  • POST /api/agents/{agent_id}/bootstrap/runtime/switch
  • POST /api/agents/{agent_id}/bootstrap/runtime/rollback
  • POST /api/agents/{agent_id}/bootstrap/runtime/reload
  • POST /api/agents/{agent_id}/bootstrap/runtime/exec-python

本地开发

1. 安装依赖

uv sync

2. 启动服务端

$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

3. 启动 Agent

$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.py

4. 启动前端

cd .\apps\web
npm install
npm run dev

5. 构建前端

cd .\apps\web
npm run build

构建产物输出到:

  • apps/server/static

5.1 构建远程桌面 native DLL

powershell -ExecutionPolicy Bypass -File .\remote-desktop\native-dll\scripts\build.ps1

当前默认产物与同步位置:

  • remote-desktop/native-dll/build/Release/aspia_capture_probe.dll
  • apps/agent/plugins/remote_desktop/native/bin/aspia_capture_probe.dll

本地直跑源码插件时,Agent 优先从 apps/agent/plugins/remote_desktop/native/bin/ 读取 DLL; 服务端在打包 remote_desktop 插件时,也会把当前构建出的 DLL 一并复制进插件包。

6. 前端类型检查

cd .\apps\web
npm run typecheck

首页抽屉相关渐进式重构,优先以 vue-tsc 结果为准。

7. 静态检查

uv run ruff check .
uv run pyright

配置

Server

常用配置项:

  • SERVER_DATABASE_URL
  • SERVER_DB_AUTO_SCHEMA
  • SERVER_DB_CONNECT_TIMEOUT
  • SERVER_HOST
  • SERVER_PORT
  • SERVER_ADMIN_TOKEN
  • SERVER_AGENT_SHARED_TOKEN

SERVER_ADMIN_TOKEN 和 SERVER_AGENT_SHARED_TOKEN 默认为空。为空时,HTTP 管理面与新 Agent bootstrap 均会失败闭合。

Agent

常用环境变量:

  • AGENT_SERVER_URL
  • AGENT_ID
  • AGENT_TOKEN
  • AGENT_MONITOR_INTERVAL
  • AGENT_MAX_CONCURRENCY
  • AGENT_PLUGIN_ROOT
  • AGENT_LOG_DIR
  • AGENT_CACHE_DIR

默认配置已内嵌在 Python 源码中:

  • apps/agent/core/config.py

如果需要做源码加密,可直接围绕这个模块处理;运行时仍保持“环境变量优先,内嵌默认值兜底”。

数据库

默认 schema:

  • agmg_app

主要表:

  • agents
  • agent_connections
  • tasks
  • task_events
  • metric_samples
  • file_sessions
  • agent_plugins
  • agent_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 当前在插件层基于现有元数据归一化
  • 远程桌面当前光标链路仍有已知边界:
    • cursor 协议当前已正式拆成 desktop.cursor.position 与 desktop.cursor.shape
    • shape 已改为随 native cursor 事件内联传输,并带 cache slot;浏览器端已有正式 cursor cache
    • position 已走轻量高频查询路径,并在 Agent / Server 两段发送队列里启用“只保留最新值”的覆盖发送
    • 当前 native capture wrapper 已补齐更接近 Aspia 的 capture_cursor() / query_cursor_state() 边界,但整体调度仍是本地 worker + clock,而不是 Aspia 原生的 Qt timer / scheduler 结构
    • 浏览器侧当前直接渲染 native 位图 shape,并用 requestAnimationFrame 驱动 overlay 更新;动画光标与更高层系统语义映射仍未单独处理
  • 远程桌面当前 clipboard 仍有已知边界:
    • 只支持纯文本,不支持文件、图片、富文本
    • 文本同步当前基于会话级 allow_clipboard 开关和浏览器侧模式选择,不是 Aspia 式的更完整原生 clipboard 会话协议
    • 当前对文本大小做了保守上限,超过上限会跳过同步并回传状态
  • 远程桌面 display / topology 当前仍有剩余边界:
    • 还没有 Aspia 那种 ScreenList 持续推送与 current_screen / primary_screen / resolution 的完整会话协议
    • 还没有 full desktop 捕获、系统级分辨率切换、无缝热切换与多客户端协调
  • 前端仍存在大包优化空间

相关文档