maestro/docs/workspace-apps-bridge.md
oss-sync b857c33ef6
Some checks failed
CI / build-and-test (push) Has been cancelled
sync: update from private repo (f6d625db)
2026-06-26 03:35:45 +00:00

142 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ワークスペース・アプリ ブリッジ仕様(アプリ作者=エージェント向け)
ワークスペース・アプリは、ワークスペースの `apps/{name}/index.html` に置く HTML 製の小さな
GUI です。ワークスペースの **「アプリ」タブ**に一覧表示され、「開く」を押すと `AppRunner`
サンドボックス iframe で実行します(ファイルタブの「アプリとして実行」からも起動できます)。
アプリはワークスペースのファイルを **postMessage ブリッジ経由でのみ** 読み書きできます。
このドキュメントは、生成する HTMLアプリが従うべきブリッジ API を定めます。
> **エージェントへ**: ユーザーから「ワークスペース・アプリを作って」と頼まれたら、この仕様に
> 従った**自己完結 HTML** を `apps/{name}/index.html` に Write してください(外部リソース禁止・
> インライン JS/CSS のみ)。動くひな型は `docs/examples/workspace-apps/note-editor/``index.html`
> `app.json`)。この仕様自体は `ReadToolDoc({ name: "workspace-apps" })` でいつでも読めます。
## 実行環境(重要な制約)
- アプリは `<iframe sandbox="allow-scripts">` で動きます。**`allow-same-origin` は付きません。**
そのため不透明オリジンopaque originで動作し、親ウィンドウの DOM・Cookie・セッション・
localStorage には一切アクセスできません。
- **資格情報は渡されません。** ファイル I/O は親が代理します。代理は実行中ユーザーのセッションで
行われるため、アプリはそのユーザーの権限を超えられません(メンバーシップ・編集権限・パス
ガードはサーバ側でそのまま効きます)。
- **外部ネットワークは使えません。** `fetch` / `XMLHttpRequest` による外部通信は想定外です
(配信はサンドボックス + CSP で絞られます)。データの入出力は必ずブリッジ経由で行ってください。
- V1 の主対象は **単一の自己完結 `index.html`**JS/CSS をインライン化したもの)。複数ファイル
構成(`apps/{name}/` 配下の相対参照)はベストエフォートで、相対 `src`/`href` は親が raw URL に
書き換えます。確実に動かしたいなら 1 ファイルにまとめてください。
## マニフェスト `app.json`(任意)
アプリフォルダに `apps/{name}/app.json` を置くと、「アプリ」タブの表示情報を指定できます。
**任意**で、無ければフォルダ名と `index.html` がそのまま使われます。形式が壊れていても無視され、
フォールバックされます(アプリ一覧は壊れません)。
```json
{
"title": "ファイル・ノート",
"description": "output/ のファイルを閲覧してメモを保存するアプリ",
"entry": "index.html"
}
```
| キー | 必須 | 説明 |
|---|---|---|
| `title` | 任意 | 一覧に出す表示名(無ければフォルダ名) |
| `description` | 任意 | 一覧に出す短い説明 |
| `entry` | 任意 | アプリフォルダ基準の HTML エントリ(既定 `index.html`)。**必ず `apps/{name}/` 配下**に収めること(`..` や絶対パスは拒否され、`index.html` にフォールバックされる) |
## ブリッジ・プロトコル
要求は `window.parent.postMessage(req, '*')` で送り、応答は `window``message` イベントで
受け取ります。各要求には一意の `id` を付け、応答の `id` と突き合わせてください。
### 要求
```js
// 読み取り
window.parent.postMessage({ id, type: 'readFile', path: 'output/data.json' }, '*');
// 一覧dir 省略でワークスペース直下)
window.parent.postMessage({ id, type: 'listFiles', dir: 'output' }, '*');
// 書き込みcontent は文字列)
window.parent.postMessage({ id, type: 'writeFile', path: 'output/請求書.html', content: '<html>…' }, '*');
// 削除(常に確認ダイアログが出る)
window.parent.postMessage({ id, type: 'deleteFile', path: 'output/old.txt' }, '*');
```
### 応答
```js
window.addEventListener('message', (e) => {
const r = e.data; // { id, ok, data } または { id, ok:false, error }
if (r.id !== myRequestId) return;
if (r.ok) { /* r.data を使う */ } else { /* r.error を表示 */ }
});
```
応答ペイロード:
| type | 成功時 `data` |
|-------------|-------------------------------------------------|
| `readFile` | `{ path, content }`content は UTF-8 テキスト)|
| `listFiles` | `{ dir, entries }`entries はファイル/フォルダ)|
| `writeFile` | `{ path, bytes }` |
| `deleteFile`| `{ deleted, skipped }` |
失敗時は `{ id, ok:false, error }`
## パスの規則
- パスは必ず **ワークスペース files 直下からの相対パス**。先頭 `/``..`・ドライブ文字
(`C:`)・バックスラッシュは拒否されます(クライアントとサーバの二重ガード)。
- アプリは自分のワークスペースの外には出られません。
## 書き込みの確認ポリシー
- **確認なしで書ける場所**: `output/` 配下、および自分のアプリの `apps/{name}/data/` 配下。
- それ以外のパスへの `writeFile` は、親が確認ダイアログ(「アプリ『{name}』が {path} に書き込もう
としています。許可しますか?」)を出し、ユーザーが許可したときだけ書き込みます。
- `deleteFile` は場所に関わらず常に確認ダイアログが出ます。
データを残すアプリは、原則として `output/`(ユーザーに見せる成果物)か
`apps/{name}/data/`(アプリ内部の状態)に書いてください。そうすればユーザーを確認で煩わせません。
## 最小サンプル(自己完結 index.html
```html
<!doctype html><meta charset="utf-8">
<button id="save">保存</button>
<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));
});
document.getElementById('save').onclick = async () => {
await call({ type: 'writeFile', path: 'output/note.txt', content: 'hello' });
alert('保存しました');
};
</script>
```
## ネットワーク制約(重要)
アプリ HTML には `default-src 'none'; connect-src 'none'; ...` の CSP が自動注入され、
**外部ネットワークfetch/XHR/WebSocket/sendBeacon/フォーム外部 POSTはすべて遮断**される。
アプリのファイル I/O は postMessage ブリッジ(親が代理)だけが経路。これは、ユーザーから
渡されたワークスペースのファイル内容を外部へ持ち出されないための exfil 対策。
V1 のアプリは **自己完結 HTML**(インライン JS/CSS、`data:` 画像)で書くこと。
外部 CDN・外部 API・別ファイルの `<script src>` は CSP で読み込めない。