- 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]
56 lines
2.5 KiB
Markdown
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
|