deckide 3.5.32 → 3.5.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,7 @@
11
11
  - **マルチリポジトリ対応** — ワークスペース内の複数 Git リポジトリを自動検出
12
12
  - **ファイルエクスプローラー** — ファイル・フォルダの作成、削除、コンテキストメニュー
13
13
  - **ターミナルバッファ永続化** — WebSocket 切断・再接続時にターミナル出力を完全復元
14
+ - **セッション保存ブラウザ** — Codex / Claude Code と同じ Chromium プロファイルを Deck IDE 内から操作
14
15
  - **Basic 認証** — CLI またはブラウザ設定画面から認証の有効化が可能
15
16
  - **CLI ツール** — `deckide` コマンドでバックグラウンド起動・停止・設定管理
16
17
  - **モバイル対応** — タッチ操作、スワイプでのデッキ切替、レスポンシブレイアウト
@@ -98,6 +99,10 @@ deckide config reset 設定リセット
98
99
  3. コミットメッセージを入力してコミット
99
100
  4. プッシュ / プル / フェッチで同期
100
101
 
102
+ ### ブラウザ
103
+
104
+ サイドバーのブラウザアイコンから、永続プロファイル付き Chromium を起動・停止・操作できます。既定では `~/.local/share/agent-browser/profile` を使うため、Codex / Claude Code の Playwright MCP ブラウザとログインセッションを共有できます。音声は PulseAudio / PipeWire pulse 互換の monitor source を ffmpeg で WebM/Opus にして Deck IDE へ転送します。音を出す環境には `ffmpeg` と `pactl`、または `AGENT_BROWSER_AUDIO_SOURCE` で指定した monitor source が必要です。
105
+
101
106
  ## プロジェクト構成
102
107
 
103
108
  ```
@@ -156,6 +161,31 @@ ide/
156
161
  | `TERMINAL_BUFFER_LIMIT` | ターミナルバッファ上限 (bytes) | 500000 |
157
162
  | `TRUST_PROXY` | プロキシヘッダーを信頼 | false |
158
163
  | `DECKIDE_DATA_DIR` | データディレクトリ | ~/.deckide |
164
+ | `AGENT_BROWSER_CHROME` | Chromium / Chrome 実行ファイル | Playwright Chromium または PATH から自動検出 |
165
+ | `AGENT_BROWSER_PROFILE_DIR` | ブラウザプロファイル | ~/.local/share/agent-browser/profile |
166
+ | `AGENT_BROWSER_OUTPUT_DIR` | ブラウザログ出力先 | ~/.local/share/agent-browser/output |
167
+ | `AGENT_BROWSER_FFMPEG` | ffmpeg 実行ファイル | PATH から自動検出 |
168
+ | `AGENT_BROWSER_PACTL` | pactl 実行ファイル | PATH から自動検出 |
169
+ | `AGENT_BROWSER_AUDIO_SOURCE` | 音声キャプチャ元 | 既定 sink の monitor source |
170
+
171
+ ## MCP / OAuth
172
+
173
+ Deck IDE は MCP Streamable HTTP エンドポイントを提供します。
174
+
175
+ | 用途 | URL |
176
+ |------|-----|
177
+ | MCP | `http://localhost:8787/mcp` |
178
+ | Protected Resource Metadata | `http://localhost:8787/.well-known/oauth-protected-resource/mcp` |
179
+ | Authorization Server Metadata | `http://localhost:8787/.well-known/oauth-authorization-server` |
180
+ | Dynamic Client Registration | `http://localhost:8787/oauth/register` |
181
+ | Authorization Endpoint | `http://localhost:8787/oauth/authorize` |
182
+ | Token Endpoint | `http://localhost:8787/oauth/token` |
183
+
184
+ OAuth は Authorization Code + PKCE (`S256`) を使用します。MCP クライアントは Dynamic Client Registration でクライアント登録し、`deckide:all` scope の Bearer トークンを取得して `/mcp` に接続します。
185
+
186
+ Basic 認証が有効な場合、OAuth の認可画面 (`/oauth/authorize`) も Basic 認証で保護されます。MCP 呼び出し自体は `Authorization: Bearer <access_token>` を使用します。
187
+
188
+ MCP tools からはワークスペース、ファイル、ディレクトリ、デッキ、端末、Git、WebSocket 管理、設定、サーバー停止、低レベル API 呼び出しを利用できます。端末はMCP専用に `write` / `read_buffer` / `resize` も提供します。
159
189
 
160
190
  ## API
161
191
 
@@ -199,6 +229,17 @@ ide/
199
229
  | DELETE | `/api/terminals/:id` | 削除 |
200
230
  | WS | `/api/terminals/:id` | WebSocket 接続 |
201
231
 
232
+ ### ブラウザ
233
+
234
+ | メソッド | パス | 説明 |
235
+ |---------|------|------|
236
+ | GET | `/api/browser/status` | 状態取得 |
237
+ | POST | `/api/browser/start` | 起動 |
238
+ | POST | `/api/browser/stop` | 停止 |
239
+ | POST | `/api/browser/navigate` | URL へ移動 |
240
+ | WS | `/api/browser/ws` | 画面・入力ストリーム |
241
+ | WS | `/api/browser/audio/ws` | 音声ストリーム |
242
+
202
243
  ### Git
203
244
 
204
245
  | メソッド | パス | 説明 |
@@ -233,7 +274,7 @@ ide/
233
274
 
234
275
  ## WebSocket プロトコル
235
276
 
236
- ターミナル接続は `/api/terminals/:id` WebSocket エンドポイントを使用します。
277
+ ターミナル接続は `/api/terminals/:id` WebSocket エンドポイントを使用します。ブラウザ接続は `/api/browser/ws` で JPEG screencast とマウス・キー入力イベントを送受信します。ブラウザ音声は `/api/browser/audio/ws` で WebM/Opus のバイナリチャンクを送ります。
237
278
 
238
279
  **クライアント → サーバー:**
239
280
  - `{ type: "claim" }` — リサイズ権限を取得
package/bin/deckide.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import path from 'node:path';
@@ -18,7 +18,7 @@ export const corsMiddleware = async (c, next) => {
18
18
  }
19
19
  }
20
20
  c.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
21
- c.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
21
+ c.header('Access-Control-Allow-Headers', 'Content-Type,Authorization,Accept,MCP-Protocol-Version,Mcp-Session-Id,Last-Event-ID');
22
22
  if (c.req.method === 'OPTIONS') {
23
23
  return c.body(null, 204);
24
24
  }
@@ -0,0 +1,40 @@
1
+ import { Hono } from 'hono';
2
+ import { handleError, readJson } from '../utils/error.js';
3
+ export function createBrowserRouter(browserService) {
4
+ const router = new Hono();
5
+ router.get('/status', async (c) => {
6
+ return c.json(await browserService.getStatus());
7
+ });
8
+ router.post('/start', async (c) => {
9
+ try {
10
+ await browserService.start();
11
+ return c.json(await browserService.getStatus());
12
+ }
13
+ catch (error) {
14
+ return handleError(c, error);
15
+ }
16
+ });
17
+ router.post('/stop', async (c) => {
18
+ try {
19
+ await browserService.stop();
20
+ return c.json(await browserService.getStatus());
21
+ }
22
+ catch (error) {
23
+ return handleError(c, error);
24
+ }
25
+ });
26
+ router.post('/navigate', async (c) => {
27
+ try {
28
+ const body = await readJson(c);
29
+ if (!body?.url || typeof body.url !== 'string') {
30
+ return c.json({ error: 'url is required' }, 400);
31
+ }
32
+ await browserService.navigate(body.url);
33
+ return c.json(await browserService.getStatus());
34
+ }
35
+ catch (error) {
36
+ return handleError(c, error);
37
+ }
38
+ });
39
+ return router;
40
+ }
@@ -75,7 +75,6 @@ export function createDeckRouter(db, workspaces, decks, terminals) {
75
75
  }
76
76
  });
77
77
  const deleteDeckStmt = db.prepare('DELETE FROM decks WHERE id = ?');
78
- const deleteTerminalsByDeckStmt = db.prepare('DELETE FROM terminals WHERE deck_id = ?');
79
78
  function closeDeckTerminalSockets(sockets, reason) {
80
79
  sockets.forEach((socket) => {
81
80
  try {
@@ -103,7 +102,6 @@ export function createDeckRouter(db, workspaces, decks, terminals) {
103
102
  closeDeckTerminalSockets(session.sockets, 'Deck deleted');
104
103
  session.kill();
105
104
  });
106
- deleteTerminalsByDeckStmt.run(deckId);
107
105
  deleteDeckStmt.run(deckId);
108
106
  decks.delete(deckId);
109
107
  return c.json({ deleted: true });