本文面向想了解 ifu Agent 内部实现、或希望为其扩展能力的开发者。它采用清晰的模块化架构,全部依赖为无 cgo 库,支持 CGO_ENABLED=0 交叉编译。
1. 架构总览
cmd/main.go 入口:参数解析、运行模式选择(前台/服务)、功能自检打印
├── internal/service/ 服务管理:Windows SCM / Linux systemd + ServerManager(三协议服务器生命周期)
│ └── internal/server/ HTTP (:42000) / TCP (:42001) / UDP (:42002) 服务器
│ ├── internal/power/ 命令分发核心:电源策略 + 扩展控制命令路由
│ │ ├── internal/control/ 桌面控制实现(音量/屏幕/键盘/鼠标/剪贴板)
│ │ └── internal/capability/ 功能可用性检测
│ └── internal/discovery/ UDP 设备发现
└── pkg/
├── types/ 协议结构(PowerCommand/CommandResponse)、常量
└── logger/ 日志(按日切分 + 10MB 大小轮转)
命令流转:HTTP/TCP/UDP 服务器 → PowerController.ExecuteCommand → MAC 验证 → 电源命令 | 扩展控制命令
2. 模块接口
2.1 pkg/types — 协议类型
PowerCommand:命令请求,Cmd/MAC+ 扩展参数Value/Text/Key/Mods/X/Y/Button/ScrollCommandResponse:命令响应,Status/Message+ 查询返回值DataPowerController:电源控制器接口(ExecuteCommand/GetLocalMAC/GetMACByIP/ValidateMAC)Server/ServerManager/ServiceManager/DiscoveryService/Logger:服务器、服务、发现、日志接口
命令常量定义在 pkg/types/constants.go(Cmd* 系列,与命令协议文档一致)。
2.2 internal/control — 桌面控制
四个控制器接口:
type VolumeController interface {
GetVolume() (int, error) // 0-100
SetVolume(volume int) error // 0-100,越界报错
GetMuted() (bool, error)
SetMuted(muted bool) error
}
type ScreenController interface {
TurnOff() error
TurnOn() error
}
type InputController interface { // 键盘 + 鼠标
KeyTap(key string, mods []string) error
KeyType(text string) error // Unicode 文本
MouseMoveTo(x, y int) error
MouseClick(button string) error // left/right/middle
MouseWheel(detents int) error
}
type ClipboardController interface {
Read() (string, error)
Write(text string) error
}
平台实现通过 build tag 拆分,无 cgo 交叉编译验证通过:
| 文件 | 构建标签 | 实现 |
|---|---|---|
volume_windows.go |
windows | go-wca(Core Audio COM,纯 Go) |
volume_linux.go |
linux | pactl 命令(PulseAudio/PipeWire-pulse) |
screen_windows.go |
windows | user32 SC_MONITORPOWER(syscall) |
screen_linux.go |
linux | xset dpms(仅 X11) |
input.go |
全部 | makc(Windows SendInput / Linux uinput / macOS CGEvent) |
clipboard.go |
全部 | atotto/clipboard(Linux 需 xclip/xsel/wl-copy) |
2.3 internal/capability — 功能自检
type Feature struct {
Name string `json:"name"`
Available bool `json:"available"`
Reason string `json:"reason,omitempty"`
}
type Capabilities struct {
Features []Feature // 固定顺序: power, volume, screen, keyboard, mouse, clipboard
}
func Check() *Capabilities
func (c *Capabilities) Available(name string) bool
func (c *Capabilities) Reason(name string) string
- Windows 检测:
ProcessIdToSessionId判断交互会话(Session 0 = 服务模式,交互类功能不可用) - Linux 检测:euid(电源)、
pactl(音量)、DISPLAY+xset/Wayland(屏幕)、/dev/uinput写权限(键盘/鼠标)、xclip/xsel/wl-copy(剪贴板) - 不变量:
Available=true时Reason为空;false时Reason必须说明原因
使用位置:前台启动打印、Windows 服务启动日志、GET /capabilities、命令失败提示。
2.4 internal/power — 命令分发
NewPowerController():工厂创建,注入音量/屏幕/剪贴板控制器;输入控制器初始化失败仅告警不致命(命令执行时返回不可用原因)ExecuteCommand(cmd):MAC 验证(懒加载,sync.Once并发安全)→ 扩展命令路由 → 电源策略(Windowsshutdown/ Linux 多命令回退 + D-Bus)
3. 扩展指南
3.1 新增一个命令(以 brightness_set 为例)
- 常量:
pkg/types/constants.go添加CmdBrightnessSet = "brightness_set" - 参数:若需要新参数类型,在
PowerCommand添加字段(含json:"...,omitempty") - 处理器:
internal/power/controller.go的executeControlCommand添加 case,实现handleBrightnessSet(参考handleVolumeSet:nil 控制器走controlUnavailable) - 控制器:
internal/control/control.go定义接口(如BrightnessController),平台文件实现,NewPowerController注入 - 能力检测:
internal/capability添加checkBrightness()与FeatureBrightness常量 - 测试:
internal/power/controller_test.go添加分发与错误用例;control 包添加解析函数单测 - 文档:命令协议文档命令表 + README 命令表同步
3.2 新增一个平台实现
- 新文件加
//go:build windows或//go:build linux(含+build兼容行) - 工厂函数在平台文件内定义,跨平台文件只引用接口
- 交叉编译验证:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ./cmd
4. 测试
| 包 | 测试文件 | 覆盖内容 |
|---|---|---|
pkg/types |
types_test.go |
JSON 编解码、omitempty、Data 字段、命令常量一致性 |
internal/control |
input_test.go |
按键/修饰键/鼠标按钮名称解析、keyMap 完整性 |
internal/control |
volume_test.go |
pactl 音量输出解析(含异常格式) |
internal/capability |
capability_test.go |
结构完整性、顺序稳定、Available/Reason 不变量 |
internal/power |
controller_test.go |
命令分发路由、nil 控制器错误提示、查询类 Data 返回、MAC 验证路径 |
运行:go test ./...
真实设备注入(按键/鼠标落到桌面)、真实音量/剪贴板修改不纳入单元测试(会干扰本机桌面),由手工冒烟测试覆盖。
5. 已知限制
| 功能 | 限制 |
|---|---|
| 键盘/鼠标 | 需要交互桌面会话(服务模式 Session 0 不可用);Linux 需 root 或 /dev/uinput 权限 |
| 剪贴板 | Linux 运行时依赖 xclip/xsel(X11)或 wl-copy/wl-paste(Wayland) |
| 屏幕开关 | Linux 仅 X11(Wayland 不支持 xset dpms) |
| 音量 | 作用于默认播放设备(Linux 为 @DEFAULT_SINK@) |
| 电源 | 前台模式需关机权限(Linux 需 root);服务模式天然具备 |
继续阅读
- ifu Agent 通信协议与 API 详解—— 命令表与协议细节,扩展时需保持一致。
- ifu Agent 是什么?—— 产品定位与核心功能。
- ifu Agent 常见问题与排查—— 运行时问题排查。