Repository navigation
Example Plugins zh
English · 中文
对应 v0.12.0(
8dd8ae9)。下面每一个工具、命令、事件和 grant 都是从插件源码里读出来的,不是凭记忆写的。 源码在仓库examples/下;若与本页不符,以仓库为准。
三个演示机制,两个是拿来装了真用的。
| 插件 | 作用 | 工具 | 命令 | 事件 |
|---|---|---|---|---|
wasm-plugin-minimal |
拿来复制的骨架。 还能体现能力门的最小插件 —— 一个纯计算工具、一个受门控的,用一句标记注释把「样板」和「你的部分」分开。 |
greet、fetch_head
|
— | — |
wasm-plugin |
参考实现。 四个工具、斜杠命令,以及三种 command action(print / send_user_message / error)全都演示到。 |
wordcount、readfile、fetch、rot13
|
/todo、/explain
|
— |
wasm-plugin-events |
事件订阅,以及「两份清单必须都同意」:插件自己的 list-events 和 配置里的 events 缺一不可,否则一个事件都不投递。另外它也带着断路器测试要用的 trap/spin fixture。 |
events_seen、busy、greet
|
(无 —— 一个 [] 桩,阶梯的代价) |
tool_execution_start、turn_start、input
|
wasm-plugin-memory |
持久化项目记忆。 每条记忆是 .nanopi/memory/ 下一个 markdown 文件,可读、可手改、可 diff、可提交。注入模型上下文的只有那份精简索引,全文由模型判断相关后调 recall 取。 |
remember、recall、forget
|
/memory |
— |
wasm-plugin-report |
时间花在哪了。 跨会话记录每个工具的调用次数、耗时和失败。它能存在的理由是:tool_execution_end 带的那个 duration_ms 别处一概不持久化 —— 所以「刚才为什么慢」「什么老在失败」本来根本没有答案。 |
tool_stats |
/tools-report |
tool_execution_end、session_start
|
从 wasm-plugin-minimal 开始。 它就是为了被复制而写的:YOUR TOOLS 标记以上是逐字保留的样板,以下是你的。需要命令或第二个能力时再看 wasm-plugin。
想看能力被真正用起来而不是被演示,读 wasm-plugin-memory 和 wasm-plugin-report。 这两个是刻意互补的:加起来把整个 outbound surface 覆盖了(见 能力门)。
插件拿不到任何没被授予的东西。这些写在它的 [[extensions]] 条目里;每个插件自己的 README 都给了完整的配置块。
| 插件 | grants | 实际调用的 host import |
|---|---|---|
wasm-plugin-minimal |
allow_network + url_allowlist
|
host-log、host-http-get
|
wasm-plugin |
allow_fs、allow_network + url_allowlist
|
host-log、host-fs-read、host-http-get
|
wasm-plugin-events |
events = [...] |
host-log |
wasm-plugin-memory |
allow_fs、allow_tools = ["write"]、allow_context
|
host-log、host-fs-read、host-notify、host-set-context、host-call-tool
|
wasm-plugin-report |
allow_store、events = [...]
|
host-log、host-notify、host-store-get、host-store-set
|
表里有两处特别容易搞错:
-
wasm-plugin-minimal不需要allow_fs,尽管它源码里声明了host-fs-read。那句声明挂在#[allow(dead_code)]后面、没有任何地方调用,所以 LTO 会把这个 import 丢掉,构建出的 world 里根本没有它。要确认一个插件真正 import 了什么,对组件跑wasm-tools component wit—— 源码可能声明得比实际用的多。 -
host-notify不需要任何 grant。 它是输出而不是访问:既碰不到什么也读不到什么。约束它的是每轮的速率上限,而不是一道门;而且署名前缀由宿主加,所以一个插件没法冒充另一个。
WIT 的世界是线性阶梯 —— extension ⊂ extension-commands ⊂ extension-events —— 所以插件应该选能覆盖它导出内容的最低那一级。选得比需要的高,就要为用不到的部分补桩导出。
| 插件 | world | 构建 |
|---|---|---|
wasm-plugin-minimal |
extension |
无 make target —— 手动,见其 README |
wasm-plugin |
extension-commands |
make plugin |
wasm-plugin-events |
extension-events |
make plugin-events |
wasm-plugin-memory |
extension-commands |
make plugin-memory |
wasm-plugin-report |
extension-events |
make plugin-report |
一定要在仓库根目录构建。 wasm-tools component embed wit/ 是按工作目录去解析 wit/ 的,在示例目录里面构建会静默嵌进一个过时的模块。产物落在 dist/。
宿主需要 --features wasm;标准发布二进制没有 WASM 运行时,而且它会明说 —— 它会打印自己忽略了几条 [[extensions]],而不是默默不干活。
在 GFW 后面且没配 cargo 镜像时,新插件项目解析 crates.io index 会超时(这五个示例都各自带了 Cargo.lock,能省掉一部分解析,但首次拉取依赖仍要出网)。一劳永逸的做法是配镜像:
# ~/.cargo/config.toml
[source.crates-io]
replace-with = 'mirror'
[source.mirror]
registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"wasm-tools 用 cargo install wasm-tools --locked 安装,同样走上面的镜像。
wasm32-wasip1 目标如果 rustup target add 失败(部分镜像不带这个组件),先检查它是不是已经在 sysroot 里:
ls "$(rustc --print sysroot)/lib/rustlib/" | grep wasm32-wasip1make 的 ensure-wasm-tools 检查的正是这个路径,而不是去信 rustup target list --installed。
examples/hooks/ 里是给 [[hooks.*]] 用的脚本 —— check-rm-rf.sh、protected-paths.sh、redact-secrets.sh、dirty-repo-guard.sh、audit-log.sh、session-log.sh。见 生命周期 hooks。
有两件事只有 hook 能做,而且都是设计决定而非缺失:
- 否决。 hook 能阻断或改写一次工具调用。插件只观察事件,拦不住。想让某件事不发生,那就是 hook 的活。
-
绝不漏事件。 hook 是同步的。插件的事件投递是 drop-on-busy 且不排队 —— 插件正在另一个 guest 调用里时,下一个事件直接丢掉。所以任何必须完整的东西都属于 hook。
wasm-plugin-report在自己的输出里就是这么说的,并且在计数上打≥,因为一个会悄悄少算的统计比一个承认自己少算的更糟。
tests/fixtures/runaway-plugin/ 是一个 execute-tool 永不返回的插件,用来证明 epoch 断路器确实会触发。它自己的头部就写了为什么它不是示例:"Deliberately NOT in examples/ — nothing here is worth copying." 一个别的 README 都在说「复制我当起点」的目录,不该是一个会挂死的组件的家。
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