Skip to content

CH 18 · 最初のプラグイン:hello-plugin

全文字数約 2990 字所要時間約 15 分前提CH 08(プラグインツリー)、CH 17(プラグインインストール)難易度手を動かせる

この章の目標

CH 17 では他人のプラグインをインストールする方法を学びました。この章から自作を始めます —— 目標は最小限:hello-plugin を書き、実際に dsh にロードさせる。ツールは書かない、界面も触らない、検証することは 1 つだけ:自分の書いたプラグインが dsh に発見され、ロードされ、実行される。このステップが通れば、CH 19 から CH 23 のおもしろい部分(ツール、フック、UI、公開)はすべてその上に育ちます。

まず心構え:プラグインは神秘的なものではありません。CH 08 で「すべてはプラグイン」と述べましたが、逆は:dsh に 1 つ能力を増やしたいなら、apply 関数を export する小さなモジュールを書く。それがプラグイン全体の骨格です。

実操作:hello-plugin を書く

任意の作業ディレクトリで、ディレクトリを作ります(まずそのディレクトリへ cd、それから実行):

powershell
New-Item -ItemType Directory -Path "hello-plugin\src" -Force

次に hello-plugin\src\hello-plugin.js を作成し、次のように書きます:

js
export const name = 'hello-plugin'

export function apply(ctx) {
  console.log('[hello-plugin] plugin loaded!')
}

コアロジックはたったの 2 行:プラグインがロードされたとき、[hello-plugin] plugin loaded! を表示。これが表示できれば「自分のコードが dsh に実行された」ことの証明 —— これが最初のマイルストーン。

ここでは公式の TS 例ではなく JS を使います:グローバルインストールした dsh には tsx ランタイムが入っておらず、直接 .ts をロードするとエラーになります;.js ならビルド不要、依存ゼロ、5 分で動きます。複雑なプラグインを書くときに TypeScript とビルドチェーンを持ち込みます(CH 20 で展開)。

dsh にロードさせる

ファイルがあるだけでは、dsh はロードを知りません。dsh に「追加でこのプラグインをロードせよ」と伝える「オーバーレイ」が必要です。hello-plugin\cordis.yml を作ります(下の name のパスは例、自分のディレクトリに置き換え、下記の注意参照):

yaml
- insert:
    - id: hello
      name: 'file:///E:/software-workspace/DeepSeek%20harness%20demo/hello-plugin/src/hello-plugin.js'

3 つの注意:

  • namefile:// で始まる完全な URL である必要がありE:\...E:/... とは書けません。Windows では、dsh のモジュールローダは file:///E:/... の形のみ受け付けます;ドライブレターのパスを直接書くと Only URLs with a scheme in: file, data, and node are supported と報告されます。
  • パス内のスペースは %20 にエンコードする必要があります。たとえばディレクトリが my work space なら、my%20work%20space と書きます。
  • これは絶対パスです。 patch ファイルは設定のみを提供し、モジュール解決のルートは依然として profile ディレクトリなので、ローカルプラグインはフルパスで書く必要があります。

headless で 1 回走らせ素早く検証

Web UI は触らず、headless を使って素早くプラグインが実際にロードされたか検証します:

powershell
cd your-workspace-directory
dsh --profile headless --patch "./hello-plugin/cordis.yml" "Just reply: hi"

出力に次の 2 行が出ます:

text
[hello-plugin] plugin loaded!
hi

最初の行は起動時にあなたのプラグインが表示したもので、2 行目はタスク完了後のモデルの返答です。[hello-plugin] plugin loaded! が見えれば、最初のプラグインは動いています。

次に Web UI にロードする

headless で検証できますが、プラグインは Web UI で使うものです。まず実行中の dsh web を停止します(そうでないとポート占有)、次に patch を付けて起動:

powershell
dsh web --patch "./hello-plugin/cordis.yml"

http://127.0.0.1:3080 を開くと、dsh を起動したターミナルにも [hello-plugin] plugin loaded! が表示されます。今のところ hello-plugin に UI 効果はありません、その唯一の「アウトプット」はこのログ行ですが、これで CH 08 で話したツリーのメンバーと一緒に web プラグインツリーに入ったことが証明されます。

ターミナル出力をみてください:最初の行は dsh 起動時に出力されたプラグインロードログ、次の 2 行は Web UI の準備情報。

アンロード時の自動クリーンアップ

ctx に登録するものすべて(イベントリスナー、ツール、タイマー)は、プラグインがアンロードされるときにフレームワークによって自動でクリーンアップされます;手動で removeListenerclearInterval を呼ぶ必要はありません。手動解放が必要なリソース(ネットワーク接続など)がある場合、ctx.effect() を使ってフレームワークにクリーンアップ方法を伝えます:

js
export function apply(ctx) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('heartbeat')
    }, 5000)

    // 返された関数はプラグインがアンロードされたときに走る
    return () => clearInterval(timer)
  })
}

effect が返したクリーンアップ関数は、プラグインがアンロードされる瞬間に呼ばれます —— これは dsh がリソースライフサイクルを管理する標準的な方法です。

依存の宣言:inject

プラグインが他の能力(例:toolsllm)を必要とする場合、inject を宣言し、フレームワークがあなたのプラグインをロードする前に依存の準備を確実にします:

js
export const name = 'my-tool-plugin'
export const inject = ['tools']

export function apply(ctx) {
  // ここでは ctx.tools の利用が保証されている
  ctx.tools.register(/* ... */)
}

inject は Cordis における「サービス依存」のエントリポイントです。今はとりあえず慣れておき、ツールプラグインを書く CH 20 で正式に使います。

プラグインの 3 形態

apply 関数が最も一般的な形ですが、プラグインには 3 つの書き方があります(CH 19 でそれぞれ詳しく、ここでは概観):

形態見た目使いどき
関数export function apply(ctx) {}デフォルトの選択、本チュートリアルはこちら
オブジェクトexport default { name, apply(ctx) {} }静的メタデータも一緒に持たせたいとき
クラスexport default class extends Service {}他のプラグインにサービスを提供したいとき(CH 19 で展開)

今は 1 行だけ覚えてください:関数形態が 90% のニーズを解決、「あなたのプラグインが他のプラグインから依存される必要がある」ときだけサービス形態に

dsh にやらせる:1 つのプロンプトで完結

ここまでのステップは手動でなぞったもので、すべて学びました。でも dsh 自身は Agent —— プラグインを書く仕事もこなせるし、「自分で書いて自分で検証」もできます。

Web UI の入力欄にそのままこのプロンプトを送ってください:

text
現在の作業ディレクトリで、dsh がロードできる最小限の hello-plugin プラグインを書いてください。まず公式の dsh プラグイン開発ドキュメントを読み、プラグインの書き方とロード方法を把握し、それから公式仕様に従って実装、実際にロードされたか検証し、最後に結果とファイルの場所を教えてください。

技術的な詳細を何も伝える必要はありません —— dsh は自分で公式プラグイン開発ドキュメントを読み、どう書くか、どうロードするか、どう検証するかを自分で決めます。作業を眺め、それからファイルを開いて何を書いたか確認すれば OK。これはまさしく「すべてはプラグイン」の延長:プラグインを書く仕事も、プラグインから組み立てられた Agent に任せられる。

私が実際に走らせたところ、右上に参照した資料と生成されたファイル(package.jsonindex.jscordis.patch.yml)が表示されています。書き込み完了後、右側のファイルパネルには作業ディレクトリの新しい hello-plugin ディレクトリが直接表示 —— まさに CH 17 でインストールしたサイドバープラグインが輝く場面、ファイルマネージャに切り替えなくても何を書いたか確認できます。

よくある落とし穴

問題何が起きているか対処
Only URLs with a scheme in: file... と報告されるWindows でパスがドライブレター形式で書かれているnamefile:///E:/... のような完全 URL に変更
ファイル/モジュールが見つからないと報告パスのスペースがエンコードされていないスペースを %20 として書く
patch と再起動後に反応なし?プラグインパスまたは yml の typoidname のスペルを確認、dsh --profile web --patch ./hello-plugin/cordis.yml --dump-config でプラグインが設定ツリーにあるか確認
.ts を直接ロードしてエラー?グローバル dsh には tsx ランタイムが入っていないまずビルド不要の .js を使う、TS が必要になったらまず .js にビルドしてからロード
ポート占有で起動できない?前の dsh web がまだ動いている古いプロセスを先に停止、それから起動

この章で学んだこと

下の項目を自力で達成できれば合格です:

  • [ ] プラグインの最小形態を言える:apply(ctx) 関数を export するモジュール
  • [ ] hello-plugin を作成し、cordis.ymlfile:// URL を使って宣言できる
  • [ ] dsh --profile headless --patch ... を使って素早くプラグインがロードされたか検証できる
  • [ ] --patch を付けて web を起動し、プラグインを Web UI のプラグインツリーに持ち込める
  • [ ] ctx.effect() がリソースクリーンアップを行い、inject がサービス依存を宣言することを知っており、プラグインの 3 形態(関数 / オブジェクト / クラス)を知っている

Open Source · MIT · Community Driven