プラグイン開発ドキュメント
1. プラグインパッケージ構成
プラグインはディレクトリとして作成し、ZIP にまとめてアップロードします。最小構成は次のとおりです。
my_plugin/
├── plugin.json # プラグイン宣言(必須)
├── __init__.py # 空ファイルで可(必須)
├── runner.py # エントリーロジック(必須)
├── provider.py # provider 契約(必須、ローカルのみなら空でも可)
├── credentials.py # secrets_schema 宣言時に必須
└── README.md # ドキュメント(任意)ZIP のルートに plugin.json を直接置いてください。外側に別のディレクトリを追加しないでください。
2. plugin.json 契約
Manifest は loader の厳格な契約に従います。下の最小例はリポジトリの契約テストで検証され、アップロード時に plugin_id と package_id が非公開名前空間へ書き換えられます。
{
"plugin_id": "store.sandbox.example.text_uppercase",
"node_type": "text_uppercase",
"package_id": "store.sandbox.example.text_uppercase",
"version": "1.0.0",
"runtime_contract_version": "1.0.0",
"manifest_schema_version": "1.0.0",
"execution": "server",
"title": "文本转大写",
"category": "文本处理",
"description": "把输入文本转换为大写。",
"available_services": [
"workflow"
],
"inputs": [
{
"id": "text",
"label": "输入文本",
"data_type": "Text",
"path": "input.text"
}
],
"outputs": [
{
"id": "result",
"label": "处理结果",
"data_type": "Text",
"path": "output.result"
}
],
"config_schema": {},
"pricing_schema": {
"pricing_mode": "fixed",
"unit_credits": 1,
"unit_label": "次"
},
"secrets_schema": [],
"provider_policy": {
"provider_key": "developer.local",
"provider_type": "local",
"auth_type": "none",
"timeout_ms": 30000,
"max_retries": 0
},
"capabilities_schema": {},
"style_schema": {
"icon": "type",
"accent": "#6366f1",
"theme": "indigo",
"node_width": 420
},
"asset_schema": {},
"failure_policy": {
"refund": {
"mode": "refund_on_failure"
}
},
"permissions": {},
"metadata": {
"capabilities": [
"workflow.text_transform"
]
},
"compatibility": {
"workflow_runtime": ">=1.0.0",
"service_slug": "workflow"
},
"dependencies": {
"plugins": [],
"python": [],
"providers": []
},
"independent": true,
"entrypoint": {
"module": "runner",
"callable": "run_text_uppercase"
},
"author": {
"name": "示例开发者"
}
}ui_schema、connection_schema、output_schema、layout_schema、display_schema は手書きしないでください。ポート、設定、スタイル宣言から loader が生成します。未知のフィールドは拒否され、拡張には x_ 接頭辞を使います。
第三者プラグインの現在の呼び出し単価、最低価格、作者とプラットフォームの分配率は、 開発者センターから読み込む現在の契約を基準にします。文書中の過去の数値で精算を実装しないでください。
3. runner.py エントリーポイント
エントリーポイントは async 関数です。設定は request["node"]["config"]、接続入力は request["node"]["input"] にあります。戻り値には manifest の出力または標準メッセージだけを含めます。
from typing import Any
async def run_text_uppercase(request: dict[str, Any]) -> dict[str, Any]:
node = request.get("node", {}) if isinstance(request.get("node"), dict) else {}
config = node.get("config", {}) if isinstance(node.get("config"), dict) else {}
inputs = node.get("input", {}) if isinstance(node.get("input"), dict) else {}
text = str(inputs.get("text") or config.get("text") or "")
return {
"result": text.upper(),
"message": "処理完了",
"message_code": "TEXT_UPPERCASE_COMPLETED",
}outputs または派生 output_schema に宣言されていない業務フィールドを返すと契約エラーになります。runner.py からプラットフォームの環境変数や秘密情報を読まないでください。
4. セキュリティ制限(審査でブロック)
プラットフォームと他のユーザーを守るため、プラグインコードでは次の操作を禁止します。自動審査でブロックされます。
- os / sys / subprocess / socket / shutil などのシステムモジュールをインポートする
- os.environ、環境変数、プラットフォームの秘密情報へアクセスする
- eval / exec / open / __import__ などの危険な関数を呼び出す
- ローカルファイルシステムを読み書きする、または子プロセスを起動する
許可:httpx(HTTP 呼び出し)、json / typing / datetime / re / base64 / hashlib / math / decimal / uuid / collections / itertools、app.core.errors、app.plugins.toolkit。パッケージ内モジュールは相対インポートを使います。リスト外のインポートが自動的に機能を付与することはありません。
API キーなどのユーザー資格情報は secrets_schema で宣言し、permissions.secrets で明示的に許可してください。config_defaults、ソース、README、ZIP 例に秘密情報を置かないでください。
5. 提出と審査の流れ
- ZIP を作成し、開発者申請ウィザードから非公開サンドボックス(自分だけが閲覧可能)へアップロードします。
- 非公開ロードテストを実行します。本番 loader で manifest、エントリーポイント、ポート、料金、資格情報の契約を検証します。runner の実行や公開 registry への書き込みは行いません。
- 審査と提出を実行し、契約検証、AST 機能スキャン、Bandit 高リスクスキャンを行います。AI による事前審査は含みません。
- すべての検査に合格すると manual_review となり、人手審査に進みます。失敗した場合は rejected となり、再アップロード、再テスト、再提出が必要です。
- 承認後に approved 領域へコピーしてホットリロードします。ロード成功後にのみ approved になります。
6. 収益と分配
公開後に実際の有料呼び出しが発生すると、呼び出しクレジットは精算待ちとなり、精算サイクル後に作者残高へ反映されます。失敗、返金、過去の精算は不変のサーバー台帳に従います。
作者分配率、プラットフォーム比率、最低価格は、収益 API の権威ある契約フィールドを開発者センターが表示します。このページは業務定数を複製せず、契約変更時の二重管理を避けます。