首页
/ CLI-Anything AdGuardHome Harness:通过 cli-anything-adguardhome 用命令行与 Agent 控制 AdGuardHome

CLI-Anything AdGuardHome Harness:通过 cli-anything-adguardhome 用命令行与 Agent 控制 AdGuardHome

2026-09-05 11:49:27作者:羿妍玫Ivan

本文以 adguardhome/agent-harness/cli_anything/adguardhome/README.md 为主体,讲解 CLI-Anything 项目中为 AdGuardHome 构建的 CLI harness:从前置依赖、安装与连接配置,到过滤规则、DNS 重写、客户端、统计与日志的完整操作命令,并结合 源码实现 深入 API 客户端层、配置解析优先级与错误处理机制。读完本文,你将能在终端或 Agent 工作流中直接管理一个正在运行的 AdGuardHome 实例,并理解每条命令背后对应的 REST API 调用链。

定位与前置依赖

cli-anything-adguardhome 是 CLI-Anything 项目"Making ALL Software Agent-Native"理念在 AdGuardHome 上的落地:它是一个 CLI harness,让你可以从命令行(或经由 Agent)控制广告拦截器,而不是打开 Web 界面点点点。根据配套的 SOP 文档 ADGUARDHOME.md,其控制对象是 正在运行的 AdGuardHome 实例暴露的 REST HTTP API(默认 58 个端点、14 个 tag 分组,Basic Auth 保护),而不是某个本地二进制文件——CLI 的职责是"生成结构化命令 → 调用真实 API → 校验响应"。

因此第一个前提是 AdGuardHome 必须在运行。README 给出的两种部署方式:

# Linux - native
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

# Docker
docker run --name adguardhome -p 3000:3000 adguard/adguardhome

注意 Docker 方式中容器 Web 界面监听 3000 端口,这个端口需要保留在 setup wizard 中——后端客户端 在连接失败抛出的 RuntimeError 信息里也内嵌了同样的部署提示,方便 Agent 在报错时自我修复。

安装

进入 harness 目录以可编辑模式安装:

cd agent-harness
pip install -e .
cli-anything-adguardhome --help

setup.py 声明了入口点 cli-anything-adguardhome=cli_anything.adguardhome.adguardhome_cli:main,依赖为 click>=8.0.0prompt-toolkit>=3.0.0requests>=2.28.0,要求 Python >= 3.10,并把 skills/*.md 作为包数据一并打包——这意味着安装后 Agent 可以直接读取 skills/SKILL.md 获得该 harness 的技能描述。

连接配置:环境变量、配置文件与解析优先级

README 支持两种配置方式。一是环境变量:

export AGH_HOST=localhost
export AGH_PORT=3000
export AGH_USERNAME=admin
export AGH_PASSWORD=secret

二是把连接参数写入配置文件(默认路径为 ~/.config/cli-anything-adguardhome.json):

cli-anything-adguardhome --host localhost --port 3000 --username admin --password secret config save

core/project.pyload_config 揭示了完整的解析逻辑,四层优先级从高到低:

  1. CLI flags--host--port--username--password(在根命令中 host or cfg["host"] 覆盖,见 adguardhome_cli.py);
  2. 环境变量AGH_HOSTAGH_PORTAGH_USERNAMEAGH_PASSWORD,只要设置就覆盖文件值;
  3. 配置文件:默认 ~/.config/cli-anything-adguardhome.json,可被 --config 指定其他路径;文件损坏(JSONDecodeError/OSError)时静默回退,不会中断;
  4. 默认值localhost:3000,匿名(无 Basic Auth)。

config 子命令组提供三个操作(源码 L164-L206):

  • config show:显示当前连接设置,密码输出时掩码为 ***
  • config save:把当前生效的 host/port/username/password/https 写入配置文件,返回保存路径;
  • config test:真实调用一次 server status(即 GET /status)验证连通性,成功时输出 {"connected": true, ...}

基本用法:REPL 与一次性命令

不带子命令直接运行即进入交互式 REPL(这是默认行为,根 group 声明了 invoke_without_command=True,无子命令时 ctx.invoke(repl),见 adguardhome_cli.py)。README 中的典型用法:

# Interactive REPL (default)
cli-anything-adguardhome

# One-shot commands
cli-anything-adguardhome server status
cli-anything-adguardhome filter list
cli-anything-adguardhome --json stats show

--json 是根级 flag,配合 output() 实现双模输出:JSON 模式下 json.dumps(..., indent=2) 全量输出;人类可读模式下 dict 逐行 key: value、list 逐条输出。

README 覆盖的实战命令族:

# Filtering
cli-anything-adguardhome filter add --url https://somehost.com/list.txt --name "My List"
cli-anything-adguardhome filter refresh

# DNS rewrites
cli-anything-adguardhome rewrite add --domain "myserver.local" --answer "192.168.1.50"
cli-anything-adguardhome rewrite list

# Clients
cli-anything-adguardhome clients add --name "My PC" --ip 192.168.1.100

# Stats
cli-anything-adguardhome stats show
cli-anything-adguardhome stats reset

完整命令地图

REPL 内输入 help 会打印与下面一致的命令树(repl 命令 L130-L142),与 SOP 文档中的 CLI Command Map 对应:

cli-anything-adguardhome
├── config show / save / test
├── server status / version / restart
├── filter list / add / remove / enable / disable / refresh / status / toggle
├── blocking parental status/enable/disable
│          safebrowsing status/enable/disable
│          safesearch status/enable/disable
├── blocked-services list / set
├── clients list / add / remove / show
├── stats show / reset / config
├── log show / config / clear
├── rewrite list / add / remove
├── dhcp status / leases / add-static / remove-static
└── tls status

补充几个 README 未逐一展开、但源码中可复制可运行的命令(参数均直接取自对应 click 装饰器):

# 全局过滤开关(on/off)
cli-anything-adguardhome filter toggle on
cli-anything-adguardhome filter remove --url https://somehost.com/list.txt

# 家长控制 / 安全浏览 / 安全搜索
cli-anything-adguardhome blocking parental status
cli-anything-adguardhome blocking safebrowsing enable
cli-anything-adguardhome blocking safesearch disable

# 阻止服务分类(位置参数,可多个)
cli-anything-adguardhome blocked-services list
cli-anything-adguardhome blocked-services set analytics ads

# 查询日志:默认 limit=50、offset=0
cli-anything-adguardhome log show --limit 100 --offset 0
cli-anything-adguardhome log config --enabled --interval 30
cli-anything-adguardhome log clear

# 统计保留天数(days),不带 --interval 则只查询配置
cli-anything-adguardhome stats config --interval 7

# DHCP 静态租约
cli-anything-adguardhome dhcp status
cli-anything-adguardhome dhcp leases
cli-anything-adguardhome dhcp add-static --mac AA:BB:CC:DD:EE:FF --ip 192.168.1.10 --hostname "nas"

# TLS 配置状态
cli-anything-adguardhome tls status

API 客户端层:AdGuardHomeClient

所有命令最终汇聚到 utils/adguardhome_backend.py 中的 AdGuardHomeClient,它把全部 REST 调用封装在 requests.Session 之上。关键实现细节(L11-L27):

  • base URL 构造{scheme}://{host}:{port}/control;当端口为 80/443 时省略端口段。这与 AdGuardHome API 固定挂载在 /control 路径下、且 Web 界面端口即 API 端口的事实一致;
  • HTTPS 自动探测https 参数为 True 时使用 https;即使未显式指定,端口为 443 时强制升级为 https
  • 认证:只要 username 或 password 非空就设置 session.auth(HTTP Basic Auth),请求头统一为 Content-Type: application/json
  • 响应处理:空响应体返回 {},非 JSON 内容降级为原始文本,避免对空 body 的端点(如 /restart)解析失败;
  • 超时:GET/POST 均为 10 秒超时;
  • POST 多形态:dict/list 走 JSON body,str 走 text/plain 编码,None 则发送空 POST(adguardhome_backend.py L55-L69)。

连接失败时 ConnectionError 被统一转成带可操作建议的 RuntimeError(含安装命令与 Docker 命令),这保证了人与 Agent 都能从报错信息中恢复。

命令到 API 端点的调用链

每个命令组都是一个薄的"CLI → core 模块 → API 端点"映射。以 README 中出现的四条命令为例:

命令 core 模块函数 AdGuardHome API 端点
server status server.get_status GET /control/status
filter list filtering.get_status GET /control/filtering/status
filter add filtering.add_filter POST /control/filtering/add_url(body: name/url/whitelist
filter refresh filtering.refresh POST /control/filtering/refresh
rewrite add core/rewrite.py POST /control/rewrite/add
stats show / stats reset stats.get_stats / reset_stats GET /control/stats / POST /control/stats_reset
log show log.get_log GET /control/querylog?limit=&offset=

有两个值得注意的实现细节:

  1. filter toggle 是读-改-写filtering.set_enabledGET /filtering/status 读出当前 interval(缺省 24 小时),再把 {"enabled": ..., "interval": ...} 整体 POST 到 /filtering/config——避免切换开关时把更新周期重置掉;
  2. stats config / log config 支持"只读探测"模式:不带写参数时仅 GET 配置(stats_config / querylog_config),带参数时才 POST 更新(log.py L14-L20 的 set_log_config 固定附带 anonymize_client_ip: False)。

core/ 目录下的模块划分与 API tag 组一一对应:blocking.py(parental/safebrowsing/safesearch/blocked_services)、clients.pyrewrite.pydhcp.pystats.pylog.pyserver.py(含 GET /tls/status),另有 session.pyproject.py。从源码结构看,server restartPOST /control/restart,会中断当前连接)与 tls status 是刻意保持只读/低频的操作。

错误处理与 Agent 友好的退出路径

main() 入口 捕获 RuntimeErrorrequests.exceptions.RequestException,行为取决于调用方式:

  • --json 时输出 {"error": "<message>"} 并以退出码 1 结束——保证 Agent 的管道永远拿到可解析的 JSON,而不是空输出加 traceback;
  • 否则把错误消息写入 stderr 后退出。

源码注释明确解释了这一点:后端失败若不被捕获,会表现为链式 traceback,并且 --json 模式将产生不可解析的输出。REPL 模式下同类异常则被 repl 循环 逐行捕获并通过 ReplSkin.error() 显示,不会让会话崩溃——UsageErrorRuntimeErrorSystemExit 各有分支。

测试策略与验证方式

README 给出的三条测试命令:

cd agent-harness
python3 -m pytest cli_anything/adguardhome/tests/test_core.py -v
python3 -m pytest cli_anything/adguardhome/tests/test_full_e2e.py -v -s
CLI_ANYTHING_FORCE_INSTALLED=1 python3 -m pytest cli_anything/adguardhome/tests/ -v -s

tests/TEST.md 和 SOP 文档的 Testing Strategy 一致,分三层:

  1. 单元测试tests/test_core.py):用 unittest.mock 打桩 requests.Session,无需真实 AdGuardHome。例如 TestAdGuardHomeClient 验证了默认 base_url == "http://localhost:3000/control"、Basic Auth 设置(session.auth == ("admin", "pass"))、URL 拼接(/statushttp://192.168.1.1:8080/control/status)、空响应返回 {} 等契约;
  2. E2E 测试tests/test_full_e2e.py):通过 Docker 拉起 adguard/adguardhome 镜像,使用 3001 端口做隔离,跑完整命令链路;
  3. 子进程测试CLI_ANYTHING_FORCE_INSTALLED=1 环境变量触发 _resolve_cli("cli-anything-adguardhome") 逻辑,直接测试安装后的 CLI 二进制本身。

小结

cli-anything-adguardhome 展示了 CLI-Anything 的典型 harness 形态:一个薄而完整的 Click 应用(adguardhome_cli.py),把 AdGuardHome 的 /control REST API 映射成 14 个命令组;连接配置支持 flag/环境变量/配置文件/默认值四级回退(core/project.py);--json 双模输出与结构化错误路径使其既可被人直接执行,也可被 Agent 在自动化管道中可靠调用。适用前提:AdGuardHome 实例已运行、Basic Auth 凭据可获取、网络可达;所有端点行为以 AdGuardHome 自身 API 为准,harness 仅做封装而不修改服务端。

登录后查看全文
热门项目推荐
相关项目推荐