插件规格指标

# 插件规格指标

本文定义了什么是秋之盒插件:它的封装格式、必须和可选的文件、宿主如何解析和加载它、以及插件内部的组成结构。

# 什么是秋之盒插件

秋之盒插件是一个 .atmb 文件——本质是 ZIP 格式归档。宿主(@autumnbox/app)在启动时或用户手动安装时读取 .atmb 文件,解压、执行其中的 JavaScript 代码,将插件提供的 App、Card、Service 注册到运行时。

一句话定义.atmb = ZIP(package.json + UMD bundle + 可选 resources/)

# .atmb 文件结构

my-plugin.atmb (ZIP 归档)
│
├── package.json          ← 必须。插件元数据和加载配置
├── index.js              ← 必须。UMD 格式的 JavaScript bundle
│
├── resources/            ← 可选。随插件分发的静态资源
│   ├── icon.png          ← 插件图标(128×128 PNG,透明背景)
│   ├── lang/             ← 国际化语言文件(自动扫描注册)
│   │   ├── zh-CN.json
│   │   └── en-US.json
│   └── ...               ← 其他自定义资源(模板、二进制等)
│
└── assets/               ← 可选。Vite 构建产物(worker、WASM 等)
    └── ...

# 必须文件

# package.json

插件的身份证。宿主通过 package.json 识别插件、确定入口文件、读取元数据。

{
  "name": "@myplugins/my-toolkit",
  "version": "1.0.0",
  "description": "A powerful toolkit for Android devices",
  "author": "Developer",
  "license": "MIT",
  "main": "index.js",
  "repository": "https://github.com/dev/my-toolkit",
  "autumnbox": {
    "apps": [
      { "export": "ToolkitApp" }
    ],
    "cards": [
      { "export": "StatusCard" }
    ],
    "services": [
      { "className": "ToolkitService", "export": "ToolkitService" }
    ],
    "hasPluginMain": true
  }
}
字段 必须 说明
name 插件唯一标识pluginPackageName)。一旦发布不可更改
version 语义化版本号
main 入口文件路径,默认 index.js
description 插件描述(未提供翻译时的回退值)
author 作者名(未提供翻译时的回退值)
license 开源许可证
repository 源码仓库地址(字符串或 { type, url } 格式)
autumnbox SDK 构建时自动生成的注册元数据

关于 name

name 字段就是 pluginPackageName——插件在整个系统中的唯一身份来源。没有额外的 pluginIdidmanifest.json

autumnbox 字段详解:

autumnbox 字段由 autumnbox-sdk build 根据约定目录自动生成,不需要手动编写:

子字段 说明
apps src/apps/ 目录中导出的 AutumnApp 对象列表
cards src/cards/ 目录中导出的 AutumnCard 对象列表
services src/services/ 目录中导出的 Service 类列表
hasPluginMain 是否存在 src/main.ts 入口函数

# index.js(UMD Bundle)

插件的可执行代码。autumnbox-sdk build 将 TypeScript 源码编译为 UMD 格式的单文件 bundle。

关键特性:

  • UMD 格式:通过 new Function('require', 'module', 'exports', code) 执行
  • 共享模块已 external 化:宿主通过 ModuleRegistry 提供共享依赖,插件不需要打包这些模块

宿主提供的共享模块:

模块 说明
react React 19
react-dom React DOM
react/jsx-runtime JSX 运行时
antd Ant Design UI 库
@ant-design/icons Ant Design 图标
@autumnbox/interfaces 接口和 Handle 类型
@autumnbox/common State、ServiceContainer
@autumnbox/core 核心 Service 类
@autumnbox/app 应用层 Service 和 Hooks
@xterm/xterm xterm.js 终端模拟器
@xterm/addon-fit xterm 自适应插件

插件 require() 这些模块时,ModuleRegistry 返回宿主已加载的实例。如果插件 require() 了不在此列表中的模块,会抛出错误。

入口导出:

Bundle 必须导出以下之一(宿主按此顺序检测):

导出名 说明
__autumnbox_entry__ 约定模式(推荐)。由 SDK 自动生成的入口函数,处理所有注册逻辑
main / pluginMain / default Legacy 模式。手动编写的 PluginMain 函数

# 可选文件

# resources/ 目录

随插件分发的只读静态资源。构建时 autumnbox-sdk package 将整个 resources/ 打包进 .atmb

运行时,宿主将 resources/ 下所有文件解压为 Map<path, Blob>,插件通过 context.getResource(path) 按需懒加载。

resources/lang/ 的特殊地位:

宿主在加载插件时自动扫描 resources/lang/*.json,按文件名识别 locale(如 zh-CN.jsonzh-CN),将内容注册到 LanguageService。语言文件中的特殊键 <name><description><author> 在打包时已被 SDK CLI 替换为带命名空间的 key。

详见 资源与文件系统国际化

# assets/ 目录

Vite 构建时产生的额外产物(如 Web Worker、WASM 文件等),一般由构建工具自动管理。

# 宿主加载流程

宿主按以下步骤解析和加载 .atmb

┌──────────────────────────────────────────────────────┐
│ 1. 读取 .atmb 文件(File 对象)                        │
│    ↓                                                  │
│ 2. JSZip 解压 ZIP                                     │
│    ↓                                                  │
│ 3. 读取 package.json → 提取 pluginPackageName          │
│    (已注册过?抛 "already registered")                 │
│    ↓                                                  │
│ 4. 读取 index.js(或 package.json.main 指定的入口)     │
│    ↓                                                  │
│ 5. 提取 resources/ 为 Map<path, Blob>                  │
│    ↓                                                  │
│ 6. 执行 UMD bundle(new Function + ModuleRegistry)    │
│    ↓                                                  │
│ 7. 检测入口模式:                                      │
│    ├─ 有 __autumnbox_entry__ → 约定模式                │
│    └─ 有 main/pluginMain/default → Legacy 模式         │
│    ↓                                                  │
│ 8. 创建插件数据目录                                     │
│    {autumnHome}/plugins/data/{pluginPackageName}/      │
│    ↓                                                  │
│ 9. 自动加载语言文件                                     │
│    resources/lang/*.json → LanguageService.load()      │
│    ↓                                                  │
│ 10. 解析插件元数据(名称/描述/作者)                     │
│     优先从语言 key 读取,回退到 package.json 字段        │
│    ↓                                                  │
│ 11. 构建 PluginContext 并调用入口函数                   │
│     返回的 dispose 函数保存用于卸载                      │
│    ↓                                                  │
│ 12. 可选:持久化 .atmb 到 OPFS(刷新后保留)            │
└──────────────────────────────────────────────────────┘

# 约定模式 vs Legacy 模式

特性 约定模式(推荐) Legacy 模式
入口导出 __autumnbox_entry__(context) main(context) / pluginMain(context)
注册方式 SDK 自动扫描 src/apps/src/cards/src/services/ 生成注册代码 手动在 main 函数中调用 context.registerApp()
是否需要手写入口 不需要。autumnbox-sdk build 自动生成 __entry__.ts 需要手写 main.ts
SDK 版本 当前版本 向后兼容保留

# 插件内部结构

一个插件可以包含三种注册物:

# App(全屏页面)

通过 defineApp() 定义,打开后显示为独立 Tab。

import { defineApp } from '@autumnbox/sdk';

export const MyApp = defineApp({
  id: 'my-app',
  name: 'app.name.my-app',
  icon: 'data:image/svg+xml,...',
  component: MyAppView,                // React 模式
  // 或 mount: (el) => () => {},       // 自定义 DOM 模式
  shallSelectAdbDevice: true,          // 可选:打开前要求选择设备
  singleton: true,                     // 可选:只允许一个实例
  tags: ['tools'],                     // 可选:分类标签
});

详见 App 详解

# Card(首页卡片)

通过 defineCard() 定义,始终展示在主界面。

import { defineCard } from '@autumnbox/sdk';

export const StatusCard = defineCard({
  id: 'status',
  name: 'card.name.status',
  order: 10,
  component: StatusCardView,
  // 或 mount: (el) => () => {},
});

详见 Card 详解

# Service(无 UI 服务)

通过导出 Service 类定义,提供后台能力。

import { ServiceContainer } from '@autumnbox/sdk/common';

export class MyService {
  constructor(private readonly services: ServiceContainer) {}

  doSomething(): string {
    return 'Hello from MyService';
  }
}

Service 在 src/services/ 目录下导出后,SDK 自动扫描并在 __entry__.ts 中生成注册代码。其他插件的 App/Card 可通过 useService(MyService) 消费。

详见 Service 详解

# main.ts(可选初始化逻辑)

如果需要在加载时执行一次性初始化:

import type { PluginContext } from '@autumnbox/sdk';

export function main(context: PluginContext): (() => void) | void {
  console.log(`Plugin ${context.pluginPackageName} loaded`);
  // 可选:返回清理函数
  return () => console.log('Plugin unloaded');
}

main 在约定模式下是可选的。SDK 检测到 src/main.ts 存在时会在 __entry__.ts 中调用它。

# 开发时的源码结构

开发者在项目中按以下约定组织源码,SDK 构建工具自动扫描生成入口:

my-plugin/
├── src/
│   ├── apps/              ← 导出 AutumnApp 对象(*App)
│   │   ├── ToolkitApp.tsx
│   │   └── SettingsApp.tsx
│   ├── cards/             ← 导出 AutumnCard 对象(*Card)
│   │   └── StatusCard.tsx
│   ├── services/          ← 导出 Service 类(*Service)
│   │   └── ToolkitService.ts
│   ├── main.ts            ← 可选:初始化逻辑
│   └── __entry__.ts       ← 自动生成,不要手动编辑
├── resources/
│   ├── icon.png
│   └── lang/
│       ├── zh-CN.json
│       └── en-US.json
├── package.json
└── tsconfig.json

# 编译过程

从源码到 .atmb 分为两个阶段:

# 阶段一:autumnbox-sdk build

autumnbox-sdk build 执行以下步骤:

┌─────────────────────────────────────────────────────┐
│ 1. 扫描约定目录                                      │
│    src/apps/    → 匹配 export const *App            │
│    src/cards/   → 匹配 export const *Card           │
│    src/services/→ 匹配 export class *Service        │
│    src/main.ts  → 检查是否存在                       │
│                                                     │
│ 2. 生成入口文件 src/__entry__.ts                      │
│    ├─ import 所有发现的 App/Card/Service             │
│    ├─ 生成 __autumnbox_entry__(context) 函数         │
│    │  ├─ 注册 Service(带 deriveServiceName)        │
│    │  ├─ 注册 App                                    │
│    │  ├─ 注册 Card                                   │
│    │  └─ 调用 main(context)(如果存在)               │
│    └─ 返回 dispose 函数                              │
│                                                     │
│ 3. Vite 构建                                         │
│    ├─ 入口: src/__entry__.ts                         │
│    ├─ 输出: dist/index.js(UMD 格式)                │
│    ├─ 共享模块 external 化(不打包)                   │
│    └─ React JSX 转换                                 │
│                                                     │
│ 4. 生成 dist/package.json                            │
│    ├─ 复制 name/version/description/author 等字段     │
│    └─ 写入 autumnbox 元数据(apps/cards/services 列表)│
│                                                     │
│ 5. i18n 验证                                         │
│    ├─ 检查 <name> 是否至少在一个语言文件中定义         │
│    └─ 警告未翻译的 App/Card name key                 │
└─────────────────────────────────────────────────────┘

自动发现的命名规则:

目录 匹配正则 示例
src/apps/ export const *App export const ShellApp
src/cards/ export const *Card export const BatteryCard
src/services/ export class *Service export class AuthService

支持的文件结构:Name.tsxName.tsName/index.tsxName/index.ts

Service 名称派生:

Service 注册到 IoC 容器时的名称由类名派生:去掉 Service 后缀,首字母小写。

类名 注册名
AuthenticationService authentication
ScrcpyBridgeService scrcpyBridge
MyToolService myTool

# 阶段二:autumnbox-sdk package

autumnbox-sdk package 将构建产物打包为 .atmb ZIP:

┌──────────────────────────────────────────────────────┐
│ 1. 读取 dist/package.json                             │
│                                                      │
│ 2. 处理语言文件语法糖                                  │
│    resources/lang/*.json 中的特殊 key:                │
│    "<name>"        → "plugin.name.{pluginPackageName}" │
│    "<description>" → "plugin.description.{...}"        │
│    "<author>"      → "plugin.author.{...}"             │
│                                                      │
│ 3. 创建 ZIP 归档(.atmb)                              │
│    ├─ package.json  ← dist/package.json               │
│    ├─ index.js      ← dist/index.js                   │
│    └─ resources/    ← 整个 resources/ 目录             │
└──────────────────────────────────────────────────────┘

# 完整命令

# 分步执行
autumnbox-sdk build      # TypeScript → UMD + __entry__.ts
autumnbox-sdk package    # 打包 → .atmb

# 一步到位(build 内部已集成 package)
autumnbox-sdk build      # 默认在构建后自动打包

# PluginContext

每个插件加载时收到独立的 PluginContext

interface PluginContext {
  pluginPackageName: string;
  pluginHome: string;
  fs: IFileSystem;
  serviceContainer: ServiceContainer;
  getResource(name: string): Promise<Blob | null>;
  registerApp(...apps: AutumnApp[]): () => void;
  registerCard(...cards: AutumnCard[]): () => void;
  t(key: string): IReadonlyState<string>;
  getService<T>(token: ServiceClass<T>): T;
}
成员 说明
pluginPackageName 插件包名(package.json 的 name),唯一身份标识
pluginHome 插件专属可读写数据目录路径
fs 文件系统接口实例
serviceContainer IoC 容器,获取任意 Service
getResource .atmb 内 resources/ 目录懒加载资源(Blob)
registerApp 注册 App,返回取消注册函数
registerCard 注册 Card,返回取消注册函数
t(key) 获取响应式翻译状态(IReadonlyState<string>
getService 从 IoC 容器获取 Service 的便捷方法

# TypeScript 配置

使用 AutumnBoxPluginTemplate 模板时已预配置。手动配置需添加 SDK 路径映射:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "moduleResolution": "bundler",
    "paths": {
      "@autumnbox/sdk": ["./node_modules/@autumnbox/sdk/types/sdk.d.ts"],
      "@autumnbox/sdk/hooks": ["./node_modules/@autumnbox/sdk/types/app.d.ts"],
      "@autumnbox/sdk/services": ["./node_modules/@autumnbox/sdk/types/core.d.ts"],
      "@autumnbox/sdk/common": ["./node_modules/@autumnbox/sdk/types/common.d.ts"],
      "@autumnbox/sdk/interfaces": ["./node_modules/@autumnbox/sdk/types/interfaces.d.ts"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"]
}

# 下一步

最后更新: 4/8/2026, 2:35:44 AM