Skip to content

Sidecar 插件开发

Sidecar 插件以独立的子进程运行,通过标准输入/输出 (stdio) 与 AIO Hub 后端通信。这种模式是语言无关的,只要能编译成可执行文件并遵循通信协议即可。

适用于:

  • 计算密集型任务(如图像处理、大规模数据计算)
  • 需要隔离环境或沙箱
  • 使用 AIO Hub 未内置的语言(Python、Go、Node.js 等)
  • 需要长连接的低延迟场景(本地 OCR 引擎、自动化执行引擎)

编写 manifest.json

type 必须为 sidecar,并提供一个 sidecar 配置块。

json
{
  "id": "file-hasher",
  "name": "文件哈希计算器",
  "version": "0.1.0",
  "description": "计算文件的 SHA-256 哈希值的示例 Sidecar 插件",
  "author": "AIO Hub Team",
  "type": "sidecar",
  "sidecar": {
    "executable": {
      "win32-x64": "dev-bin/win32-x64/file-hasher.exe",
      "darwin-x64": "dev-bin/darwin-x64/file-hasher",
      "linux-x64": "dev-bin/linux-x64/file-hasher"
    },
    "args": []
  },
  "methods": [
    {
      "name": "calculateHash",
      "description": "计算文件的哈希值",
      "parameters": [
        {
          "name": "path",
          "type": "string",
          "required": true
        }
      ]
    }
  ]
}

sidecar 配置项

  • executable: 一个对象,按平台和架构 (<os>-<arch>) 指定可执行文件的相对路径。
  • args: 启动可执行文件时传递的命令行参数数组。
  • resident (可选): 是否为持久型模式(见下文)。

target/debug 只表示 Cargo 为当前宿主平台生成的默认缓存目录;显式使用 --target 时产物位于 target/<target-triple>/debug。不要让 manifest 直接依赖这两种缓存布局。构建脚本应将二进制复制到 dev-bin/<platform>/ 等稳定开发路径,发布时再复制到 ZIP 内的 bin/ 并生成对应 manifest。

方法声明

Sidecar 插件同样需要在 manifest.jsonmethods 数组中声明可调用方法。

实现插件逻辑(普通 Sidecar)

Sidecar 插件通过 stdio 与主进程进行基于 JSON-RPC 的通信。

  1. 启动: AIO Hub 启动 executable 中指定的可执行文件。
  2. 请求: AIO Hub 向子进程的 stdin 发送 JSON 请求。
  3. 响应: 插件处理请求,并将 JSON 响应写入其 stdout

API v3 普通 Sidecar 每次调用都会启动新进程,因此每个请求都必须校验宿主注入的顶层 hostContext

json
{
  "method": "calculateHash",
  "params": { "path": "example.bin" },
  "hostContext": {
    "pluginApiVersion": 3,
    "sidecarProtocolVersion": 3
  }
}

hostContext 由运行时生成,不读取或透传 manifest 中的同名配置。API v2 普通 Sidecar 不要求该字段。

编译

你需要通过插件构建脚本编译 Sidecar,并将可执行文件部署到 manifest.json 当前平台键指定的路径。验证必须针对该路径实际启动一次,不能只检查 Cargo 编译成功。

持久型 Sidecar 插件 (Resident Mode)

对于需要长连接、低延迟、事件流或需要常驻后台运行的场景(如本地 OCR 引擎、自动化执行引擎),AIO Hub 支持持久型 Sidecar 插件(Resident Mode)

声明常驻模式

manifest.jsonsidecar 配置块中声明 "resident": true

json
{
  "id": "paddle-ocr",
  "type": "sidecar",
  "sidecar": {
    "executable": {
      "win32-x64": "bin/aiohub-paddle-ocr-windows-x64.exe"
    },
    "resident": true,
    "startupMethod": "healthCheck",
    "startupParams": {}
  }
}
  • resident: 必须设为 true
  • startupMethod (可选): 进程启动后自动执行的健康检查或初始化方法。推荐使用 healthCheck / warmup 这类语义明确的方法,避免复用业务方法和空参数伪造检查。
  • startupParams (可选): 初始化方法所需的参数。

API v3 宿主会在首次启动请求的 params 中覆盖注入 hostContext.pluginApiVersionhostContext.sidecarProtocolVersion。插件不能信任 manifest 自己声明的同名字段;每个新进程的首次启动检查必须验证宿主注入值,完成后普通业务调用不要求重复携带该字段。

进程生命周期

  • 启动: 当插件被启用(enable)时,主应用会自动启动常驻进程,并保持其 stdinstdoutstderr 句柄。
  • 退出: 当插件被禁用(disable)或应用关闭时,主应用会向常驻进程的 stdin 发送 shutdown 指令。常驻进程应在收到指令后优雅退出,超时(默认 5 秒)未退出则会被主应用强制 kill。
  • 命令超时恢复: request 模式单条常驻命令默认最多等待 300 秒。API v3 长任务应使用 job 模式,以合法进度刷新无进度超时;取消宽限期结束仍无终态时才重启 sidecar。
  • 进程代际: 每次 spawn 都生成 generationId。pending request、stdout reader 和前端事件只属于该代际,旧进程的迟到输出不能完成新进程请求。

JSON-RPC 通信协议规范

常驻 Sidecar 进程通过 stdin 接收命令,并通过 stdout 输出响应或主动推送事件。

前端 → Sidecar (命令格式)

typescript
interface ResidentCommand {
  id: number; // 唯一请求 ID,用于匹配响应
  method: string; // 调用的方法名
  params: Record<string, unknown>; // 方法参数
}

Sidecar → 前端 (响应与事件格式)

所有输出必须是单行 JSON(JSON Lines 格式):

typescript
type SidecarOutput =
  | { id: number; type: "progress" | "result" | "error"; data: unknown }
  | { type: "event"; event: string; data: unknown };

progress 是非终态消息,不会完成或移除对应的 pending 请求;只有同一 idresulterror 会结束本次命令。需要跨窗口或业务级关联部分结果时,可在无 id 的自定义事件 data 中携带调用方生成的流标识。

API v3 长任务

长任务的提交命令应立即以 result 返回 { accepted: true, jobId }。实际完成、失败和取消通过无请求 ID 的命名事件报告,所有事件必须携带同一 jobId。宿主取消后必须等待取消终态或确认 sidecar 已重启,才能释放任务使用的临时文件。

进度事件(带 id)

json
{
  "id": 42,
  "type": "progress",
  "data": { "message": "正在识别 1/2", "percent": 50 }
}

成功结果(带 id)

json
{ "id": 42, "type": "result", "data": { "results": [...] } }

错误事件(带 id)

json
{ "id": 42, "type": "error", "data": "模型文件损坏" }

主动推送事件(无 id):用于向前端实时推送状态变更、日志等。

json
{
  "type": "event",
  "event": "status",
  "data": { "status": "running", "stepIndex": 4 }
}

Sidecar 间中转协议 (Broker 模式)

当常驻 Sidecar A(如自动化引擎)需要调用常驻 Sidecar B(如 OCR 引擎)时,不允许直接启动 B,必须通过主应用进行中转。

Sidecar A 发送中转请求(通过 stdout)

json
{
  "type": "forward",
  "id": 100,
  "target": "example-file-hasher",
  "method": "hashFile",
  "params": {
    "path": "C:/Temp/example.bin"
  }
}
  • type: 固定为 "forward"
  • id: 调用方侧的唯一 ID,用于匹配最终响应。
  • target: 目标常驻 Sidecar 的插件 ID。
  • method / params: 目标 Sidecar 的方法名和参数。

主应用转发并推回结果

主应用会自动调用目标 Sidecar B,并将结果通过 forward_result 事件推回给 Sidecar A 的 stdin

json
{
  "type": "event",
  "event": "forward_result",
  "data": {
    "id": 100,
    "targetId": "paddle-ocr",
    "result": { "results": [...] },
    "error": null
  }
}

统一临时文件管理规范

为了避免临时文件混乱,所有 Sidecar 插件应遵循主应用的统一临时文件规范:

  • 临时目录: ${appDataDir}/temp/aiohub-shared/
  • 文件命名: {sourceId}_{timestamp}_{uuid}.png
  • 自动清理: 主应用启动时会自动扫描并删除超过 24 小时的残留临时文件。
  • 优先路径: 插件接口应优先支持 path 文件路径输入,以减少 base64 传输压力。

下一步

Released under the Apache-2.0 License.