# 機能設計書 59-プラグインシステム

## 概要

本ドキュメントは、OpenCodeのプラグインシステムについて、その設計仕様を記述するものである。

### 本機能の処理概要

プラグインシステム機能は、OpenCodeの機能を外部から拡張するためのプラグインアーキテクチャを提供する。認証プロバイダ、カスタムツール、イベントフック、LLM設定のカスタマイズなど、様々な拡張ポイントを通じてOpenCodeの機能を拡張できる。

**業務上の目的・背景**：OpenCodeは様々なAIプロバイダ（OpenAI、Anthropic、GitHub Copilot等）との統合や、企業固有の認証方式、カスタムツールなどに対応する必要がある。プラグインシステムにより、コア機能を変更せずに柔軟な拡張が可能になる。

**機能の利用シーン**：
- ChatGPT Pro/Plus（Codex）OAuth認証の追加
- GitHub Copilot認証の追加
- 企業固有の認証プロバイダ統合
- カスタムツールの追加
- LLMパラメータのカスタマイズ
- イベント監視・ロギング

**主要な処理内容**：
1. 内部プラグインの読み込み（CodexAuthPlugin、CopilotAuthPlugin、GitlabAuthPlugin）
2. npm/ファイルプラグインのインストールと読み込み
3. Hooksオブジェクトの収集と管理
4. イベント発火時のフック実行（trigger関数）
5. 認証フローのカスタマイズ

**関連システム・外部連携**：npm registry、OpenAI OAuth、GitHub OAuth、GitLab OAuth

**権限による制御**：プラグイン実行は設定ファイルで制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | レイアウト | 補助機能 | 認証設定画面からプラグイン認証を利用 |

## 機能種別

拡張機構 / プラグインシステム

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| plugin | string[] | No | プラグイン指定（package@version形式またはfile://） | 有効なnpmパッケージ名またはファイルパス |

### 入力データソース

- 設定ファイル（config.plugin配列）
- 内部プラグイン（INTERNAL_PLUGINS）
- 環境変数（OPENCODE_DISABLE_DEFAULT_PLUGINS）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| hooks | Hooks[] | 登録されたフックの配列 |
| input | PluginInput | プラグインに渡す入力オブジェクト |

### 出力先

- 内部状態（Instance.state）
- 各フックの実行結果

## 処理フロー

### 処理シーケンス

```
1. プラグイン状態初期化（state関数）
   ├─ OpenCodeクライアント作成
   ├─ PluginInput構築
   └─ hooks配列初期化

2. 内部プラグイン読み込み
   └─ INTERNAL_PLUGINS（Codex、Copilot、GitLab）を順次初期化

3. 外部プラグイン読み込み
   ├─ BUILTIN追加（opencode-anthropic-auth）
   ├─ configから追加プラグイン取得
   ├─ npmパッケージのインストール（BunProc.install）
   └─ dynamic import + フック収集

4. フック実行（trigger関数）
   └─ 全hooksを順次実行、outputを更新

5. イベント購読（init関数）
   └─ Bus.subscribeAllでイベントをフックに配信
```

### フローチャート

```mermaid
flowchart TD
    A[プラグインシステム初期化] --> B[PluginInput構築]
    B --> C[内部プラグイン読み込み]
    C --> D[CodexAuthPlugin]
    D --> E[CopilotAuthPlugin]
    E --> F[GitlabAuthPlugin]
    F --> G{外部プラグインあり?}
    G -->|Yes| H[Config.waitForDependencies]
    G -->|No| L[初期化完了]
    H --> I[BUILTINプラグイン追加]
    I --> J[npm install or file://]
    J --> K[dynamic import]
    K --> L
    L --> M[trigger/init利用可能]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | デフォルトプラグイン | opencode-anthropic-auth@0.0.13が自動追加 | OPENCODE_DISABLE_DEFAULT_PLUGINS未設定時 |
| BR-02 | 旧プラグイン無視 | opencode-openai-codex-auth、opencode-copilot-authは無視 | プラグイン読み込み時 |
| BR-03 | 重複防止 | 同一関数の重複初期化を防止（seen Set使用） | プラグインimport時 |
| BR-04 | エラー継続 | プラグイン読み込み失敗時もエラーを通知して継続 | プラグイン読み込み時 |
| BR-05 | バージョン指定 | package@versionの最後の@で分割 | npmプラグイン解析時 |

### 計算ロジック

- パッケージ名分割: `lastIndexOf("@")`で最後の@位置を取得し、パッケージ名とバージョンを分離

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | プラグインシステム自体はDBを使用しない |

### テーブル別操作詳細

該当なし（プラグインが個別にDB操作を行う可能性あり）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | インストール失敗 | npm install失敗 | NamedError.Unknown発行、Session.Event.Error通知 |
| - | 読み込み失敗 | dynamic import失敗 | NamedError.Unknown発行、Session.Event.Error通知 |
| - | 内部プラグイン失敗 | 内部プラグイン初期化失敗 | ログ出力のみ、継続 |

### リトライ仕様

リトライは行わない。エラー時はログ出力とイベント通知後に継続。

## トランザクション仕様

該当なし

## パフォーマンス要件

- プラグイン初期化: サーバー起動時に1回のみ実行
- trigger呼び出し: フック数に比例するO(n)

## セキュリティ考慮事項

- npmプラグインはBunProc.installで信頼性検証
- file://プラグインはローカルファイルのみ許可
- プラグイン実行コンテキストはサンドボックス化されない（信頼されたコードのみ使用）

## 備考

- パッケージ名: @opencode-ai/plugin
- 内部プラグイン: CodexAuthPlugin、CopilotAuthPlugin、GitlabAuthPlugin
- デフォルトプラグイン: opencode-anthropic-auth@0.0.13
- Hooksインターフェース: 20以上の拡張ポイントを提供

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

プラグインの型定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/plugin/src/index.ts` | Plugin型、Hooks型、PluginInput型 |

**読解のコツ**:
- **20-24行目**: `ProviderContext`型 - プロバイダ情報
- **26-33行目**: `PluginInput`型 - プラグインへの入力
- **35行目**: `Plugin`型 - プラグイン関数シグネチャ
- **37-103行目**: `AuthHook`型 - 認証フック定義
- **148-234行目**: `Hooks`インターフェース - 全拡張ポイント定義

#### Step 2: プラグインローダーを理解する

プラグインの読み込み処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/opencode/src/plugin/index.ts` | Plugin namespace、state関数 |

**主要処理フロー**:
- **16-104行目**: `Plugin` namespace全体
- **19行目**: `BUILTIN`配列 - デフォルトプラグイン
- **22行目**: `INTERNAL_PLUGINS`配列 - 内部プラグイン
- **24-104行目**: `state`関数 - 初期化処理
- **42-48行目**: 内部プラグイン読み込みループ
- **56-98行目**: 外部プラグイン読み込みループ

#### Step 3: トリガー機能を理解する

フック実行処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/opencode/src/plugin/index.ts` | trigger関数、init関数 |

**主要処理フロー**:
- **106-121行目**: `trigger`関数 - フック実行
- **123-125行目**: `list`関数 - フック一覧取得
- **127-142行目**: `init`関数 - 初期化とイベント購読

#### Step 4: 内部プラグイン（Codex）を理解する

OpenAI Codex認証プラグインを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | codex.ts | `packages/opencode/src/plugin/codex.ts` | CodexAuthPlugin、OAuth処理 |

**主要処理フロー**:
- **10-14行目**: 定数定義（CLIENT_ID、ISSUER、エンドポイント）
- **21-46行目**: PKCE生成関数群
- **88-127行目**: OAuth URL構築、トークン交換
- **245-314行目**: OAuthサーバー起動・停止
- **351-624行目**: `CodexAuthPlugin`本体

#### Step 5: 内部プラグイン（Copilot）を理解する

GitHub Copilot認証プラグインを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | copilot.ts | `packages/opencode/src/plugin/copilot.ts` | CopilotAuthPlugin、デバイスフロー |

**主要処理フロー**:
- **5行目**: `CLIENT_ID`定数
- **13-18行目**: `getUrls`関数 - エンドポイント生成
- **20-327行目**: `CopilotAuthPlugin`本体
- **143-301行目**: OAuth methods定義（デバイスコードフロー）

### プログラム呼び出し階層図

```
Plugin.init() (index.ts: 127行目)
    │
    ├─ state() → プラグイン状態取得
    │      │
    │      ├─ createOpencodeClient() → SDK クライアント作成
    │      │
    │      ├─ INTERNAL_PLUGINS.forEach()
    │      │      │
    │      │      ├─ CodexAuthPlugin(input) (codex.ts)
    │      │      │      └─ return Hooks { auth, chat.headers }
    │      │      │
    │      │      ├─ CopilotAuthPlugin(input) (copilot.ts)
    │      │      │      └─ return Hooks { auth, chat.headers }
    │      │      │
    │      │      └─ GitlabAuthPlugin(input)
    │      │             └─ return Hooks { auth }
    │      │
    │      └─ plugins.forEach()
    │             │
    │             ├─ BunProc.install(pkg, version)
    │             │
    │             └─ import(plugin) → mod.forEach(fn => hooks.push(fn(input)))
    │
    ├─ hook.config?.(config) → 設定フック実行
    │
    └─ Bus.subscribeAll() → イベント購読
           │
           └─ hook.event?.({ event }) → イベントフック実行

Plugin.trigger(name, input, output) (index.ts: 106行目)
    │
    └─ hooks.forEach(hook => hook[name]?.(input, output))
```

### データフロー図

```
[入力]                   [処理]                         [出力]

設定ファイル
  config.plugin[] ──────▶ state() ────────────────────▶ hooks[]
     │                          │
     │                          ▼
内部プラグイン              PluginInput構築
  INTERNAL_PLUGINS ─────▶ { client, project, ... }
     │                          │
     │                          ▼
npmプラグイン              BunProc.install()
  package@version ──────▶ dynamic import() ───────────▶ Hooks
     │                          │
     │                          ▼
トリガー実行               trigger(name, input, output)
  イベント名 ──────────▶ hooks[name]?.(input, output) ─▶ 更新されたoutput
     │                          │
     │                          ▼
バス購読                   Bus.subscribeAll()
  Event ───────────────▶ hook.event?.({ event })
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/opencode/src/plugin/index.ts` | ソース | プラグインローダー |
| codex.ts | `packages/opencode/src/plugin/codex.ts` | ソース | Codex認証プラグイン |
| copilot.ts | `packages/opencode/src/plugin/copilot.ts` | ソース | Copilot認証プラグイン |
| index.ts | `packages/plugin/src/index.ts` | ソース | プラグイン型定義 |
| tool.ts | `packages/plugin/src/tool.ts` | ソース | ツール定義ヘルパー |
| shell.ts | `packages/plugin/src/shell.ts` | ソース | シェル型定義 |
| example.ts | `packages/plugin/src/example.ts` | サンプル | プラグイン実装例 |
| package.json | `packages/plugin/package.json` | 設定 | @opencode-ai/plugin定義 |
