扩展 CLI 入口 xune¶
扩展除了往智能体上挂工具、命令与钩子,还可以声明一套自己的命令行,由 xune 直接运行——
适合报表、数据回填、预算设置这类「和智能体无关,但和扩展数据有关」的运维操作。
xune # 打印用法,并列出声明了 CLI 的扩展
xune <扩展名> [参数…] # 运行该扩展的 CLI
xune <扩展名> -h # 该扩展自己的 --help
xune stat show --days 7
xune stat budget set --day 500 --user alice
参数与退出码¶
xune 只有一个位置参数——扩展名,其余参数原样交给扩展自己的解析器。
| 情况 | 输出 | 退出码 |
|---|---|---|
| 无参数 | 用法 + 带 CLI 的扩展列表 | 2 |
-h / --help |
同上 | 0 |
| 扩展未加载 | unknown extension '<名字>' — loaded: … |
2 |
| 扩展未声明 CLI | extension '<名字>' declares no CLI |
2 |
| 正常执行 | 由扩展决定 | 处理函数返回 int 即退出码,否则 0 |
它是怎么找到扩展的¶
xune 复用核心的扩展加载器,从 $XUN_HOME/extensions/ 扫描并导入扩展模块,
读取 @extension_attr(cli=(parser, handler)) 声明的解析器与处理函数:
flowchart LR
A["xune <name> args"] --> B["扫描并导入<br/>$XUN_HOME/extensions"]
B --> C{"该扩展声明了 cli?"}
C -- 否 --> X["提示并退出 2"]
C -- 是 --> D["parser.parse_args(args)"]
D --> E["handler(namespace)"]
E --> F["返回值即退出码"]
两点值得注意:
- 只导入,不 setup。
xune不会执行setup_extension(ctx),因此不会往任何智能体上挂东西; 需要初始化的资源(建库、建目录)应在 CLI 处理函数里自己做。 - 数据目录同一套规则。 因为是核心进程在导入扩展,
extension_data_dir()解析出的路径与智能体看到的一致, 不需要额外设置PYTHONPATH或模块搜索路径。
声明方式¶
cli 是一个 (argparse.ArgumentParser, handler) 二元组,处理函数接收解析后的 Namespace:
import argparse
from xun import ExtensionContext, extension_attr
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument("--days", type=int, default=7)
return parser
def run(args) -> int:
print(f"last {args.days} days")
return 0
@extension_attr(api_min_version="1.4.0", data_version="1", cli=(build_parser(), run))
def setup_extension(ctx: ExtensionContext) -> None:
...
解析器的 prog 会被改写为 xune <名字>,所以 --help 里显示的就是用户实际敲的命令。
内置的 stat 扩展即用此机制提供报表与预算命令(xune stat show|budget …),
详见 Extension Attributes。