npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@relictombs/opencode-browser

v0.2.0

Published

Relictombs Browser: a shared browser workspace for the OpenCode v2 TUI

Readme

Relictombs Browser

@relictombs/opencode-browser is a shared browser workspace for the OpenCode V2 TUI. In the production remote layout, the server owns one hidden Electron runtime while the client renders the selected page in the terminal and forwards input. An explicit local fallback remains available when remoteHost is omitted.

There is no separate Electron window. The address bar, navigation controls, loading state, and tab strip are OpenTUI UI inside OpenCode. Rendering is provided by @relictombs/opentui-browser, whose terminal-browser direction is inspired by zenbu-labs/terminal-browser.

What it provides

  • Real interactive pages with keyboard, paste, mouse, wheel, hover, links, and forms.
  • Multiple browser tabs sharing one Electron runtime and profile.
  • Responsive viewports that follow OpenCode and terminal resizes.
  • Hidden background tabs that stay alive while only the selected page is presented.
  • Automatic OpenCode registration for browser lifecycle tools and, locally or through an explicit CDP forward, Chrome DevTools MCP.
  • Scoped Effect lifecycle, bounded recovery, and cleanup for Electron, pages, MCP servers, and the OpenTUI surface.

Requirements

  • OpenCode V2 0.0.0-next-16650 or a newer compatible @next build, launched as opencode2.
  • A local Kitty-capable terminal. Ghostty is the primary tested terminal.
  • Node.js 24 or newer for the controller and Chrome DevTools MCP subprocesses.
  • Bun 1.3 or newer only when developing this repository.

The terminal and OpenCode TUI run on the client. Electron may run on the server through the authenticated remote host. Plaintext WebSockets must be carried through SSH loopback tunnels; a trusted TLS edge may use wss:. Tmux and screen still need Kitty graphics passthrough and are not part of the supported baseline.

The OpenCode V2 plugin API is still beta. This release is tested against 0.0.0-next-16650; upgrade OpenCode and this plugin together if that API changes.

Install OpenCode V2

Install the current V2 prerelease and allow its package install script:

npm install -g @opencode-ai/cli@next --allow-scripts='@opencode-ai/cli'
opencode2 --version

If npm previously installed the CLI without its platform binary:

npm rebuild -g @opencode-ai/cli --allow-scripts='@opencode-ai/cli'

Set up Portal

Run the interactive setup wizard for a complete local or remote configuration:

npx --yes @relictombs/[email protected]

Choose browser or both surfaces, then choose where the runtime should run. For remote use, the wizard accepts a direct SSH destination or a Tailscale machine name or address, verifies FFmpeg and connectivity, starts the server, and owns the authenticated tunnel. Local and existing-host flows can print generated JSON instead of writing it; setup-owned SSH sessions apply the global or project configuration before optionally launching OpenCode.

Advanced manual configuration

OpenCode V2 reads ~/.config/opencode/cli.json, or $OPENCODE_CONFIG_DIR/cli.json when that environment variable is set. Add the published TUI plugin:

{
  "tabs": {
    "enabled": true,
    "scope": "global"
  },
  "mouse": true,
  "plugins": [
    {
      "package": "@relictombs/[email protected]",
      "options": {
        "url": "https://example.com",
        "profile": "project"
      }
    }
  ]
}

The exact version keeps the client aligned with the remote host protocol. If no options are needed, the short form also works:

{
  "plugins": ["@relictombs/[email protected]"]
}

OpenCode resolves the package's ./tui export. There is no separate plugin file, MCP configuration, Electron window, Chrome installation, or browser-server setup.

Portal's shared MCP and OpenCode registry packages are private bundled implementation details. Install only Portal; they do not need separate package entries or releases.

Electron is a package dependency. If its install script did not provision the platform binary, first activation runs Electron's installer asynchronously in a bounded worker and validates the result before starting the browser. That first activation can take longer and needs either a cached binary or network access.

Restart opencode2 after changing the package or its options.

The configuration above is the local setup: omitting remoteHost launches Electron on the same machine as OpenCode. It needs no Relictombs host, FFmpeg, or SSH tunnel. Prefer the wizard for end-to-end setup; edit this JSON directly only when integrating Relictombs into an independently supervised deployment.

Use

/browser reveals the currently selected browser tab when one exists; it does not create another tab. When the browser is empty, it creates the local New tab page on demand. /browser https://example.com reuses the selected tab and starts navigation only after its terminal surface has the final viewport. Use /browser-new or /browser-new https://example.com when you explicitly want an additional tab. Both commands are available from the slash-command picker and command palette.

| Action | Keyboard or UI | | --------------------------- | -------------------------------------------------------- | | Focus the address | Ctrl-L / Cmd-L | | New browser tab | /browser-new [url], Ctrl-T / Cmd-T, or + | | Close selected browser tab | Ctrl-W / Cmd-W, or the tab's × | | Next / previous browser tab | Ctrl-Tab / Ctrl-Shift-Tab | | Reload or stop loading | Ctrl-R / Cmd-R, or the toolbar button | | Back / forward | Cmd-[ / Cmd-], or Alt-Left / Alt-Right | | Reset an unresponsive page | Ctrl-Shift-R / Cmd-Shift-R | | Return to OpenCode | Ctrl-Shift-B, a session tab, or Return to OpenCode |

While the address field is focused, Escape restores the live page URL. Ctrl-W keeps its normal word-deletion behavior there; Cmd-W still closes the page.

Returning to OpenCode hides the browser without closing Electron or its pages, so reopening is fast. Remove browser tabs from strip also hides every browser page item while keeping the runtime ready; /browser restores them. Closing the final tracked page closes the browser workspace and leaves no hidden replacement target. The Electron runtime stays ready; the next explicit /browser, open, or new_tab operation creates the next page. A plain /browser creates the local welcome page on demand and presents it as New tab with an empty address instead of exposing its internal data URL.

Popup windows are deliberately denied. Use + or the controller's new_tab tool when a new tracked page is required.

Options

| Option | Purpose | | ------------------------ | ------------------------------------------------------------------------------- | | url | Initial HTTP(S) URL and default new-tab page. Defaults to a local welcome page. | | profile | Cookie/profile isolation policy. Defaults to "project"; see below. | | electronExecutablePath | Explicit Electron executable. Normally resolved from the package dependency. | | frameRate | Active Electron paint ceiling from 1 to 240 fps. Defaults to 30. | | maxViewportPixels | Optional raw viewport area cap; lowers terminal traffic while preserving input. | | nodeCommand | Node.js executable used for both local MCP subprocesses. | | remoteHost | Authenticated server control/media endpoints; selects the remote runtime path. |

remoteHost accepts endpoint, a 64-character lowercase hexadecimal token, optional requestTimeoutMs, optional mediaBaseEndpoint for rewriting server media URLs to a client tunnel, and optional devToolsWebSocketEndpoint for a separate CDP tunnel. Local Electron/profile/frame-rate options cannot be combined with remoteHost; those resources belong to the server.

Advanced manual remote configuration

The wizard is the supported start-to-finish remote path. The following shape is only for operators who supervise the server and tunnel themselves:

{
  "tabs": { "enabled": true, "scope": "global" },
  "mouse": true,
  "plugins": [
    {
      "package": "@relictombs/[email protected]",
      "options": {
        "url": "https://example.com",
        "remoteHost": {
          "endpoint": "ws://127.0.0.1:43100/control",
          "mediaBaseEndpoint": "ws://127.0.0.1:43101",
          "token": "<copy the 64-character token printed by the server>"
        }
      }
    }
  ]
}

Do not include profile, frameRate, or electronExecutablePath with remoteHost; the server owns those resources. The wizard verifies the client's H.264 decoder and the server's libx264 encoder, and provides installation guidance when either is missing.

The control/media tunnel transports the browser workspace. Chrome DevTools is deliberately separate, and the stock host CLI neither prints nor configures Electron's dynamic CDP endpoint. A custom host operator can expose a known server loopback endpoint, forward it separately, and set devToolsWebSocketEndpoint. Otherwise Portal registers the control MCP only and does not pretend DevTools is available. The stock remote browser profile is temporary and is deleted when the host stops; persistent remote-profile selection requires custom host composition.

Exact setup, plugin, and host versions remain a coordinated release set; current peers negotiate ALV2 while older peers retain the ALV1/NUT compatibility path. The npx setup and host commands are available from the public Relictombs packages on npm.

Host composition API

Custom server launchers can import @relictombs/opencode-browser/host-mcp. Use serveBrowserControllerHostMcp(controllerCall, httpOptions) to expose the same bounded browser controller tools on an authenticated loopback Streamable HTTP endpoint. Use browserDevToolsHostRegistration(...) to describe a server-local Chrome DevTools MCP process for a validated loopback browser CDP endpoint. Both functions return strict OpenCode registration descriptors; the HTTP server remains scoped to the caller's Effect scope.

Cookies, logins, and profiles

For the local fallback, the default "project" profile persists cookies, logins, local storage, history, and cache for one stable OpenCode project. It survives browser-tab closure, Electron recovery, plugin reloads, and TUI restarts. Project identifiers are hashed before being used in the profile path.

Choose the narrowest sharing policy that fits the work:

| profile value | Persistence and sharing | | ----------------------------------------- | ---------------------------------------------------------------------------- | | "project" | Persistent and shared by workspaces belonging to one OpenCode project. | | "workspace" | Persistent and isolated to one OpenCode project/workspace pair. | | "global" | Persistent and intentionally shared across every project using this profile. | | "temporary" | Private for this activation and deleted when its root scope closes. | | { "mode": "custom", "directory": "/…" } | Persistent in an explicit absolute directory dedicated to this plugin. |

Persistent profiles live below $XDG_STATE_HOME/opencode/relictombs/browser/profiles/v1, falling back to ~/.local/state/opencode/relictombs/browser/profiles/v1. A mode-0700 directory and exclusive owner lock prevent two Electron runtimes from opening the same profile concurrently. The UI displays only a safe mode label, never the path or project identity.

Agent access

The plugin always registers browser control. Local generations also register DevTools; remote generations do so only when remoteHost.devToolsWebSocketEndpoint names an explicit client loopback tunnel:

| MCP server | Responsibility | | ----------------------------- | ------------------------------------------------------------------------------------- | | relictombs-browser-control | Navigate, create, list, select, and close tracked browser tabs in the background. | | relictombs-browser-devtools | Inspect and operate the same live Electron pages through [email protected]. |

The control server exposes six tools: open, new_tab, list_tabs, select_tab, close_tab, and status. Agent operations never select the Browser workspace. They can add a browser item to the terminal tab strip and update its loading state, but OpenCode stays active until the user runs /browser or selects that browser tab.

For reliable agent automation:

  1. Use open for an ordinary single-page request; it reuses the selected tab in the background, including the initial welcome tab.
  2. Use new_tab only when the user requests an additional tab or the task must keep multiple pages open concurrently; the new page is also created and selected in the background.
  3. Read its stable logical tabId, current CDP targetId, URL, and title.
  4. Call Chrome DevTools MCP list_pages and match the live page by URL/title.
  5. Use Chrome DevTools MCP's numeric pageId for page-scoped DevTools operations.

tabId, targetId, and Chrome DevTools MCP's pageId are different identities:

  • tabId belongs to the plugin and survives replacement of an unresponsive page.
  • targetId belongs to the current Electron webContents and changes when that page is replaced.
  • pageId belongs to the current Chrome DevTools MCP process and must be obtained from list_pages.

Use the controller for new_tab and close_tab; creating page lifecycle through DevTools would bypass the Browser Session and leave the terminal tab strip stale. Give new_tab a caller-generated requestId and reuse it when retrying the same timed-out call.

The plugin launches the installed Chrome DevTools MCP entry with Node.js. It does not use npx, download an MCP package during activation, discover system Chrome, or create a second browser runtime.

DevTools screenshots default to WebP at quality 80 and are bounded to 640×640 during capture. The bound keeps even an explicit PNG capture below Chrome DevTools MCP's inline-image threshold, so screenshot results remain typed image attachments that OpenCode can render in the session feed instead of opaque temporary-file messages.

If the plugin becomes active during an existing agent turn, begin a new turn so OpenCode can refresh its MCP tool catalog.

How it works

One Browser Generation owns one hidden Electron Managed Browser Runtime. Every browser tab is a long-lived Shared Browser Page inside that runtime. Only the selected visible page owns an active Presentation:

OpenCode Host UI
  -> selected Shared Browser Page
    -> Electron offscreen paint
      -> owned in-memory RGBA
        -> local: Kitty direct-data image
        -> remote: negotiated ALV2/H.264 media socket (ALV1/NUT fallback) -> client decode -> Kitty direct-data image

Background pages remain available to the user and agent, idle their offscreen paint rate at 1 fps, and do not send frames to Kitty. Changing terminal geometry updates the selected page viewport and placement without stretching a stale frame. Explicit foreground URL commands wait for that viewport and Presentation to be ready before navigation; agent navigation remains immediate and offscreen.

OpenCode V2 currently exposes session tabs rather than arbitrary native plugin tabs. The plugin therefore renders a composite strip in the global app slot: OpenCode sessions and logical browser pages remain independently owned, while the current OpenCode route stays mounted underneath the browser workspace.

If a page Presentation fails, the plugin retries it and can replace only that Shared Browser Page. If Electron disconnects, it retires the Browser Generation, launches a clean replacement, and refreshes the DevTools endpoint without replacing the stable controller or MCP names.

Contributor-level lifecycle, identity, and Effect boundaries are documented in CONTEXT.md.

Resource use

Electron's fixed main, GPU, network-service, profile, and DevTools cost is paid once per Browser Generation. Each additional tab adds a page renderer; only the selected visible page pays active Presentation cost.

On Apple M5/macOS with a 960×600 animated page:

| State | Electron memory | Electron CPU | Bun + Electron memory | | -------------------------------------- | --------------: | --------------------: | --------------------: | | Hidden/no Presentation, 1 fps idle cap | 223–225 MiB | 0.25–0.6% of one core | 276–278 MiB | | Active Presentation, 30 fps | 227–232 MiB | 26–29% of one core | 281–283 MiB |

This short measurement predates the encoded remote path and covers a local in-memory RGBA path; it excludes Ghostty's image storage and a full OpenCode host baseline. Real websites, video, WebGL, extra tabs, viewport size, and terminal image storage can use more.

Uncompressed capture work scales with width × height × 4 × frameRate, but the server → client link carries encoded video rather than RGBA files. Keep the visible viewport bounded when reducing network or decode cost; the stock host CLI does not currently expose a remote frame-rate option.

Troubleshooting and limitations

  • No browser appears: run /browser, then check OpenCode's Plugins view for an activation error.
  • First activation is slow: Electron may be provisioning its platform binary after lifecycle scripts were skipped.
  • Terminal becomes unresponsive: set maxViewportPixels to 144000 to reduce raw Kitty traffic by about 75% for a 960×600 surface without enabling terminal-side compression.
  • Blank remote surface: verify both the control and media SSH forwards, and ensure rewritten media URLs resolve on the client. A successful control connection alone does not carry pixels.
  • DevTools missing remotely: the stock host CLI neither prints nor configures Electron's dynamic CDP endpoint. A custom host operator must first expose a known server loopback endpoint, then forward it separately and set devToolsWebSocketEndpoint.
  • Input goes to OpenCode: click the page or use /browser to focus the Browser Surface; Ctrl-L intentionally focuses the address field.
  • MCP registration fails: remove or rename pre-existing servers called relictombs-browser-control or relictombs-browser-devtools. The plugin will not overwrite them.
  • Duplicate plugin behavior: configure the package once. Do not combine cli.json registration with a local auto-discovered copy.

Page-created popup windows, browser permission prompts, and terminal multiplexers are not supported. Remote browser rendering is supported through the authenticated server path described above.

Cleanup and security

Hiding the workspace keeps Electron ready. Deactivating the plugin from OpenCode's Plugins view—or removing it from cli.json and restarting—detaches that TUI and removes its app slot and authenticated controller. The machine-local Browser daemon and Electron remain ready for other clients and later TUI restarts.

Electron's DevTools server and the controller bind only to loopback. The controller requires a random bearer token passed only to its stdio sidecar, bounds request bodies and concurrent work, and accepts only HTTP(S) navigation. Treat every persistent browser profile as sensitive: an attached DevTools agent can inspect the cookies and storage of the live profile. "global" deliberately broadens that access across projects; "workspace" provides the strongest persistent isolation.

Local development

Install the workspace dependencies once, then launch the checkout from the repository root:

bun install
bun run dev:browser

The launcher packs the checkout into a temporary loopback registry and starts a TUI against a managed local OpenCode core; it does not publish any package. Run the same command in a second terminal to attach another TUI. Both terminals share one OpenCode core and one machine-local Electron daemon, while each TUI keeps its own browser pages. To open a different project, pass --project /absolute/path.

Closing the last TUI leaves the shared development core and Browser daemon ready for another terminal. While Browser has focus, Ctrl-C is sent to Chromium; close each hand-test TUI with Ctrl-Shift-B, then /exit, and only after every TUI exits stop the shared processes with:

bun run dev:browser -- --stop

bun run test:packed uses a sibling ../opencode checkout by default. Set OPENCODE_REPO=/absolute/path/to/opencode when that checkout is elsewhere. bun run test:mcp launches the real Electron runtime and directly installed Chrome DevTools MCP.

Release verification:

bun run check
bun run test:packed
bun run --cwd packages/opencode/browser test:mcp