5.6 KiB
ワークスペース・アプリの作り方(エージェント向け)
ユーザーから「ワークスペース・アプリを作って」「○○用の GUI を作って」などと頼まれたときの
手順です。専用ツールは不要 — edit: true の movement で Write/Edit を使って HTML を書く
だけで成立します。
何を作るか
ワークスペースの apps/{name}/index.html に置く、自己完結した 1 枚の HTML(インライン
JS/CSS、data: 画像のみ)。ユーザーはワークスペースの「アプリ」タブから「開く」で起動します。
アプリはサンドボックス iframe(opaque origin、allow-same-origin 無し)で動き、ファイル I/O は
親への postMessage ブリッジ経由のみです。
守るべき制約(重要)
- 外部ネットワーク禁止:
default-src 'none'; connect-src 'none'の CSP が自動注入される。 外部 CDN・外部 API・別ファイルの<script src>・Web フォントは読み込めない。すべてインライン化する。 - 資格情報は渡らない: I/O は親がユーザーのセッションで代理する。ユーザーの権限を超えられない。
- パスはワークスペース相対:
output/...・apps/{name}/data/...への書き込みは確認不要。 それ以外への書き込み・削除はユーザー確認が出る。..・絶対パスは拒否される。
ブリッジ API(このヘルパーをそのまま使うこと)
アプリは window.parent.postMessage({ ...req, id }, '*') で要求を送り、応答は window の
message イベントで受け取る。応答エンベロープは { id, ok: true, data } か
{ id, ok: false, error }。id を突き合わせ、ok を見て data を取り出す。
次の call() ヘルパーが正典実装。改変せずそのままコピーして使う(自己流で書くと
エンベロープを取り違えて動かない)。
<script>
let seq = 0;
const pending = new Map();
function call(req) {
return new Promise((resolve, reject) => {
const id = ++seq;
pending.set(id, { resolve, reject });
window.parent.postMessage({ ...req, id }, '*');
});
}
window.addEventListener('message', (e) => {
const r = e.data;
const p = pending.get(r && r.id);
if (!p) return;
pending.delete(r.id);
r.ok ? p.resolve(r.data) : p.reject(new Error(r.error));
});
</script>
使い方(call() は成功時に応答の data を返す):
const { entries } = await call({ type: 'listFiles', dir: 'output' }); // 一覧
const { content } = await call({ type: 'readFile', path: 'output/x.md' }); // 読み取り
await call({ type: 'writeFile', path: 'output/note.md', content: '...' }); // 書き込み
await call({ type: 'deleteFile', path: 'output/old.txt' }); // 削除(常に確認)
確認なしで書ける場所は output/ 配下と自分のアプリの apps/{name}/data/ 配下のみ。
それ以外への writeFile・すべての deleteFile はユーザー確認ダイアログが出る。
..・絶対パスは拒否される。
ひな型をコピーする
ワークスペース内の readonly/app-templates/ に、複数のアーキタイプテンプレートが
seed 済み(workspace-app ピース実行時に自動コピーされる)。Read できる実ファイルなので、
Glob({ pattern: "readonly/app-templates/*/index.html" }) で一覧を確認できる。
| フォルダ | 用途 |
|---|---|
note-editor/ |
output/note.md を読み書きするテキストエディタ |
data-viewer/ |
CSV・JSONL ファイルをテーブル表示するビューア |
form-input/ |
フォームで入力した内容を output/ に書き出す |
dashboard/ |
複数ファイルの集計結果をカードで表示するダッシュボード |
各フォルダに index.html・app.json・e2e.example.json が入っている。
e2e.example.json は後述の E2E テストに直接渡せる入力例。
いちばん近いものを Read readonly/app-templates/{名前}/index.html で読み、
apps/{name}/index.html に Write するのが最短。テンプレには上記の正しいブリッジ実装が
組み込み済みなので、ファイル I/O もそのまま動く。
apps/{name}/app.json(任意)で一覧の表示名・説明・エントリを指定できる(無ければフォルダ名)。
E2E テスト(verify ステップ)
workspace-app ピースの verify ステップは、作成したアプリを TestWorkspaceApp ツールで自動 E2E テスト する。
- ヘッドレスブラウザでアプリを実際に起動
seed_filesで指定した入力データをワークスペースに書き込んでからロードactionsに沿ってクリック・入力・スクリーンショット操作を実行expect条件(テキスト含有・ファイル存在・要素可視)を検証
テストに通過した場合のみ verify ステップを完了とする。失敗した場合はエラー内容と スクリーンショットが返るので、それをもとに HTML を修正して再実行する。
E2E の入力例は各テンプレートの e2e.example.json を参照。詳細な使い方は
ReadToolDoc({ name: "TestWorkspaceApp" }) で取得できる。
仕上げ
- 書き込んだら、ユーザーに「アプリ」タブの「開く」で起動できることを一言伝える。
- アプリ名(フォルダ名)は内容が分かる短い英小文字+ハイフンにする(例:
invoice-gen)。