diff options
| author | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-16 13:03:51 -0400 |
|---|---|---|
| committer | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-16 13:03:51 -0400 |
| commit | ea567819d835d4704d9fb38cd114e0e5b231759e (patch) | |
| tree | b0169202354482907dd95e51fcc94018d9e4e491 | |
| parent | fcae5b188baa1bdfed48208d7cfc2b711af4aae3 (diff) | |
0.17.3: daemon self-heal on missing-daemon fetch failure
In 0.17.x the daemon is a separate process that can idle-shut-down
(10 min idle) or be killed externally while Pi sessions are running.
Without self-heal, the next /chrome onboard or chrome_* call surfaces
'fetch failed' instead of recovering.
bridge.admin() now catches the failed fetch, re-runs ensureDaemonRunning
(probe -> spawn if missing), and retries once.
bridge.sendViaOwner() pre-probes /status and spawns the daemon if missing
before issuing the signed /command.
Net effect: a missing daemon transparently respawns instead of bubbling
fetch failed up to the user.
| -rw-r--r-- | CHANGELOG.md | 11 | ||||
| -rw-r--r-- | extensions/chrome-profile-bridge/browser-extension/manifest.json | 2 | ||||
| -rw-r--r-- | extensions/chrome-profile-bridge/index.ts | 19 | ||||
| -rw-r--r-- | package.json | 2 |
4 files changed, 31 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 978bd75..2a9de5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable user-facing changes to `pi-chrome`. +## 0.17.3 — 2026-05-16 + +### Daemon self-heal + +0.17.x makes the Pi process a client of a separately-spawned daemon. If the daemon idle-shuts-down (after 10 min of inactivity) or is killed externally while a Pi session is running, the next chrome_* tool call or `/chrome onboard` would surface `fetch failed` instead of recovering. + +- **`bridge.admin()`** catches the failed fetch, re-runs `ensureDaemonRunning()` (which probes, kills incompatible, or spawns fresh), then retries the request once. +- **`bridge.sendViaOwner()`** pre-probes `/status`; if no daemon is reachable, it spawns one before issuing the signed `/command`. + +Net effect: a missing daemon transparently respawns on the next Pi-side request instead of bubbling `fetch failed` up to the user. + ## 0.17.2 — 2026-05-16 ### Fixes for the daemon model surfaced during 0.17.0/0.17.1 testing diff --git a/extensions/chrome-profile-bridge/browser-extension/manifest.json b/extensions/chrome-profile-bridge/browser-extension/manifest.json index d3db49e..b3fcd7a 100644 --- a/extensions/chrome-profile-bridge/browser-extension/manifest.json +++ b/extensions/chrome-profile-bridge/browser-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Pi Chrome Connector", - "version": "0.17.2", + "version": "0.17.3", "description": "Lets Pi control tabs in Chrome via a local connector at 127.0.0.1.", "permissions": [ "tabs", diff --git a/extensions/chrome-profile-bridge/index.ts b/extensions/chrome-profile-bridge/index.ts index 820c7d7..718012a 100644 --- a/extensions/chrome-profile-bridge/index.ts +++ b/extensions/chrome-profile-bridge/index.ts @@ -923,7 +923,17 @@ class ChromeProfileBridge { const sig = this.auth.signPeerRequest("POST", "/admin", payload); if (sig) headers[AUTH_HEADER] = sig; } - const response = await fetch(`${this.url}/admin`, { method: "POST", headers, body: payload }); + const doFetch = () => fetch(`${this.url}/admin`, { method: "POST", headers, body: payload }); + let response: Response; + try { response = await doFetch(); } + catch (error) { + // Daemon isn't reachable. Self-heal by re-running the spawn path, then retry once. + // Covers the common case where the daemon idle-shut-down since the Pi session started + // (or was killed manually) but the Pi-side bridge still thinks mode === "client". + console.warn(`pi-chrome: /admin fetch failed (${(error as Error).message}); re-spawning daemon and retrying`); + await this.ensureDaemonRunning(); + response = await doFetch(); + } const respText = await response.text(); // Verify owner sig when present (paired state). For pre-pair calls, no sig to verify. if (this.auth.paired) { @@ -1011,6 +1021,13 @@ class ChromeProfileBridge { } private async sendViaOwner(action: string, params: Record<string, unknown>, timeoutMs: number, signal?: AbortSignal): Promise<unknown> { + // Self-heal: re-spawn the daemon if it's gone. Cheap when daemon is up (a no-op since + // the version probe inside ensureDaemonRunning returns compatible immediately). + try { + const probed = probeBridgeStatus(this.host, this.port); + if (!probed) await this.ensureDaemonRunning(); + } catch { /* fall through to the fetch — will surface a clearer error if anything is + actually broken. */ } if (!this.auth.paired) { throw new Error("Chrome bridge not paired with this Pi config. Run /chrome pair in the Pi session that owns the bridge first."); } diff --git a/package.json b/package.json index 1765ab5..5f2ebaa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pi-chrome", - "version": "0.17.2", + "version": "0.17.3", "scripts": { "version": "node scripts/sync-manifest-version.js", "prepublishOnly": "node scripts/sync-manifest-version.js" |
