Repository navigation
fix(desktop): handle opencode:// links reliably - #59058
kitlangton wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
The change looks sound. I couldn't run it here: it needs the Electron app and OS-level opencode:// handling. In packages/desktop, the lifecycle tests (13) and all src/main tests (118) pass, and tsc -b is clean (the package's typecheck script calls bun check, which this Bun version doesn't have). I also ran sample links through parseDeepLink: uppercase schemes, encoded and plain http(s) origins and sidecar are accepted, while javascript: server values and trailing slashes are rejected. The main process reads the same window storage file and tabs fields the app writes, so tab matching should work.
The public API is unchanged: the only signature change is the internal consumeInitialDeepLinks.
One small regression, explained inline: an opencode:// link that is rejected no longer brings any window forward. Before, the app was focused.
| return win | ||
| } | ||
|
|
||
| const openLinks = (urls: string[]) => focusWindow(urls.map(openLink).findLast((win) => !!win) ?? null) |
There was a problem hiding this comment.
When every link in a batch is rejected (for example an old v1 opencode://new-session link, see #55106, or anything malformed), openLinks calls focusWindow(null) and nothing comes forward. On Windows and Linux, secondInstance also skips its own focus because linkArguments(argv) isn't empty. On macOS, open-url used to focus the last-focused window too. So clicking a link the app doesn't recognise now does nothing visible, where before it at least brought OpenCode forward. Falling back to getLastFocusedWindow() when no link was accepted would keep the old behaviour.
There was a problem hiding this comment.
Fixed in f35e44b: when no link in a batch is accepted, the app now brings forward the last-focused window (restoring closed windows, or creating one if there is nothing to restore), covered by a new linkBatchWindow test.
f3accf6 to
7ba83f8
Compare
There was a problem hiding this comment.
The new commit looks sound. When a session link arrives on macOS with every window closed, the windows from last time are restored first, so the link opens in the restored window that holds the session rather than a new blank one. windows.restore always returns at least one window, and because restore runs before activate, the windows aren't restored a second time. I couldn't run the desktop app here. In packages/desktop, the lifecycle tests (15) and all src/main tests (120) pass, and tsc -b is clean.
The public API is unchanged.
My earlier comment isn't addressed yet: when every link in a batch is rejected, no window is focused.
7ba83f8 to
f35e44b
Compare
There was a problem hiding this comment.
My earlier point is addressed: when every link in a batch is rejected, the app comes forward again. It focuses the last-focused or any open window, and on macOS with every window closed it restores the windows from last time. I couldn't run the desktop app here. In packages/desktop, the lifecycle tests (17) and all src/main tests (122) pass, and tsc -b is clean.
The public API is unchanged. Nothing new to raise.
f35e44b to
8eea83c
Compare
There was a problem hiding this comment.
This push only rebases the PR onto dev. The deep-link commits are unchanged apart from switching to dev's renamed mainWindows, which also removes the two lint suppressions the PR had added. I couldn't run the desktop app here. In packages/desktop, the lifecycle tests (17) and all src/main tests (163) pass, and tsc -b is clean.
The public API is unchanged. Nothing new to raise.
Desktop no longer drops or replays
opencode://links.A link that launches the app on macOS could arrive before anything was listening, Windows and Linux never read the link from the first launch, and one global list replayed old links into every new window or reload. Links are now checked in the main process before any window sees them.
❌ Before
✅ After
open-url, second instance, command line) and wait in a small capped buffer until windows are restored.opencode://session/<ses_…>(optionalserver) and the existing console sign-in link are accepted. Anything else, repeated or unknown parameters, control characters, and links over 2 KB are rejected, and the window gets a link rebuilt from the accepted fields only.📊 Proof: by hand in a packaged build on a fresh macOS VM (before the latest rebase), together with #59061.
17 lifecycle tests pass, covering valid, malicious and oversized links, window choice, and no replay.
👀 Read:
lifecycle/index.ts(which window gets the link, and restoring closed windows first) andlink-queue.ts. The app side that opens a session is #59061; until it lands, the visible change is only which window comes forward. Two lint findings in touched files are suppressed with a reason. The rest is routine.Found while wiring links for another app to open sessions · no user reports
Checks run
bun test src/main/lifecycle(packages/desktop, after the rebase): 17 passbun typecheck(packages/desktop, after the rebase): cleanbun run check(lint and typecheck): passAdded by the opencode bot (AI-generated).
Related: #44167, #53529