Skip to content

Consolidate the three platform browser-launchers into one shared helper #347

Description

@initializ-mk

Why

PR #312 fixed a Windows cmd /c start &-truncation bug in the OAuth launcher — a bug that survived precisely because the launcher is copy-pasted three times and the copies had diverged: one was already fixed (mcp_browser.go used rundll32), while two others still used cmd /c start. The review's standing recommendation (finding 1, reiterated on the re-review inline at forge-ui/server.go) was to consolidate so this class of divergence cannot recur.

#312 closed the immediate bug and made the two copies that mattered identical + table-tested, but explicitly deferred the consolidation because it crosses module boundaries. This issue tracks that deferral.

The three copies today

All three now select rundll32 url.dll,FileProtocolHandler on Windows (post-#312), but each is a separately-maintained platform switch — nothing keeps them in sync:

Site Module Symbol Shape
forge-core/llm/oauth/flow.go forge-core browserCommand + openBrowser returns *exec.Cmd, errors on unsupported GOOS
forge-ui/server.go forge-ui browserCommand + openBrowser returns *exec.Cmd, no-op on unsupported GOOS
forge-cli/cmd/mcp_browser.go forge-cli openBrowserCLI builds cmd+args, .Start() inline, //nolint:gosec

The first two are already byte-identical (from #312) and table-tested the same way; the third has the same behavior but a different signature.

The constraint that makes this non-trivial

The helper spans three modules (forge-core / forge-cli / forge-ui), so the only shared home is a package in forge-core (both forge-cli and forge-ui already depend on it via replace).

BUT mcp_browser.go carries a deliberate constraint (spec §4.6 / review B4):

it lives in the CLI package (not forge-core/mcp) because the runtime image must NOT link os/exec for MCP code. The CLI forge mcp login command is laptop-only and never runs inside the agent runtime.

So the shared package must be a forge-core package that is NOT in any import path the MCP runtime links. forge-core/llm/oauth already links os/exec and is not on the MCP runtime path, but a neutral, purpose-named package is cleaner.

Proposed shape

  • New package e.g. forge-core/browser (or forge-core/internal/browserlauncher) exporting:
    • Command(goos, url string) *exec.Cmd — pure, testable selection (nil for unsupported GOOS)
    • Open(url string) errorCommand(runtime.GOOS, url).Start(), wrapping the unsupported-GOOS case
  • Repoint all three call sites at it; delete the local copies.
  • Keep the single table test (darwin/linux/windows/plan9, asserting the Windows argv is rundll32 url.dll,FileProtocolHandler <url> and the URL rides as a single un-split trailing argument) in the shared package; drop the two duplicated tests.
  • Verify the MCP-runtime-must-not-link-os/exec invariant still holds after the move — the whole reason mcp_browser.go was placed where it is. A build/link assertion or an explicit note in the new package doc should call out that it must never be imported from MCP runtime code.

Acceptance

  • One browser.Command/Open helper in forge-core; zero platform-switch duplication.
  • All three sites (flow.go, server.go, mcp_browser.go) call it.
  • Single table test covers the selection; the two per-site copies are removed.
  • Confirmed the MCP runtime image still does not link os/exec via this package (the §4.6 constraint).
  • gofmt + golangci-lint clean across forge-core / forge-cli / forge-ui; all three suites green.

Context

Follow-up to #312 (merged/ready). Non-urgent — the bug is fixed and the two copies that mattered are identical + tested; this is the durable fix that removes the divergence itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestforge-coreAffects the forge-core library (runtime, security, types, llm, mcp, auth)toolsAffects builtin or adapter tools (cli_execute, http_request, browser, etc.)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions