mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-19 19:02:29 +08:00
refactor: extract TabSession for per-tab state isolation (v0.15.16.0) (#873)
* plan: batch command endpoint + multi-tab parallel execution for GStack Browser * refactor: extract TabSession from BrowserManager for per-tab state Move per-tab state (refMap, lastSnapshot, frame) into a new TabSession class. BrowserManager delegates to the active TabSession via getActiveSession(). Zero behavior change — all existing tests pass. This is the foundation for the /batch endpoint: both /command and /batch will use the same handler functions with TabSession, eliminating shared state races during parallel tab execution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: update handler signatures to use TabSession Change handleReadCommand and handleSnapshot to take TabSession instead of BrowserManager. Change handleWriteCommand to take both TabSession (per-tab ops) and BrowserManager (global ops like viewport, headers, dialog). handleMetaCommand keeps BrowserManager for tab management. Tests use thin wrapper functions that bridge the old 3-arg call pattern to the new signatures via bm.getActiveSession(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add POST /batch endpoint for parallel multi-tab execution Execute multiple commands across tabs in a single HTTP request. Commands targeting different tabs run concurrently via Promise.allSettled. Commands targeting the same tab run sequentially within that group. Features: - Batch-safe command subset (text, goto, click, snapshot, screenshot, etc.) - newtab/closetab as special commands within batch - SSE streaming mode (stream: true) for partial results - Per-command error isolation (one tab failing doesn't abort the batch) - Max 50 commands per batch, soft batch-level timeout A 143-page crawl drops from ~45 min (serial HTTP) to ~5 min (20 tabs in parallel, batched commands). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add batch endpoint integration tests 10 tests covering: - Multi-tab parallel execution (goto + text on different tabs) - Same-tab sequential ordering - Per-command error isolation (one tab fails, others succeed) - Page-scoped refs (snapshot refs are per-session, not global) - Per-tab lastSnapshot (snapshot -D with independent baselines) - getSession/getActiveSession API - Batch-safe command subset validation - closeTab via page.close preserves at-least-one-page invariant - Parallel goto on 3 tabs simultaneously Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: harden codex-review E2E — extract SKILL.md section, bump maxTurns to 25 The test was copying the full 55KB/1075-line codex SKILL.md into the fixture, requiring 8 Read calls just to consume it and exhausting the 15-turn budget before reaching the actual codex review command. Now extracts only the review-relevant section (~6KB/148 lines), reducing Read calls from 8 to 1. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: move batch endpoint plan into BROWSER.md as feature documentation The batch endpoint is implemented — document it as an actual feature in BROWSER.md (architecture, API shape, design decisions, usage pattern) and remove the standalone plan file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.15.16.0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: gstack <ship@gstack.dev> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
50
BROWSER.md
50
BROWSER.md
@@ -113,6 +113,56 @@ Element crop accepts CSS selectors (`.class`, `#id`, `[attr]`) or `@e`/`@c` refs
|
||||
|
||||
Mutual exclusion: `--clip` + selector and `--viewport` + `--clip` both throw errors. Unknown flags (e.g. `--bogus`) also throw.
|
||||
|
||||
### Batch endpoint
|
||||
|
||||
`POST /batch` sends multiple commands in a single HTTP request. This eliminates per-command round-trip latency — critical for remote agents where each HTTP call costs 2-5s (e.g., Render → ngrok → laptop).
|
||||
|
||||
```json
|
||||
POST /batch
|
||||
Authorization: Bearer <token>
|
||||
|
||||
{
|
||||
"commands": [
|
||||
{"command": "text", "tabId": 1},
|
||||
{"command": "text", "tabId": 2},
|
||||
{"command": "snapshot", "args": ["-i"], "tabId": 3},
|
||||
{"command": "click", "args": ["@e5"], "tabId": 4}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{"index": 0, "status": 200, "result": "...page text...", "command": "text", "tabId": 1},
|
||||
{"index": 1, "status": 200, "result": "...page text...", "command": "text", "tabId": 2},
|
||||
{"index": 2, "status": 200, "result": "...snapshot...", "command": "snapshot", "tabId": 3},
|
||||
{"index": 3, "status": 403, "result": "{\"error\":\"Element not found\"}", "command": "click", "tabId": 4}
|
||||
],
|
||||
"duration": 2340,
|
||||
"total": 4,
|
||||
"succeeded": 3,
|
||||
"failed": 1
|
||||
}
|
||||
```
|
||||
|
||||
**Design decisions:**
|
||||
- Each command routes through `handleCommandInternal` — full security pipeline (scope checks, domain validation, tab ownership, content wrapping) enforced per command
|
||||
- Per-command error isolation: one failure doesn't abort the batch
|
||||
- Max 50 commands per batch
|
||||
- Nested batches rejected
|
||||
- Rate limiting: 1 batch = 1 request against the per-agent limit (individual commands skip rate check)
|
||||
- Ref scoping is already per-tab — no changes needed
|
||||
|
||||
**Usage pattern** (agent crawling 20 pages):
|
||||
```
|
||||
# Step 1: Open 20 tabs (via individual newtab commands or batch)
|
||||
# Step 2: Read all 20 pages at once
|
||||
POST /batch → [{"command": "text", "tabId": 5}, {"command": "text", "tabId": 6}, ...]
|
||||
# → 20 page contents in ~2-3 seconds total vs ~40-100 seconds serial
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Each server session generates a random UUID as a bearer token. The token is written to the state file (`.gstack/browse.json`) with chmod 600. Every HTTP request must include `Authorization: Bearer <token>`. This prevents other processes on the machine from controlling the browser.
|
||||
|
||||
Reference in New Issue
Block a user