cc-safety-net/api サブパスからエクスポートされる checkCommand です。型、投げられる TypeError のメッセージ、1 回の呼び出しが読むものはライブラリ API がリファレンスとして持っています。このページは、その周りの判断を扱います。
プロセス内のチェックか、インストール済みの hook か
どちらも同じ guard を通ります。違うのは、誰が呼び出しを所有するかです。
判断の目安は最後の行から導けますが、本質は所有権です。ユーザーが対応エージェント CLI のいずれかを使っているなら、インストールコマンドを案内して連携に任せてください。各エージェントの組み込み方は連携アーキテクチャにあります。自分自身がコマンドを実行するプログラムなら、関数を呼んでください。自分のランタイム向けに hook 設定のジェネレーターを作るのは、すでに存在する連携を作り直すことになります。
パッケージのルートエクスポートはエンジンではなく OpenCode のプラグインオブジェクトです。
checkCommand は cc-safety-net/api から import してください。組み込み時のポリシー解決
組み込み専用の設定はありません。呼び出しが読むファイルは hook が読むものと同じで、どのプロジェクトのファイルになるかはcwd 引数が決めます。
- ユーザースコープ:
~/.cc-safety-net/policy.jsonと~/.cc-safety-net/rules/。CC_SAFETY_NET_HOMEが設定されていればその場所に移ります。これは運用者自身のベースラインで、すべての呼び出しに適用されます。 - プロジェクトスコープ:
<cwd>/.cc-safety-net/policy.jsonと<cwd>/.cc-safety-net/rules/。渡したパスそのものから解決され、親ディレクトリをさかのぼる探索は行いません。セッションのサブディレクトリを追跡するホストは、リポジトリのルートをcwdとして渡してください。そうしないと、コミットされたプロジェクトポリシーが読み込まれません。 - マージ:プロジェクトファイルはユーザーファイルの上にフィールド単位で重なります。両スコープの保護対象パスのリストは和集合になり、プロジェクトファイルの
auditは無視され、プロジェクトが緩めたフィールドは黙って適用されるのではなく報告されます。マージの仕様はポリシーが持っています。 - 環境変数:
CC_SAFETY_NET_LEVELと各機能のスイッチは呼び出しのたびにprocess.envから読まれ、レベルはポリシーファイルの設定より引き上げることしかできません。環境変数を参照してください。
cwd がそれを選ぶからです。複数のチェックアウトを同時に扱うホストは、正しいディレクトリを渡すだけでそれぞれに正しいポリシーを得られます。追加の API は要りません。
呼び出し側を書く前に知っておく価値のある挙動が 2 つあります。読み取りと検索が可能なディレクトリとして stat できない cwd は、例外ではなく fail-closed の deny を返します。そのため、パスの打ち間違いは別のプロジェクトを解析するのではなくコマンドのブロックになります。もう 1 つ、cwd の欠落や文字列でない command といった不正な入力は TypeError を投げます。これはコマンドに対する判定ではなく、呼び出し側のバグです。
API が保証するもの
次の点はマイナーバージョンをまたいで保たれるので、ホストはこれを前提に実装できます。- 入力の形
{ command, cwd }(cwdは絶対パスのディレクトリ)と、2 つの結果の kind、すなわちallowと、reason文字列および省略可能なruleIdを持つdeny。 denyはコマンドを実行してはいけないという意味で、throw も同じ意味です。throw が allow になることはありません。cwdがポリシー解決の基準になります。コマンド内の相対パスも、プロジェクトの.cc-safety-net/設定も、渡したcwdを基準に解決され、process.cwd()は使われません。- API のパスは監査レコードを書かず、ネットワークリクエストも行いません。呼び出しが読むのはローカルのポリシーファイル、ファイルシステムの状態、環境設定で、マシンの外には何も出ません。
reason の文言も変わります。そのため、アップグレードによって、テストのフィクスチャが依存していたコマンドが allow から deny に変わることもあります。分岐は kind で行ってください。reason と ruleId は突き合わせの対象ではなく、人間とログのために残すものです。挙動をテストしている他の依存関係と同じように、バージョンは lockfile で固定してください。
実装例:プラグインホスト
プラグイン機構を持つホストは、たいてい実行前の hook を公開していて、そこでツール呼び出しを拒否できます。連携点はそれだけです。プラグインはホストのイベントを{ command, cwd } に対応付け、checkCommand を呼び、deny をホスト側の拒否表現に変換します。
- チェックするのはコマンド系ツールだけです。
checkCommandが解析するのはシェルコマンドの文字列です。ホストの read、write、search といったツールはコマンド系ツールではないので、shell ツールだけを渡し、他はそのまま通してください。 - **
cwdは絶対パスのプロジェクトルートです。**ホスト側の呼び名が何であれ、その値がプロジェクトポリシーを選び、コマンド内の.envのような相対パスの基準になります。 - throw はブロックです。
catchは契約の fail-closed 側です。投げられたエラーをそのまま実行に進ませるホストは、壊れたチェックを allow に変えてしまいます。 - **不正なイベントもブロックです。**同梱のプロセス内連携はまさにこう動きます。command フィールドが欠けている、あるいは文字列でないイベントは、スキップではなくブロックされます。解析する対象が存在しないからです。
運用上の注意
- **ログは自分のものです。**このパスでは監査レコードが書かれないので、ホストが記録しない拒否は何も残りません。
logsコマンドと GUI のダッシュボードが表示するのは hook とプラグインの判定であって、組み込み実行のものではありません。 - **運用者は通常のツールで設定します。**組み込みホストも同じポリシーファイルを読むため、
status、doctor、explainはそのままホストの挙動の説明になります。ただし、cwdとして渡すのと同じディレクトリで実行した場合に限ります。なぜブロックされたのかを聞かれたら、explain トレースを案内してください。 - **
ruleIdは診断用です。**表示する、記録する、ルールを引くために使う。いずれも問題ありませんが、分岐条件にはしないでください。 - **呼び出しは同期です。**まとめて処理したいならループで回してください。非同期版もバッチ版もありませんし、追加したところで呼び出しがすでに行っているファイル読み取りを隠すだけです。
- バージョンはパッケージから読めます。
cc-safety-net/package.jsonはエクスポートされたサブパスなので、依存関係のバージョンを報告するホストはこれも含められます。
関連ページ
- ライブラリ API:
checkCommandの完全なリファレンス。 - 連携アーキテクチャ:ホストが自分でコマンドを実行する場合に、この関数が置き換えるインストール済み連携。
- ポリシーと環境変数:呼び出しが読むものすべて。
- ブロックされるコマンド:deny になり得るものの一覧。