プラグインマニフェストとパッケージング

schema version 2、3、4 の plugin descriptor を定義し、ビルドした JAR を配布する。

すべての Turboism plugin JAR には、次の場所に descriptor が正確に 1 つあります。

META-INF/turboism/plugin.json

descriptor は "format": "turboism.plugin.meta" と schemaVersion を宣言します。Runtime が受け入れる schema version は 2、3、4 で、対応する contract に対して厳格に検証し、descriptor を自動で upgrade することはありません。

バージョン契約
2基本的な lifecycle、entrypoint、API range、dependency、permission、resource、localization。
3必須の category フィールドと任意の tags を追加します(first-party plugin が使用しているのは v3 です)。
4宣言された cross-plugin event contract(eventExports と eventImports)を追加します。

schema version 1 はロードされません。未知のフィールドは無視されず、拒否されます。

Manifest の例

{
  "format": "turboism.plugin.meta",
  "schemaVersion": 3,
  "id": "dev.example.plugin.hello",
  "name": "Hello Plugin",
  "version": "0.1.0",
  "description": "Minimal Turboism plugin lifecycle example.",
  "category": "workflow",
  "tags": ["example", "lifecycle"],
  "entrypoints": [
    "dev.example.plugin.HelloPlugin"
  ],
  "turboismApi": "[0.1.0,0.2.0)",
  "authors": [
    { "name": "Example Author" }
  ],
  "license": "MIT",
  "website": "https://example.org/hello-plugin",
  "resources": [],
  "i18n": {
    "baseName": "META-INF/turboism/i18n/messages",
    "locales": []
  },
  "dependencies": [],
  "permissions": [],
  "capabilities": [],
  "environment": {
    "requiresCubism": false,
    "ui": "none"
  }
}

必須フィールドは id、name、version、entrypoints、turboismApi、authors、website、resources、i18n です。schema 3 ではさらに category が必須です。

Entrypoint のルール

各 entrypoint は次を満たさなければなりません。

  • 同じ plugin JAR 内に存在する。
  • public である。
  • TurboismPlugin または subinterface を実装する。
  • public no-argument constructor を公開する。

複数の entrypoint は、1 つの plugin identity、descriptor、ClassLoader、PluginContext、DisposableScope、permission set、configuration namespace、storage namespace、resource、localization declaration を共有します。

construct/init/enable: manifest order
disable/shutdown:       reverse manifest order

construct、initialization、enablement の途中で failure が発生すると、JAR 全体が rollback されます。

Category と tags

category は 2〜32 文字の小文字 kebab-case token でなければなりません。レビュー済みの first-party category は modeling、workflow、appearance、analysis、performance、integration、system、development です。形式が正しいものの登録されていない category もロードされ、other として提示され、構造化された PLUGIN_CATEGORY_UNKNOWN diagnostic を出力します。

tags は任意で、2〜32 文字の小文字 kebab-case token を最大 12 個保持し、permission や capability を持ちません。

Dependency

{
  "id": "dev.example.plugin.base",
  "version": "[1.0.0,2.0.0)",
  "type": "required",
  "ordering": "after",
  "reason": "Uses the base plugin contract."
}
  • type は required または optional です。
  • ordering は none、before、または after です。
  • dependency ID は reverse-domain syntax を使います。
  • version range は Turboism version-range grammar を使います。

dependency record は resolution と ordering を制御します。別 plugin の private implementation package を public Java dependency にするものではありません。

Permission

各 permission record には、既知の ID、application または user scope、空でない reason が含まれます。

{
  "id": "turboism.action.register",
  "scope": "application",
  "reason": "Registers the Hello action."
}

plugin が呼び出す SDK service に必要な permission だけを宣言してください。permission は Provider の availability を保証せず、operation validation を迂回するものでもありません。

Resource と localization

Resource root は / で終わる正規化済みの相対 prefix です。

"resources": ["icons/"],
"i18n": {
  "baseName": "META-INF/turboism/i18n/messages",
  "locales": ["base", "en", "zh-Hans"]
}

その場合、JAR には次が含まれていなければなりません。

icons/hello.png
META-INF/turboism/i18n/messages.properties
META-INF/turboism/i18n/messages_en.properties
META-INF/turboism/i18n/messages_zh_Hans.properties

宣言した root と catalog は存在しなければなりません。通常の class 以外の resource は、宣言された root に属していなければなりません。path traversal、absolute path、backslash、空の segment、undeclared resource は拒否されます。

サポートされる runtime locale は en、ja、ko、zh-Hans、zh-Hant と、base bundle です。manifest の locale ID は scripted form(zh-Hans)を使用し、resource bundle のファイル名は Java の underscore form(messages_zh_Hans.properties)を使用します。

Cross-plugin event contract(schema 4)

schema 4 では、plugin が所有および消費する public event type を宣言できるため、Runtime は ClassLoader delegation なしでそれらを解決できます。

"eventExports": [
  {
    "id": "dev.example.event.hello",
    "contractVersion": "1.0.0",
    "eventType": "dev.example.api.HelloEvent",
    "abiSha256": "<sha256 of the event type bytes>"
  }
],
"eventImports": [
  {
    "provider": "dev.example.plugin.base",
    "eventId": "dev.example.event.base-status",
    "contractVersion": "1.0.0",
    "eventType": "dev.example.api.BaseStatusEvent",
    "abiSha256": "<sha256 of the event type bytes>"
  }
]

eventImports の entry は "required": true を持つこともでき、その provider contract が認定されることを要求します。Runtime はすべての entry について宣言された ABI digest を検証し、plugin 内で重複する export ID や event type を拒否し、認定された provider contract に解決しない import を拒否します。

Plugin ID は、少なくとも 1 つのドットを含む reverse-domain syntax を使用します(dev.example.plugin.hello)。event ID は小文字の dot/dash token 形式を使用します。

build と検証

repository 内の plugin module では、次を実行します。

./gradlew :plugins:hello:test :plugins:hello:jar validatePluginMeta \
  --no-daemon --console=plain

repository は Java 17 を使用し、module のビルド結果を次の下に書き込みます。

build/worktree/<worktreeId>/<module>/libs/

libs/ にある JAR がリリース用ファイルです。wrapper archive も別個の packaging step もありません。

厳格な JAR 検証

JAR 自体がインストール成果物です。Runtime は選択された JAR を信頼できないローカル入力として扱い、次を検証します。

  • 通常ファイルで、source chain にシンボリックリンクがなく、raw size、entry ごとの size、総展開量、entry 数、圧縮率の上限の範囲内である。
  • META-INF/turboism/plugin.json が正確に 1 つ。
  • descriptor の ID、version、API range の整合性。
  • コピーされた SDK、Runtime、test、test-framework、Live2D class がない。
  • native binary や installer payload がない。
  • ネストされた JAR がない。
  • 宣言された entrypoint、resource root、i18n catalog が存在する。
  • 未宣言の通常 resource がない。

マネージドインストールは検証済み JAR をステージングし、Cubism の再起動後に適用します。任意の ZIP や JAR の名前を変更して plugin を作成しないでください。常に Gradle build が生成した JAR を配布してください。