昨天用 Python + PySide2 发布了 agent-sudo 的初始版本。queue + flush 模式跑通了,一个下午从零到能用的原型,挺有成就感。
但今天再回头看,代码组织、性能、分发都存在明显的天花板。于是我通宵把它全量重写了一遍。

为什么重写
v1 用 Python 做原型是刻意的选择——半天写完、能跑、能演示。但在昨天发完博客后,几个问题开始变得明显:
依赖地狱。用户需要安装 PySide2,而这个包在某些发行版上根本装不上(Qt5 的 Python binding 与系统 Qt 版本不兼容、PyPI wheel 不覆盖某些架构)。一个”零配置”的工具,安装第一步就先卡住。
分发沉重。Python 脚本意味着用户必须有一个完整的 Python 运行时,且路径需要符号链接到 /usr/local/bin。Agent 调用时可能切换了虚拟环境或者 PATH 混乱,导致 agent-sudo 根本找不到。
GUI 组件拼凑。PySide2 的 API 是命令式的,每增加一个组件都要在 __init__ 里塞一大堆 QPushButton、QLineEdit、connect 语句。随着 60 秒倒计时、LLM 输入区、主题切换、危险标注这些功能加进来,main.py 膨胀到了六百多行,维护成本直线上升。
Python 自身的限制。ctypes.memset 清零密码是一个运行时操作,编译器无法静态保证它一定被执行。多线程的密码管道读写(写入 sudo 的 stdin 后关闭写端让 sudo 读到 EOF)在 Python 里需要精心处理 QThread 和 closeWriteChannel 的时序关系,容易写出竞态。
重写目标
| 目标 | 说明 |
|---|---|
| 零运行时依赖 | 编译成单个 ELF 二进制,不依赖 Python 或任何解释器 |
| 类型安全 | C++ 编译期类型检查,消除 category 错配、信号/槽类型不对等 bug |
| 声明式 UI | 用 creeper-qt 声明式组件库替代命令式 PySide2,UI 结构即代码结构 |
| 模块化 | 拆分 main.cc(511 行)与 gui/ 下的独立模块,每个模块职责单一 |
| Qt6 原生 | 使用 Qt6 的 Widgets、Network、Svg、Multimedia 模块,不再受 Qt5 兼容限制 |
| 可审计 | 编译选项可见、构建脚本可复现、产物可校验 |
架构变化
v1 是一个 main.py 单文件。v2 拆分成了清晰的模块结构:
src/
├── main.cc # argv[0] 分派 + creeper-qt 声明式 UI
├── gui/
│ ├── command_card.{h,cc} # 命令卡片构建器(含危险检测)
│ ├── ring_countdown.{h,cc}# 36×36 环形倒计时
│ ├── icon.{h,cc} # Material Icons + 应用 SVG
│ ├── sound.{h,cc} # QSoundEffect 声音管理器
│ ├── theme_transition.{h,cc} # ColorScheme 平滑插值
│ ├── queue_io.{h,cc} # JSON 队列/历史 I/O
│ ├── executor.{h,cc} # sudo 子进程执行器
│ └── tr.h # JSON 驱动 i18nUI 层没有从零手写 Qt6 Widget 代码,而是基于一个优秀的声明式组件库:
creeper-qt 把 Qt 的命令式 Widget 构建转换为声明式的嵌套组件树——窗口、卡片、按钮、输入框的层级关系直接对应代码的嵌套结构。主题系统(3 套预设 + 亮暗模式切换)和 MixerMask 展开动画也直接由 creeper-qt 的 ThemeManager 和 TransitionValue 驱动,不需要自己写一帧帧的过渡逻辑。
感谢 @creeper5820 的优秀工作,src/gui/creeper-qt/ 为 vendored 源码,遵循 MIT 许可。
单二进制通过 argv[0] 检测实现分派:调用 agent-sudo 走入队逻辑,调用 agent-sudo-flush 走弹窗逻辑,两者是同一个 ELF 的符号链接。Agent 不需要关心这是 Python 还是 C++ —— agent-sudo --reason "..." -- <cmd> 的行为完全不变。
新技术栈
| 维度 | v1 (Python) | v2 (C++23) |
|---|---|---|
| 语言 | Python 3.8+ | C++23 (GCC 14+) |
| GUI | PySide2 (Qt5) | creeper-qt + Qt6 |
| 构建 | pip install | CMake 3.22+ |
| 依赖 | PySide2, Python | Qt6, Eigen3, CMake |
| 产物 | .py 脚本 + 符号链接 | 单个 ELF 二进制 |
| 密码清零 | ctypes.memset | std::fill + clear() |
| i18n | 硬编码中文 | 8 语言 JSON 驱动 |
v2 新增功能
在重写过程中,借着架构的灵活性顺手加了不少新功能:
危险检测。每条命令自动分析字符串内容并标注安全等级:常规 apt install 标绿色 ✓,systemctl stop/kill/chmod 标黄色 ⚠,rm -rf/dd if=/mkfs 标红色 ✕。纯字符串匹配,不需要 Shell 解析,但 Agent 无法绕过标注。
主题系统。3 套预设配色(BlueMiku/GoldenHarvest/Green)+ 亮/暗模式。切换时 ColorScheme 平滑插值动画,不是硬切。点击窗口右上角的调色板图标就能轮换,设置自动写入 ~/.config/agent-sudo/theme.conf。
声音反馈。4 种事件音效(窗口打开/10 秒警告/执行成功/审批拒绝),每个事件可独立设为 default/none/自定义 WAV。QSoundEffect 实现,不依赖外部播放器。
多语言。8 种语言(zh/en/ja/ko/fr/de/es/pt),JSON 驱动。中文为内置默认,其余通过 Qt 资源文件加载。翻译条目覆盖所有 UI 文案,包括危险检测的等级标签和按钮文字。
Material Icons。用 Material Icons Round 字体渲染所有图标(codepoint 引用),不再依赖系统图标主题。自定义应用 SVG 图标用于窗口标题栏和任务栏。
倒计时暂停。60 秒倒计时增加暂停/恢复按钮,人在来不及操作时可以暂停计时器。
10 秒警告。倒计时最后 10 秒触发警告音效,提醒人尽快操作。
认证失败处理。自动检测中文和英文的 sudo 密码错误提示,失败后清空队列并返回退出码 126,不重试。
输出过滤。执行结果自动过滤 [sudo] 和 sudo: 噪音行,Agent 收到的输出更干净。
构建与安装
cmake -S . -B build && cmake --build build
sudo cp build/agent-sudo-flush /usr/local/bin/
sudo ln -sf /usr/local/bin/agent-sudo-flush /usr/local/bin/agent-sudo编译依赖 GCC 14+、Qt6(Widgets/Network/Svg/Multimedia)、Eigen3、CMake 3.22+。产物是单个约 490KB 的二进制文件,无动态链接的 Python 依赖。
对于不想自己编译的用户,INSTALL.md 提供了预编译二进制下载和安装步骤。
结语
v2 重写熬了一整夜,核心动机不是”Python 不够好”,而是”这个工具已经到了需要独立分发的阶段”。C++ 的单二进制分发、编译期类型安全、以及 creeper-qt 的声明式 UI 模式,让代码的组织和维护都上了一个台阶。
Python 原型仍然有价值——它在半天内验证了 queue + flush 模式的可行性。没有这个原型,v2 不可能在一个通宵内完成。但到了需要独立安装、独立运行、独立交付的阶段,C++ 是更自然的选择。
项目以 MIT 协议开源在 GitHub,欢迎试用和反馈。