-
Notifications
You must be signed in to change notification settings - Fork 1
Server Boot-ID and SSE Connection Resilience #414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
cade3a4
8001014
b807636
5046d3d
95621ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,10 @@ | |
| "vscode": { | ||
| "extensions": [ | ||
| "harmoniqs.amicode" | ||
| ] | ||
| ], | ||
| "settings": { | ||
| "amicode.opencodePort": 43117 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # ADR 0008: Server URL Push on Restart | ||
|
|
||
| ## Status | ||
|
|
||
| Accepted | ||
|
|
||
| ## Context | ||
|
|
||
| The Amicode extension embeds the opencode web app in a WebviewPanel iframe. The | ||
| iframe's `location.origin` IS the server URL (e.g., `http://127.0.0.1:43117`). | ||
| When the server restarts, two scenarios exist: | ||
|
|
||
| 1. **Same port** (the default, `amicode.opencodePort = 43117`): the iframe's origin | ||
| is still valid. The SSE reconnect loop retries every 250 ms and reconnects once | ||
| the server is back. The server's `server.connected` event carries a `bootId` that | ||
| the web app uses to detect the restart and trigger a full state refresh. | ||
|
|
||
| 2. **Different port** (ephemeral mode, `amicode.opencodePort = 0`): the iframe's | ||
| origin points to a dead port. The SSE loop fails indefinitely. The webview's | ||
| localStorage may hold a stale URL from the previous port, compounding the issue. | ||
|
|
||
| Previously, there was no mechanism for the extension host to notify the webview of | ||
| a server restart or URL change. The webview relied entirely on the SSE reconnect | ||
| loop and localStorage, both of which fail when the port changes. | ||
|
|
||
| ## Decision | ||
|
|
||
| ### Extension host (this repo) | ||
|
|
||
| 1. **`ChatPanel.notifyServerUrlChanged(url)`**: a static method that checks whether | ||
| the new URL's origin differs from the panel's recorded origin. | ||
| - If same origin: posts a `server-url-changed` message (Lane 2) to all live | ||
| panels as a "restart happened" signal. | ||
| - If different origin: returns `true`, signaling the caller to dispose and | ||
| recreate the panel. | ||
|
|
||
| 2. **`ChatPanel.disposeCurrent()`**: disposes the underlying `vscode.WebviewPanel`, | ||
| which triggers cleanup and allows `openOrReveal` to create a fresh panel with the | ||
| new iframe `src`. | ||
|
|
||
| 3. **`serverManager.onReady` hook**: after every successful server start (including | ||
| restarts), calls `notifyServerUrlChanged`. If recreation is needed, disposes the | ||
| panel before `openOrReveal` creates a fresh one. | ||
|
|
||
| 4. **Lane 2 allowlist**: `"server-url-changed"` added to the relay script's Lane 2 | ||
| filter in both `chat_panel.ts` and `deck/shell.ts`. | ||
|
|
||
| ### Web app (opencode repo) | ||
|
|
||
| 5. **`AmicodeServerBridge` component**: listens for `server-url-changed` messages. | ||
| If the URL in the message differs from `location.origin` (unexpected — the panel | ||
| should have been recreated), redirects to the new URL as a safety net. If same | ||
| origin, does nothing (the SSE loop handles it). | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Same-port restarts (the common case) are seamless: the SSE loop reconnects and | ||
| the boot-ID mismatch triggers a full refresh — no panel recreation needed. | ||
| - Port-change restarts (ephemeral mode) cause a brief panel flicker as the old | ||
| panel is disposed and a new one is created with the correct origin. | ||
| - The `onReady` approach covers ALL server-ready events, not just explicit restarts | ||
| — including the initial boot, solver-mode switches, and vault respawns. | ||
| - Future improvement: self-healing via `postMessage` (documented in | ||
| `plans/followup-self-healing-reconnect.md` in the workspace) to avoid panel | ||
| recreation entirely. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| # Devcontainer Configuration | ||
|
|
||
| This document covers how the Amicode extension's runtime invariants (server port, | ||
| storage paths, etc.) are configured in devcontainer environments, including the | ||
| Dockerfile-based build case. | ||
|
|
||
| --- | ||
|
|
||
| ## Why port stability matters | ||
|
|
||
| The Amicode webview panel embeds the opencode web app in an iframe served by a | ||
| local HTTP server. The webview's localStorage (which persists session tabs, project | ||
| paths, and connection state) lives on the **host machine** — it survives container | ||
| rebuilds. The server port, however, is ephemeral unless explicitly pinned. | ||
|
|
||
| If the server starts on a different port after a container rebuild, the persisted | ||
| connection state in localStorage is stale. The webview's SSE event stream connects | ||
| to the dead port, and the user sees no responses until localStorage is cleared. | ||
|
|
||
| **The fix:** pin the server port to a stable value (default: `43117`) so that the | ||
| persisted URL remains valid across container rebuilds. | ||
|
|
||
| --- | ||
|
|
||
| ## Use cases | ||
|
|
||
| ### A. Pre-built extension (marketplace install) | ||
|
|
||
| The devcontainer installs Amicode from the VS Code Marketplace. No build-time | ||
| dependencies are needed. | ||
|
|
||
| ```jsonc | ||
| // .devcontainer/devcontainer.json | ||
| { | ||
| "name": "Amicode", | ||
| "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04", | ||
| "customizations": { | ||
| "vscode": { | ||
| "extensions": ["harmoniqs.amicode"], | ||
| "settings": { | ||
| "amicode.opencodePort": 43117 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The `amicode.opencodePort` setting is read by the extension at startup. It passes | ||
| `--port 43117` to the spawned `opencode serve` process. This is the simplest | ||
| configuration and covers most users. | ||
|
|
||
| ### B. Dockerfile-based build (extension under development) | ||
|
|
||
| The devcontainer uses a Dockerfile that installs build-time dependencies (Node, | ||
| pnpm, Bun, etc.). The extension is NOT installed from the marketplace — it runs | ||
| via F5 ("Run Extension") or is manually installed from a locally-built `.vsix`. | ||
|
|
||
| ```jsonc | ||
| // .devcontainer/devcontainer.json | ||
| { | ||
| "name": "Amicode Dev", | ||
| "build": { | ||
| "dockerfile": "Dockerfile" | ||
| }, | ||
| "customizations": { | ||
| "vscode": { | ||
| "settings": { | ||
| "amicode.opencodePort": 43117 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| **Key point:** `customizations.vscode.settings` is applied to the VS Code instance | ||
| regardless of how extensions are installed. Even when the extension is launched via | ||
| F5 (Extension Development Host), VS Code resolves `amicode.opencodePort` from the | ||
| workspace/container settings. You do NOT need the extension to be listed in | ||
| `"extensions"` for the setting to be available. | ||
|
|
||
| If the Dockerfile needs to set a default port for cases where VS Code is not | ||
| involved (e.g., running `opencode serve` directly in a terminal during | ||
| development): | ||
|
|
||
| ```dockerfile | ||
| # Dockerfile | ||
| FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 | ||
|
|
||
| # ... build-time dependencies ... | ||
|
|
||
| # Default server port for opencode (read via OPENCODE_CONFIG_CONTENT) | ||
| ENV OPENCODE_CONFIG_CONTENT='{"server":{"port":43117}}' | ||
| ``` | ||
|
|
||
| This env var is read by the opencode binary directly, bypassing VS Code settings. | ||
| It works in all contexts — terminal, scripts, CI — but note that when the Amicode | ||
| extension IS running, it builds its own `OPENCODE_CONFIG_CONTENT` (merging | ||
| instructions, permissions, telemetry, etc.) and passes it to the server process. | ||
| The Dockerfile's `ENV` value is therefore only effective when running the binary | ||
| manually outside the extension. | ||
|
|
||
| ### C. CI / headless (no VS Code) | ||
|
|
||
| For test harnesses, build pipelines, or headless environments where VS Code is not | ||
| present: | ||
|
|
||
| **Option 1 — `opencode.json` in the project root:** | ||
| ```json | ||
| { | ||
| "server": { | ||
| "port": 43117 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| This is the most portable option. The opencode binary reads it from the working | ||
| directory (or any ancestor). It works in all contexts and requires no environment | ||
| variable management. | ||
|
|
||
| **Option 2 — `OPENCODE_CONFIG_CONTENT` env var:** | ||
| ```bash | ||
| export OPENCODE_CONFIG_CONTENT='{"server":{"port":43117}}' | ||
| opencode serve | ||
| ``` | ||
|
|
||
| Or in `docker-compose.yml`: | ||
| ```yaml | ||
| services: | ||
| opencode: | ||
| environment: | ||
| OPENCODE_CONFIG_CONTENT: '{"server":{"port":43117}}' | ||
| ``` | ||
|
|
||
| **Option 3 — CLI flag:** | ||
| ```bash | ||
| opencode serve --port 43117 | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Port resolution priority (highest wins) | ||
|
|
||
| | Priority | Mechanism | Who sets it | | ||
| |----------|-----------|-------------| | ||
| | 1 | `--port` CLI flag | The extension (internally) or manual invocation | | ||
| | 2 | `OPENCODE_CONFIG_CONTENT` env var | The extension (builds merged config) or Dockerfile `ENV` | | ||
| | 3 | `opencode.json` `server.port` field | Developer, committed to repo | | ||
| | 4 | Default: `0` → try 4096, then OS-assigned | Built-in fallback | | ||
|
|
||
| When the Amicode extension is running: | ||
| - It reads `amicode.opencodePort` from VS Code settings (default: `43117`) | ||
| - It passes this as `--port` to the spawned server (priority 1) | ||
| - All other mechanisms are fallbacks for when the extension is not present | ||
|
|
||
| --- | ||
|
|
||
| ## Other configurable paths | ||
|
|
||
| The extension also supports overriding storage locations via VS Code settings | ||
| (added in #378): | ||
|
|
||
| | Setting | Env var injected | Default (XDG) | | ||
| |---------|-----------------|---------------| | ||
| | `amicode.sessionDatabase` | `OPENCODE_DB` | `~/.local/share/opencode/opencode.db` | | ||
| | `amicode.configDir` | `OPENCODE_CONFIG_DIR` | `~/.config/opencode` | | ||
|
|
||
| These can also be set in `customizations.vscode.settings` in `devcontainer.json` | ||
| for container-specific overrides (e.g., placing the database on a mounted volume). | ||
|
|
||
| --- | ||
|
|
||
| ## Caveats for Dockerfile-based builds | ||
|
|
||
| 1. **The extension is not installed at image build time.** `customizations.vscode` | ||
| is processed by VS Code/Codespaces at container start, not during `docker build`. | ||
| Do not rely on extension presence in Dockerfile `RUN` steps. | ||
|
|
||
| 2. **`OPENCODE_CONFIG_CONTENT` conflicts.** If both the Dockerfile sets this env | ||
| var AND the extension is running, the extension's value wins (it spawns the | ||
| server with its own merged config in the process env, overriding the container | ||
| env). The Dockerfile value is only effective for manual `opencode serve` calls. | ||
|
|
||
| 3. **Port forwarding.** If VS Code auto-forwards port 43117 (which it does by | ||
| default for detected listening ports), the server is accessible from the host at | ||
| `localhost:43117`. This is expected behavior and does not interfere with the | ||
| webview (which connects to the container-internal `127.0.0.1:43117`). | ||
|
|
||
| 4. **Multiple containers on the same host.** If two devcontainers both use port | ||
| 43117, VS Code handles port forwarding conflicts (it maps to different host | ||
| ports). The webview inside each container connects to its own `127.0.0.1:43117` | ||
| without conflict. The localStorage isolation concern (multiple webviews sharing | ||
| one localStorage scope) is separate and addressed by the boot-ID mechanism | ||
| (opencode ADR 0005). | ||
|
Comment on lines
+183
to
+193
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 6 \
'asExternalUri|location\.origin|127\.0\.0\.1|opencodePort|webview' \
packages/extensionRepository: harmoniqs/amicode Length of output: 50376 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- ChatPanel and URI-related symbols ---'
rg -n -C 10 \
'class ChatPanel|ChatPanel|asExternalUri|opencodeReadyUrl|openOrReveal|renderHtml|frame-src|location\.origin|origin:' \
packages/extension/src packages/extension/package.json docs/devcontainers.md \
-g '*.ts' -g '*.md' -g '*.json' | head -n 1200
printf '%s\n' '--- candidate file list ---'
git ls-files packages/extension/src | rg 'chat|panel|shell|extension|server'Repository: harmoniqs/amicode Length of output: 50373 🏁 Script executed: #!/bin/bash
set -euo pipefail
files=$(git ls-files packages/extension/src | rg 'chat|panel|shell|extension|server')
for f in $files; do
if rg -q 'ChatPanel|asExternalUri|location\.origin|opencodeReadyUrl|openOrReveal|renderHtml' "$f"; then
echo "--- $f ($(wc -l < "$f") lines) ---"
rg -n -C 14 'ChatPanel|asExternalUri|location\.origin|opencodeReadyUrl|openOrReveal|renderHtml|frame-src|origin:' "$f" | head -n 500
fi
doneRepository: harmoniqs/amicode Length of output: 49717 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- ChatPanel relay and iframe construction ---'
sed -n '228,350p' packages/extension/src/chat_panel.ts
printf '%s\n' '--- devcontainer documentation and configuration ---'
sed -n '1,230p' docs/devcontainers.md
printf '%s\n' '--- repository references to Remote-SSH, Remote-Containers, forwarding, and 43117 ---'
rg -n -C 5 'Remote-SSH|Remote-Containers|devcontainer|forward|43117|asExternalUri|opencodePort' . \
-g '!packages/extension/dist/**' -g '!node_modules/**' | head -n 1200Repository: harmoniqs/amicode Length of output: 50376 🌐 Web query:
💡 Result: When developing VS Code extensions for remote environments (such as SSH, Containers, or GitHub Codespaces), accessing a localhost web server inside a WebviewPanel requires special handling because webviews run in an isolated context on the user's local machine, while the extension host runs on the remote server [1][2]. Simply referencing localhost inside the webview will resolve to the user's local machine, not the remote host [1][2]. To bridge this gap, you should use one of the following approaches based on your requirements: 1. vscode.env.asExternalUri (Recommended for iframes) This is the most reliable method for loading content into an iframe within a webview [3]. It programmatically converts a local URI into an external, accessible URI that VS Code routes through its remote tunneling infrastructure [1][4]. Example: const serverUri = await vscode.env.asExternalUri(vscode.Uri.parse('http://localhost:3000')); panel.webview.html = Citations:
Use a host-reachable URI for remote webviews. 🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe this one too, but this might not need to be supported. your call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
ast-grep outline packages/extension/src/chat_panel.ts --items all --view expanded
printf '%s\n' '--- URL construction and remote URI handling ---'
rg -n -C 8 \
'asExternalUri\s*\(|127\.0\.0\.1|localhost|location\.origin|iframe|serverUrl|opencodeReadyUrl|new URL\s*\(' \
packages/extension/src/chat_panel.ts \
packages/extension/src/extension.ts \
packages/extension/src/deck/shell.ts
printf '%s\n' '--- documentation lines under review ---'
sed -n '175,195p' docs/devcontainers.mdLength of output: 77243 🤖 web_search executed:
Length of output: 16353
The current code puts the extension host URL, The new documentation explicitly supports devcontainers. Therefore, the statements in The appropriate resolution is to translate the iframe origin with You are interacting with an AI system. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,8 @@ export class ChatPanel { | |
| * only when the report-a-bug skill is there to answer it. */ | ||
| private static bugReportAvailable = false; | ||
| private readonly disposables: vscode.Disposable[] = []; | ||
| /** The origin the iframe was built with — used to detect port changes on restart. */ | ||
| readonly origin: string; | ||
|
|
||
| private constructor( | ||
| private readonly panel: vscode.WebviewPanel, | ||
|
|
@@ -55,6 +57,7 @@ export class ChatPanel { | |
| authToken?: string, | ||
| hideProjectDir?: string, | ||
| ) { | ||
| this.origin = opencodeUrl.origin; | ||
| this.panel.webview.html = this.renderHtml(opencodeUrl, authToken, hideProjectDir); | ||
| ChatPanel.live.add(this); | ||
| this.panel.onDidDispose(() => this.dispose(), null, this.disposables); | ||
|
|
@@ -108,6 +111,30 @@ export class ChatPanel { | |
| setTimeout(() => void this.panel.webview.postMessage(envelope), 1500); | ||
| } | ||
|
|
||
| /** Notify all live panels that the server URL changed (or that the server | ||
| * restarted on the same port). If the port changed, the panel's iframe is | ||
| * stale and must be recreated — returns true if recreation is needed. */ | ||
| static notifyServerUrlChanged(url: URL): boolean { | ||
| const current = ChatPanel.current; | ||
| if (!current) return false; | ||
| if (current.origin !== url.origin) return true | ||
| // Same origin — just inform the webview the server restarted. | ||
| for (const panel of ChatPanel.live) { | ||
| void panel.panel.webview.postMessage({ | ||
| source: "amicode", | ||
| kind: "server-url-changed", | ||
| url: url.href, | ||
| }); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| /** Dispose the current primary panel (closes the VS Code tab). Used when the | ||
| * server port changed and the iframe needs to be rebuilt with a new origin. */ | ||
| static disposeCurrent(): void { | ||
| ChatPanel.current?.panel.dispose(); | ||
| } | ||
|
Comment on lines
+132
to
+136
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift Dispose every panel that uses the old origin.
🤖 Prompt for AI Agents |
||
|
|
||
| /** AC5's gate setter — called after each session prep with | ||
| * bugReportSkillStaged(project.skillPaths). */ | ||
| static setBugReportAvailable(available: boolean): void { | ||
|
|
@@ -273,7 +300,7 @@ export class ChatPanel { | |
| // (webview-internal origin, never the opencode origin). Forward only | ||
| // our own envelopes, pinned to the opencode origin. #351 adds | ||
| // run:*/device:* envelopes for the Work Column inspector tabs. | ||
| if (d && d.source === "amicode" && (d.kind === "theme" || d.kind === "clipboard" || d.kind === "open-compute-connect" || d.kind === "open-bug-report" || d.kind === "close-bug-report" || d.kind === "dev-tools-status" || d.kind === "dev-tools-rebuild-status" || d.kind === "data-storage-defaults" || d.kind === "data-storage-status" || (typeof d.kind === "string" && (d.kind.indexOf("run:") === 0 || d.kind.indexOf("device:") === 0)) || d.kind === "clipboard-image")) { | ||
| if (d && d.source === "amicode" && (d.kind === "theme" || d.kind === "clipboard" || d.kind === "open-compute-connect" || d.kind === "open-bug-report" || d.kind === "close-bug-report" || d.kind === "dev-tools-status" || d.kind === "dev-tools-rebuild-status" || d.kind === "data-storage-defaults" || d.kind === "data-storage-status" || d.kind === "server-url-changed" || (typeof d.kind === "string" && (d.kind.indexOf("run:") === 0 || d.kind.indexOf("device:") === 0)) || d.kind === "clipboard-image")) { | ||
| var f = document.querySelector("iframe"); | ||
| if (f && f.contentWindow) f.contentWindow.postMessage(d, ${origin}); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -407,6 +407,10 @@ window.addEventListener("message", (e) => { | |
| if ((d.kind === "dev-tools-status" || d.kind === "dev-tools-rebuild-status" || d.kind === "data-storage-defaults" || d.kind === "data-storage-status") && typeof d.tab === "string") { | ||
| frameByTab.get(d.tab)?.contentWindow?.postMessage(d, boot.origin); | ||
| } | ||
| // Server URL push: broadcast to all panes so the SSE loop can reconnect. | ||
| if (d.kind === "server-url-changed") { | ||
| for (const f of frameByTab.values()) f.contentWindow?.postMessage(d, boot.origin); | ||
| } | ||
|
Comment on lines
+410
to
+413
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- shell.ts outline ---'
ast-grep outline packages/extension/src/deck/shell.ts
printf '%s\n' '--- shell.ts relevant sections ---'
sed -n '1,90p;360,435p' packages/extension/src/deck/shell.ts
printf '%s\n' '--- server-url-changed references ---'
rg -n -C 4 'server-url-changed|serverUrl|server-url|boot\.origin|frameSrc' packages/extension/srcRepository: harmoniqs/amicode Length of output: 19893 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- DeckPanel and deck boot construction ---'
rg -n -C 6 'DeckPanel|__AMICODE_DECK__|DeckBoot|origin:|server-url-changed|opencodeReadyUrl|webview.postMessage' packages/extension/src
printf '%s\n' '--- extension entry points and panel lifecycle ---'
rg -n -C 5 'create.*Deck|deck|ChatPanel|WebviewPanel|resolveWebviewView|onDidReceiveMessage' packages/extension/src/extension.ts packages/extension/src
printf '%s\n' '--- shell remainder after message bridge ---'
sed -n '420,530p' packages/extension/src/deck/shell.tsRepository: harmoniqs/amicode Length of output: 50374 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- server onReady handling ---'
sed -n '810,855p' packages/extension/src/extension.ts
printf '%s\n' '--- ChatPanel restart handling ---'
sed -n '108,135p' packages/extension/src/chat_panel.ts
printf '%s\n' '--- DeckPanel full lifecycle and API ---'
sed -n '18,135p' packages/extension/src/deck_panel.ts
printf '%s\n' '--- all DeckPanel references ---'
rg -n -C 3 'DeckPanel\.(openOrReveal|current|notify)|notifyServerUrlChanged' packages/extension/srcRepository: harmoniqs/amicode Length of output: 11675 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
shell = Path("packages/extension/src/deck/shell.ts").read_text()
deck = Path("packages/extension/src/deck_panel.ts").read_text()
ext = Path("packages/extension/src/extension.ts").read_text()
chat = Path("packages/extension/src/chat_panel.ts").read_text()
checks = {
"shell uses boot.origin for frame resolution": "new URL(tab.url, boot.origin)" in shell,
"shell uses boot.origin for message targets": "postMessage(d, boot.origin)" in shell,
"shell never refreshes boot.origin": not re.search(r"\bboot\.origin\s*=", shell),
"deck CSP is fixed from initial URL": '`frame-src ${opencodeUrl.origin}`' in deck,
"deck openOrReveal reuses existing panel": "if (DeckPanel.current)" in deck and "DeckPanel.current.panel.reveal()" in deck,
"extension handles URL changes only through ChatPanel": "ChatPanel.notifyServerUrlChanged(url)" in ext and "DeckPanel" not in ext[ext.find("serverManager.onReady((url) =>"):ext.find("serverManager.onReady((url) =>") + 2500],
"different-origin ChatPanel changes are not posted": "if (current.origin !== url.origin) return true" in chat,
}
for name, result in checks.items():
print(f"{'PASS' if result else 'FAIL'}: {name}")
assert all(checks.values())
print("Conclusion: a different-origin restart leaves an existing DeckPanel bound to its original origin and CSP.")
PYRepository: harmoniqs/amicode Length of output: 602 Recreate the deck when the server origin changes. When 🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this really a big lift? I presume its a polling checker and maybe a little error handling but nothing too bad There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This does not need polling. The focused fix is:
This is likely a small, contained lifecycle change across 🐇 You are interacting with an AI system. |
||
| // #351: inspector fan-out — broadcast to every live pane (no tab routing; | ||
| // the app's Work Column tabs buffer per-run/per-device themselves). | ||
| if (typeof d.kind === "string" && (d.kind.indexOf("run:") === 0 || d.kind.indexOf("device:") === 0)) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.