Files
claw/docs/superpowers/specs/2026-04-14-helper-page-lifecycle-fix-v2-design.md
木炎 c60cd308ca feat: service console auto-connect, settings panel, and batch of enhancements
- Auto-connect WebSocket on page load in service console
- Settings modal for editing sgclaw_config.json (API key, base URL, model, skills dir, etc.)
- UpdateConfig/ConfigUpdated protocol messages for remote config save
- save_to_path() for SgClawSettings serialization
- ConfigUpdated handler in sg_claw_client binary
- Protocol serialization tests for new message types
- HTML test assertions for auto-connect and settings UI
- Additional pending changes: deterministic submit, org units, lineloss xlsx export, browser script tool, and docs

🤖 Generated with [Qoder][https://qoder.com]
2026-04-14 14:32:46 +08:00

56 lines
2.5 KiB
Markdown

# Helper Page Lifecycle Fix v2 — Same-Connection Close + Open
**Date:** 2026-04-14
**Status:** Approved
## Problem
Two issues remain after v1:
1. **Process restart leaves orphaned helper pages**: When the sg_claw process restarts, the old helper page tab remains open in the browser. The new process opens another one.
2. **Helper page is visible**: Uses `sgBrowerserOpenPage` (visible tab API) instead of `sgHideBrowerserOpenPage` (hidden domain API).
## Root Cause of v1 Failure
The v1 `close_helper_page` function created a **second** WebSocket connection to the browser during `Drop`. This likely conflicted with the existing bootstrap connection, causing the browser's WebSocket state to become confused.
## Solution
Send the close command on the **same** WebSocket connection used for bootstrap, before sending the open command:
1. Connect to browser WS
2. Register as "web" role
3. **Blindly send** `sgHideBrowerserClosePage(helper_url)` — closes any orphaned page from a previous process run
4. Send `sgHideBrowerserOpenPage(helper_url)` — opens the new helper page
5. Poll `/sgclaw/callback/ready` for page readiness
Both `use_hidden_domain = true` and the close+open logic are combined into a single change.
## Why This Works
- **Same connection**: Only one WebSocket connection to the browser. No conflict with existing connections.
- **Best-effort close**: If no orphaned page exists (first run ever), the close command is silently ignored by the browser. This does not affect the subsequent open command.
- **Fire-and-forget**: Both close and open commands use the same fire-and-forget semantics as the existing bootstrap command.
## API Reference
| API | Wire format | Effect |
|-----|------------|--------|
| `sgHideBrowerserOpenPage` (API #6) | `[requesturl, "sgHideBrowerserOpenPage", url]` | Opens in hidden domain |
| `sgHideBrowerserClosePage` (API #68) | `[requesturl, "sgHideBrowerserClosePage", url]` | Closes hidden domain page |
## Affected Files
| File | Change |
|------|--------|
| `src/browser/callback_host.rs` | In `bootstrap_helper_page`: add close command before open command |
| `src/service/server.rs` | Change `use_hidden_domain` from `false` to `true` |
## What Does NOT Change
- `callback_backend.rs``SHOW_AREA`, `build_command` unchanged
- `sgBrowserExcuteJsCodeByDomain` area parameter — stays `"show"`
- Helper page HTML content — unchanged
- `Drop for LiveBrowserCallbackHost` — remains simple (shutdown only, no close attempt)
- `cached_host` in `mod.rs` — remains lifted to outer loop