examples/aether-extension 包演示了如何在单个包中组合 Pi Agent 级工具与 Aether UI 扩展。本导读覆盖示例的每一部分:包清单、Pi 工厂、Aether 工厂、表面与页面注册、存储,以及 before_send 事件钩子。
此扩展做什么
- 注册 Pi
/aether-example命令,在扩展激活时通知 Agent - 在输入框上方添加卡片表面,包含实时计数按钮与提示词预填操作
- 添加全屏抽屉页面(「扩展仪表盘」),包含持久化计数卡片与嵌入式 WebView
- 通过
Aether.postMessage将 WebView 桥接回扩展操作 - 在扩展存储中跨会话持久化计数器值
- 挂钩
before_send事件,从发出的消息中剥离!raw前缀
包声明
package.json 在 pi.extensions 与 aether.extensions 下都列出同一个 index.ts 入口,因此单个文件同时驱动 Pi Agent 侧与 Aether UI 侧:
完整带注释代码导读
1. 导入与 defineAetherExtension
ExtensionAPI为 Pi 工厂(默认导出)中的pi参数提供类型。defineAetherExtension包装 Aether 工厂,并为aether上下文对象提供类型安全访问。ui是在每个render回调中使用的声明式 UI 构建器。
2. Pi 工厂(默认导出):注册 /aether-example
pi.extensions 下,Aether 将其作为标准 Pi 扩展加载。你将 Pi 工厂导出为 默认导出。处理程序仅触发一条 info 通知 — 这是证明包的 Pi 侧与 Aether 侧同时生效的最小示例。
3. Aether 工厂(activateAether):计数操作 + 预填操作
activateAether(命名导出)。因为该文件也列在 aether.extensions 下,Aether 会发现它。
increment从aether.storage读取当前count(默认为0),递增后写回,再通过aether.host.invoke("app.notify", ...)显示 Android toast。因为storage是同步的,并持久化在~/.aether/app-extension-state.json中,该值可在热重载与应用重启后保留。prefill使用app.setDraftInput用现成提示词替换输入框文本。你可在 UI 中任意按钮上传入操作 ID"prefill"来触发。
4. 在 chat.composer.top 注册表面
registerSurface 将内容挂到 chat.composer.top 插槽 — 即文本输入框正上方。要点:
order: 10— 多个扩展注册同一插槽时控制堆叠顺序。render上下文 — 接收实时应用状态。storage保存扩展的持久化键值对;is_running反映 Agent 当前是否在生成。ui.card— 将全部内容包在圆角卡片中。radius: 22更大幅度圆角,以匹配输入框形态。- 带
arrangement: "space-between"的ui.row— 将标签与状态文本拉到两端。 - 带
wrap: true的ui.row— 窄屏上三个按钮会换到第二行;rowSpacing: 8为行间增加垂直间距。 ui.node("pageButton", { page: "dashboard", ... })— 特殊按钮类型,导航到 ID 为"dashboard"的已注册页面,并在会话抽屉中打开。
5. 带 WebView 与操作桥的页面注册
registerPage 创建全屏目标页,Aether 会自动将其列在会话抽屉中。注意:
id: "dashboard"— 此字符串必须与上方表面中pageButton节点的page字段匹配。render上下文 — 接收与表面相同的应用状态对象。storage.count读取持久化计数器。ui.web— 将任意 HTML 文档注入原生 AndroidWebView。WebView 内启用 JavaScript、DOM 存储与网络访问。Aether.postMessage(JSON.stringify({ action, args }))— 从 WebView JavaScript 回到扩展逻辑的唯一桥接。用户点击 “Increment through JavaScript” 时,WebView 会发送消息,Aether 将其路由到已注册的increment操作。你可以在 JSON 对象中传入任意args;操作处理程序将其作为第一个参数接收。- 带
scroll: true的ui.column— 内容超出屏幕时使页面主体可垂直滚动。
6. before_send 事件钩子
aether.on("before_send", handler) 在用户消息提交给模型前立即触发。处理程序接收草稿载荷,并可返回部分对象以合并回去。这里,任何以 !raw 开头的消息会在发送前剥离该前缀。返回 undefined(或不返回)则保持载荷不变。
安装
- 将
examples/aether-extension目录打包为.zip归档。 - 在 Android 设备上打开 Aether。
- 前往 设置 → 扩展 并点按 导入扩展。
- 选择 zip 文件。Aether 将其解压到
~/.aether/extensions并立即热加载 Script 侧。