Repository navigation
Configuration zh
English · 中文
对应 v0.12.0(
3a762b1)。实现在src/config.rs(load_config/merge)。 完整字段列表见仓库根的config.toml.example。
nanopi 从 v0.6(fd7cdd0)起就是两级配置,两个文件都是可选的。
| 层级 | 路径 |
|---|---|
| 全局 | ~/.nanopi/config.toml |
| 项目 | <cwd>/.nanopi/config.toml |
NANOPI_HOME 环境变量可以改变全局那一层的根目录(主要用于测试隔离)。
完整优先级,从高到低:
命令行 --flag > OPENAI_* 环境变量 > 项目配置 > 全局配置 > 默认值
这是最容易踩的地方。merge() 里有三类行为:
model, base_url, api_key, api_key_file, api_kind, provider, trust.default
Option 语义:None 表示"未设置",不会覆盖下层的值。
[[extensions]]
[hooks] 的所有事件
skills.disabled / skills.extra_dirs
源码注释说得很直白:"Extensions concatenate (both additive, never override)"、"hooks are policies, not overrides"。
实际影响:全局装的插件和项目装的插件会同时加载。 你没办法在项目配置里"关掉"某个全局插件,只能再加。两个插件若导出同名工具,后者会因工具名冲突被跳过并告警。
hooks 同理——项目里加的 hook 不会替换全局 hook,两边都会跑。
tool_exec_mode:显式设置的(非默认值)赢;默认是 Parallel,用户 opt-in 到 Sequential。
如果你在 nanopi 仓库自己的目录下测试,注意仓库里有一个 .nanopi/config.toml。它是项目级配置,会覆盖你的全局设置——包括 base_url 和 model。
症状:你在 ~/.nanopi/config.toml 里改了 endpoint,启动却发现连的是别的地址。
要么改项目那份,要么换个工作目录测试。
[[extensions]]
path = "~/.nanopi/extensions/my-tool.wasm"~ 展开的是 $HOME,不认 NANOPI_HOME。
也就是说,即使你设了 NANOPI_HOME=/tmp/test(config.toml 确实会从那里读),上面这个 path 仍然解析到 $HOME/.nanopi/extensions/,于是:
nanopi: skipping extension (not found): /root/.nanopi/extensions/my-tool.wasm
插件路径建议一律写绝对路径。
# A. 文件(推荐)—— 内容就是 token,建议 chmod 600
api_key_file = "~/.nanopi/api_key"
# B. 内联(本地开发方便)—— 启动会打印 stderr 警告,防止误提交
api_key = "sk-..."# C. 环境变量,优先级高于配置文件
export OPENAI_API_KEY=sk-...内联那种会在每次启动时告警:
⚠ api_key is inline in config.toml — consider api_key_file or OPENAI_API_KEY env var
[[extensions]]
path = "/abs/path/plugin.component.wasm" # 或目录,向下扫一层
max_files = 64 # path 是目录时的上限,默认 64
allow_fs = false # host-fs-read
allow_network = false # host-http-get
url_allowlist = [] # host-http-get 能到的 host;空 = 拒绝一切
allow_store = false # host-store-get / host-store-set (v0.12)
allow_context = false # host-set-context (v0.12)
allow_tools = [] # host-call-tool,**按工具名逐个给** (v0.12)
allow_send_message = false # host-send-user-message (v0.12)
events = [] # 这个插件可以**观察**的生命周期事件 (v0.12)上面每一个默认值都是关闭的那一侧。四个布尔量默认 false,三个列表默认 []——而 [] 的含义是拒绝一切,不是"允许一切"。
| 字段 | 默认 | 授予什么 |
|---|---|---|
allow_fs |
false |
读工作目录以内的文件 |
allow_network + url_allowlist
|
false、[]
|
只对 allowlist 里的 host 发 HTTP(S) GET |
allow_store |
false |
一份跨重启存活的键值 JSON store |
allow_context |
false |
每个 turn 折进模型 system prompt 的一段文字 |
allow_tools |
[] |
调用列出来的内置工具(bash、edit、find、grep、ls、read、write) |
allow_send_message |
false |
发起或者带偏一个 turn——这一个是花钱的 |
events |
[] |
按 PI 的事件名观察生命周期事件 |
allow_tools 是列表而不是布尔量,这是刻意的:allow_tools = ["bash"] 就是任意命令执行,它会直接绕过 allow_fs 的工作目录限制和 url_allowlist 的逐 host 审批,让这个插件身上其他所有授权都变成装饰;而 allow_tools = ["find", "read"] 表达的是"可以走、可以读,不可以写、不可以执行"——这个区别没有任何一个"每插件一个开关"能表达。列表里写了不是内置工具的名字,对这个插件是加载错误,报错会列出合法的那几个。
path 指向目录时会非递归扫描一层,把其中的 .wasm 都装上。
[[extensions]] 带着 #[serde(deny_unknown_fields)]。所以下面这份配置起不来:
[[extensions]]
path = "/abs/path/plugin.wasm"
allow_stroe = true # ← 配置加载失败,并点出这个字段它挡住的是那种你看不见的故障:v0.12 之前这个拼写错误能正常解析,什么都没授予,而用户以为自己的插件有 store 了。一个"要了但没拿到"的授权必须响亮,否则症状会以插件内部莫名其妙的 error: store access denied 出现——看起来像是插件自己的 bug。
注意这里的不对称:未知的键是硬性的配置错误,而 events 列表里面的未知事件名只是插件加载时的一条告警,并会列出合法名字。这是刻意的:[[extensions]] 整体在标准(不带 wasm feature 的)二进制里必须保持"忽略并告警",所以 events 里的任何东西都不能被允许中断配置加载。
allow_network 打开后,插件的 host-http-get 走的是宿主进程的网络栈,也就跟着 nanopi 进程的 http_proxy / https_proxy 走。所以在受限网络后面:
- 插件不需要自己实现代理,配好环境变量就够了;
- 但
host-http-get10 秒超时且不跟随重定向,走代理的一次请求很容易踩到这两条中的第一条,而失败是以 in-band 的error:字符串回给插件的,不是 trap——插件要自己处理,不要假设一定成功; -
url_allowlist匹配的是 URL 解析出来的 host,代理地址不参与匹配,所以配置里写的仍然是目标站点的域名。
-
[hooks.*]每个事件的细节 → 生命周期 hooks - 写插件 → Plugin-Development-zh
- 能力门 → 能力门
- 让插件观察生命周期事件 → 插件事件
English
- Home
- Features
- Configuration
- Hooks
- Plugin Development
- Example Plugins
- Plugin Capabilities
- Plugin Events
- Debugging Plugins
中文
v0.12.0 · source of truth:
wit/nanopi-extension.wit