跳转至

扩展 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 &lt;name&gt; 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。