Studio — Web UI
ツールチェーン全体をブラウザのタブ 1 つで。ライブログとワンクリックのロールバック付き。設計上、localhost 専用です。
handbook studio # → http://127.0.0.1:4860
handbook studio --port 5000 # or: pnpm studio --port 5000CLI と同じコードパス、同じ設定解決、ディスク上の同じ成果物 — 操作方法が違うだけです。
ビルドステップはゼロです。 UI は、CSS をインライン化した手書きの HTML ファイル 1 枚と 素の JS でできています。バンドラーもフレームワークもなく、CDN から取得するものもありません。 瞬時にロードされ、ネットワークケーブルを抜いても動作します。
できること
| 領域 | 何をするか |
|---|---|
| リポジトリ | ソースツリー + 作業ディレクトリを URL セーフな名前で登録する |
| 生成 | フルパラメータセット、SSE でライブ配信されるログ、実行途中でのキャンセル |
| ハンドブックブラウザ | レンダリング済みハンドブックをその場で読む |
| 影響グラフ | あるステージがどのファイルを所有し、何が呼び込んできて、何を呼び出すか |
| ソースビューア | 任意のカードの背後にある実ファイルを、引用された行で開く |
| プラン | リクエストを入力し、読み取り専用エージェントの作業を眺め、プランを読む |
| 適用 / ロールバック | ドライラン、適用、すべてのバックアップの一覧、ワンクリックのロールバック |
| 再同期 | 生きているツリーに対してハンドブックを前へ進める — ケースディレクトリの組み立ては不要 |
| 履歴 | リポジトリごとの変遷: 各実行が何を、いつ変えたか |
ジョブ
生成、プランニング、再同期は、Server-Sent Events で配信されるログをキャプチャした バックグラウンドジョブ として実行されます。
- 1 リポジトリにつき同時に 1 ジョブ。 パイプラインの成果物は、同じ作業ディレクトリへの 並行書き込みに対して安全ではありません。2 つ目の開始は明確なメッセージ付きで拒否されます。
- キャンセル可能。 すべてのジョブは
AbortControllerを持ち、そのシグナルは実行中の LLM リクエストにまで届きます。キャンセルとはキャンセルのことであり、「ログの表示をやめる」 ことではありません。 - ステータス:
running→succeeded|failed|cancelled。完全なログが保持される ため、終了後に何が起きたかを読むことができます。
設定
Studio は 他のすべてのコマンドと同じレイヤー から設定を解決します — フラグ、環境、
.env カスケード、handbook.config.yaml、デフォルト:
handbook studio --model gpt-4o --base-url https://my-proxy/v1 --port 5000
handbook --env prod studioUI から開始された生成ジョブは、CLI が見るのと同じ設定ファイルレイヤーを見ます。そのため
detail、narrateLang、readWorkers などはすべて YAML から機能します。
セキュリティモデル
Studio は ローカルツール です。外部公開向けに堅牢化されておらず、そのふりもしません。
- デフォルトで
127.0.0.1にバインドします。 - CSRF ガードはソケットではなく
Hostリクエストヘッダーをチェックします。 ループ バックのホスト名だけが通過します。 POSTはapplication/jsonを要求します。 これは古典的なクロスオリジン HTML フォーム攻撃をブロックします。- リポジトリ名はファイルシステムに触れる前に
^[A-Za-z0-9][A-Za-z0-9._-]*$で検証され、 パスは realpath で正規化されます。 - ソースとハンドブックのファイル配信は、登録されたルートにサンドボックス化されています。
コンテナ内で
pnpm run docker:studio # docker compose up --build studio公開されたポートに到達できるようにするには、コンテナは 0.0.0.0 にバインドしなければ
なりません(docker-compose.yml の HANDBOOK_STUDIO_HOST=0.0.0.0)。
動作するのは http://localhost:4860 だけです
LAN の IP でもコンテナ名でもありません。ホストからブラウズすれば依然として Host: localhost:4860 が
送られて通過します。LAN の IP やコンテナのホスト名を指名するリクエストは、設計どおり 403 で
拒否されます。リモートアクセスは、意図的に未実装のままにされた別個の機能です — 明示的な許可リストが
必要になるでしょう — この防御の欠陥ではありません。
状態
~/.handbook-studio/
studio.json the repository registry (schema-validated on read)
work/<name>/ auto-created work dirs for repos that did not bring their own--state-dir で移動できます。それ以外のすべて — ハンドブックの成果物、変遷の履歴 — は
各リポジトリ自身の作業ディレクトリにあるので、状態ディレクトリを削除して失われるのは
レジストリだけで、重要なものは何も失われません。
スクリプトから叩く
UI は単なるクライアントです。HTTP API はスクリプトに使えるほど安定しています:
curl -s http://localhost:4860/api/repos | jq
curl -s -X POST http://localhost:4860/api/repos \
-H 'content-type: application/json' \
-d '{"name":"api","sourceRoot":"/Users/me/code/api","workDir":"/Users/me/work/api"}'
curl -s -X POST http://localhost:4860/api/repos/api \
-H 'content-type: application/json' \
-d '{"action":"analyze"}'
curl -N http://localhost:4860/api/jobs/<job-id> # SSE log stream完全なルート一覧はパッケージの READMEに あります。