AIRI 插件平台架构解析:Eventa 传输、双平面模型与多设备编排

发布时间:2026/9/12 9:29:56
AIRI 插件平台架构解析:Eventa 传输、双平面模型与多设备编排 AIRI 插件平台架构解析Eventa 传输、双平面模型与多设备编排【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airiAIRIProject AIRI是一个面向多运行时的插件化陪伴平台其插件系统围绕「一个 API 表面、多种传输、多设备协同」构建插件、桥接Bridge与查看器Viewer通过 Eventa 消息传输通信插件宿主Plugin Host统一装载插件并路由控制与数据流量。本文基于仓库中的架构设计文档 packages/plugin-sdk/docs/design/architecture.md并结合 plugin-sdk 源码 深入讲解双平面分离、插件生命周期、能力模型、部署模式与验证策略读完你将掌握 AIRI 插件平台从设计意图到运行时实现的全貌并能在自己的插件或宿主接入中复用这套思路。背景为什么 AIRI 需要一套插件平台AIRI 需要同时运行在桌面Electron、Web 与移动端Pocket并保持一套干净的插件 API 表面。插件既要能注册 UI、声明能力也要能与设备特定的桥接交换数据。为了让系统具备可扩展性架构文档明确要求高频数据流必须与生命周期、配置类流量分离。同时运行时的依赖编排被有意拆分到独立设计文档capability-orchestration.md中让本文聚焦于平台的「形态」与「平面」划分避免架构讨论被编排细节稀释。设计目标与非目标Goals在多个运行时之间提供统一的插件 API 表面将生命周期与配置流量从高频数据流中分离以同一套协议支持本地插件与远程插件允许多个查看器与桥接通过共享控制平面协同保持部署灵活性嵌入式、外部进程或远程插件宿主均可。Non-goals不在本文中定义完整的插件生命周期状态机交给 capability-orchestration.md不规定具体 UI 布局或查看器实现不实现 Eventa 适配器之外的新传输层。总体提案控制平面与数据平面分离架构的核心提案非常清晰所有控制与数据流量统一走Eventa用专用控制平面承载配置、权限、UI 注册与路由策略用数据平面承载音频、视觉、遥测等高吞吐流插件运行在插件宿主内宿主负责装载插件入口点并暴露 SDK桥接被视为仅提供数据与动作的设备侧集成不拥有 UI。控制平面Control Plane控制平面承担生命周期、配置、路由策略、权限与 UI 贡献。文档列出的典型控制消息包括control:hellocontrol:announcecontrol:plugin:registercontrol:plugin:config:getcontrol:plugin:config:setcontrol:capability:grantcontrol:capability:revokecontrol:ui:register数据平面Data Plane数据平面承担实时、高吞吐的流式数据。典型数据消息包括data:context:updatedata:vision:framedata:audio:streamdata:transcriptdata:character:output传输选项两个平面都使用 Eventa 消息传输层提供两种可选形态两个 WebSocket 端点控制与数据各自独立一个多路复用连接 命名空间namespace 隔离。这种设计的动机在文档 QA 中直接给出生命周期流量与高频数据流对可靠性reliability和服务质量QoS的需求不同物理或逻辑上隔离可以各自按需优化。插件宿主与查看器插件宿主Plugin Host是一个 Node 进程职责包括装载插件入口点entrypoint暴露 AIRI SDK注册 UI 贡献协商能力capabilities连接控制平面与数据平面。在源码层面这一角色由 core.ts 中的ExtensionHost类承担。从类定义与注释可以推断宿主内部组合了多个服务ExtensionSessionService会话管理、DependencyService能力注册表、KitRegistryServiceKit 注册、PermissionService权限校验、ResourceService资源提供、KitApiBindingRegistryService模块绑定管理。文档注释给出的调用链为caller - ExtensionHost.start - FileSystemLoader.resolveEntrypointFor - FileSystemLoader.loadExtensionFor - ExtensionHost.startExtension查看器Viewer负责渲染 UI 与角色输出文档给出的示例包括带 Configurator 特性的 Electron StageWeb Configurator 客户端Pocket Stage 客户端。插件生命周期概述架构文档给出了与 core.ts 生命周期注释 相呼应的生命周期图覆盖模块声明announcement、配置与能力阶段在源码实现中startExtensioncore.ts揭示了对应的运行时细节每个扩展会话拥有phase状态取值setting-up | ready | failed | stopped会话上下文ctx提供kits扩展级 Kit 注册表与modules.register(...)模块级注册返回带独立订阅与清理回调的ExtensionModuleContext权限采用双层模型扩展授权是包/会话级「天花板」模块级 Kit 使用按「扩展授权 ∩ 模块请求」推导的模块授权进行校验见 core.ts 头部注释失败路径会把会话标记为failed并执行完整清理cleanupExtensionSession避免泄漏绑定、订阅与模块资源。更细的能力依赖编排、等待阶段与就绪门控readiness gate在 capability-orchestration.md 中定义。桥接与远程插件桥接Bridge把外部设备与服务接入 AIRI。它们不拥有 UI只提供数据与动作。文档示例包括VS Code 扩展提供编辑器上下文与命令浏览器扩展提供页面上下文Minecraft 服务提供游戏事件与命令。远程插件Remote Plugin是任意语言编写的服务通过 Eventa 连接并注册能力。文档明确它们更适用于服务端集成与非 JS/TS 技术栈——远程插件只要求能讲 WebSocket 上的 Eventa 协议不需要 npm 或 JS 运行时。传输抽象所有 SDK 调用都是传输无关的。宿主决定通信走本地 IPC 还是远程 RPC而插件 API 保持不变。这一点在 channels/index.ts 中有实现支撑createExtensionChannelScope创建「扩展身份 Eventa context」的通道作用域createModuleChannelScope从扩展作用域派生出模块身份并复用同一 Eventa context。身份信息id、sessionId、version随上下文绑定使协议流量天然携带归属信息。配套设计文档 multi-transport.md 进一步规划了PluginTransport联合类型export type PluginTransport | { kind: in-memory } | { kind: websocket, url: string, protocols?: string[] } | { kind: web-worker, worker: Worker } | { kind: node-worker, worker: import(node:worker_threads).Worker } | { kind: electron, target: main | renderer, webContentsId?: number }其核心原则是每个插件实例一个 Eventa context由宿主的createPluginContext(transport)在生命周期方法调用前创建并通过createApis(ctx)把 API 绑定到该 context——本地插件in-memory / worker与远程插件WebSocket共享同一 API 表面互不串扰。能力模型Capability Model每个节点在注册时声明能力控制平面根据策略授予/拒绝权限并路由请求。文档给出的能力示例context.readcontext.writeui.panelui.widgetvision.capturevision.streamdevice.mobile.sensors源码层面能力由 dependencies.ts 中的DependencyService维护它是一个带快照与等待原语的内存能力注册表announce(key, metadata)声明能力状态置为announcedmarkReady(key, metadata)状态置为ready并唤醒所有等待者markDegraded/withdraw置为degraded/withdrawnwaitFor(key, timeoutMs 15000)若已 ready 立即返回否则注册等待回调超时抛出Capability ... is not ready after ...错误waitForMany(keys, timeoutMs)并行等待多个能力。协议侧capabilities/index.ts 定义了跨边界的 RPC 契约protocolCapabilityWaitproj-airi:plugin-sdk:apis:protocol:capabilities:wait与protocolCapabilitySnapshot...capabilities:snapshot返回可序列化的CapabilityDescriptorinterface CapabilityDescriptor { key: string state: announced | ready | degraded | withdrawn metadata?: Recordstring, unknown updatedAt: number }这与 capability-orchestration.md 的「快照权威、事件增量」原则一致等待者先查快照能力已就绪则立即放行避免「错过就绪信号」的竞态。部署模式插件宿主支持三种部署形态嵌入式内嵌在 Electron 主进程中安装即用install-and-go外部 Node 进程支持热重载与进程隔离远程服务器实现跨设备连续性。这与 package.json 的导出结构 相印证./plugin-host子路径通过条件导出区分noderuntimes/node/index.mjs与defaultruntimes/web/index.mjs实现且运行时字面量被限定为electron | node | web见 types.ts。清单与入口点Manifest Entrypoints插件通过清单文件声明元数据并提供运行时入口点。文档给出的示例{ id: airi.vscode, name: AIRI VS Code, version: 1.0.0, capabilities: [context.read, ui.panel, commands], entrypoints: { node: ./dist/node/index.js } }实际宿主代码中清单格式由ExtensionManifestV1types.ts定义比文档示例更严格、更完整export interface ExtensionManifestV1 { apiVersion: v1 entrypoints: { default?: string electron?: string node?: string web?: string } id: string kind: manifest.extension.airi.moeru.ai permissions: ModulePermissionDeclaration }要点说明kind固定为manifest.extension.airi.moeru.ai作为清单类型判别器入口点可按运行时细分FileSystemLoader.resolveEntrypointForfs.ts的解析顺序为entrypoints.runtime→entrypoints.default→entrypoints.electron兼容遗留清单permissions声明包级/会话级权限天花板其 schemapermissionDeclarationSchema将权限划分为五类区域每类各有动作白名单apisinvoke/emitcapabilitieswait/snapshotpipelineshook/process/emit/manageprocessorsregister/execute/manageresourcesread/write/subscribe插件入口点本身通过defineExtension(...)define.ts导出约定id必须与清单中的id一致startExtension会做一致性校验setup(ctx)是通用的编写入口通过ctx.kits使用宿主安装的 Kit。Kit API 命名与 Eventa 契约仓库 READMEpackages/plugin-sdk/README.md补充了 Kit 开发的命名约定与架构的「SDK 调用传输无关」原则直接呼应名称含义gameletKitApisKit 包导出的共享 Eventa API 契约通常是defineInvokeEventa(...)条目组成的映射gameletKitService宿主侧 Kit 行为实现拥有真实副作用如 UI 挂载/更新/清理gameletKit供ctx.kits.use(...)消费的 Kit 定义拥有身份、版本、可用性策略与客户端创建gamelets返回给插件作者的客户端实例多操作时建议复数命名空间createGameletKit(...)把依赖注入gameletKit的工厂支持本地客户端与远程 Eventa 客户端关键约束共享的产物是 Eventa API 契约而不是实现函数。本地客户端可直接调用gameletKitService远程客户端通过 Eventa 调用同一 API两者对外暴露相同的编写形态跨进程/网络时需要复用共享的 Eventa invoke 契约不得发明invokeGamelet之类的 Kit 专属传输方法名。验证与测试验收标准Criteria插件可被宿主装载并注册 UI 与能力桥接可连接并被控制平面发现数据平面流与控制平面流量保持隔离同一插件 API 表面在桌面、Web、移动端表现一致。测试与 QATest QA集成测试host viewer bridge验证控制平面路由集成测试数据平面以高频数据源流式传输兼容性测试同一插件入口点在多个运行时下运行。从源码结构看这些验收标准已被测试工程承接plugin-host目录下存在 core.test.ts、runtimes/shared/services/下的dependencies.test.ts、permissions.test.ts、kits.test.ts、bindings.test.ts等单测以及testdata/中覆盖正常插件、错误插件、注入宿主 API 插件、无连接插件、可停止入口点等场景的测试夹具。进度与后续规划StatusActive design活跃设计阶段Next Steps让运行时文档与最新的插件上下文、传输策略对齐从 capability-orchestration.md 集成能力注册表与就绪门控生命周期迁移按语言扩充远程插件示例。常见问题QAQ: 为什么拆分控制平面与数据平面A: 生命周期流量与高频数据流对可靠性和 QoS 的需求不同物理或逻辑隔离可以各自按需优化避免高频流拖垮配置/权限等关键控制消息。Q: 桥接会渲染 UI 吗A: 不会。UI 通过控制平面贡献给查看器桥接只提供数据与动作。Q: 远程插件可以不使用 JS 或 npm 吗A: 可以。远程插件只需通过 WebSocket 讲 Eventa 协议语言无关。相关文档多传输插件上下文 Multi-Transport Plugin Contexts能力导向的模块编排 Capability-Oriented Module OrchestrationPlugin SDK 源码Plugin SDK 说明文档【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考