Skip to content

Repository files navigation

paseo-plugin-vscode-web

Open any Paseo workspace in vscode.dev — real VS Code in a browser, attached to the actual worktree on the machine running the Paseo daemon.

Paseo already hands a workspace to a local editor. This adds the web one, so a worktree is reachable from a browser tab, a second machine, or a phone — including when the daemon is a headless box you have no editor installed on.

What you get

VS Code Cloud in the sidebar — every workspace on the host in one list, grouped by project, each with its links. Machine setup happens here once.

VS Code Web as a panel on each workspace, and on each agent — where it can also open the editor inside Paseo. Both are reachable from ⌘K.

Both offer up to two links:

Link Reaches Terminal Needs
vscode.dev/tunnel/<machine>/<path> the live worktree, uncommitted changes and all yes a VS Code tunnel, set up from the panel
vscode.dev/github/<owner>/<repo>/tree/<branch> the pushed branch on GitHub no a GitHub remote and an upstream branch

The tunnel link is the interesting one: the same files the agent is editing, with a working terminal, from any browser.

Install

git clone https://github.com/itsjustanks/paseo-plugin-vscode-web.git
cd paseo-plugin-vscode-web
npm install
paseo plugin install "$PWD"

Needs Paseo 0.5.0-beta.1 or newer (where local plugins landed) and pluginsEnabled: true in the daemon's config.json.

Setup

Open VS Code Cloud from the sidebar. Three steps, each ticking off as it completes:

  1. VS Code CLI — found automatically if it is already on the machine, including inside an installed VS Code, where it usually is not on PATH. Otherwise press Download CLI: the plugin fetches Microsoft's standalone, editor-free CLI for the detected platform into $PASEO_HOME/plugin-data/vscode-web/cli. Nothing is installed system-wide and no package manager is involved, which is what makes a headless Linux daemon behave like a Mac. Set PASEO_VSCODE_CLI to point at a specific binary instead.
  2. Connect this machine — one GitHub device login. The code appears in the panel and it polls until GitHub accepts it.
  3. Start tunnel — installs the tunnel as a background service, so it survives a reboot.

Every workspace then shows its links. Stop tunnel uninstalls the service and kills the tunnel.

Supported download targets: darwin and win32 on x64/arm64, linux on x64/arm64/armhf, and alpine on x64/arm64 (musl).

Where the editor opens

Open launches the daemon machine's default browser.

To keep the editor inside Paseo, copy the link into a Paseo browser tab. vscode.dev renders there in full, because Paseo's browser tabs are Electron <webview> guests and a guest is its own frame-tree root, so the content-security-policy: frame-ancestors 'none' that vscode.dev serves never applies to it.

Or have an agent do it

On an agent tab the panel offers Open as a Paseo tab, which does land the editor inside Paseo automatically. It works by asking the agent to call its own browser_new_tab tool — that tool's whole job is "create a new Paseo browser tab in this agent's workspace at a URL".

The trade is honest: it costs the agent one turn, it only exists where an agent does, and it needs daemon.browserTools.enabled: true in the daemon's config.json (the panel says so if it fails). If you would rather not spend a turn, the copy-and-shortcut route is right there beside it.

Why the plugin cannot just do it

Paseo can open a browser tab at a URL from three places — the renderer's createWorkspaceBrowser({ initialUrl }), the daemon's browser-automation channel (new_tab/navigate), and the agent tools above. None is reachable from plugin code:

  • the plugin SDK covers agents, workspaces, providers and config, with no browser namespace;
  • command callbacks get only openSurface/openPanel, limited to the plugin's own registrations;
  • paseo:// deep links route to agents, workspaces, settings, projects and plugin surfaces — but there is no browser route;
  • the daemon's browser broker is wired solely to agent tools;
  • plugin surfaces are React Native with no WebView, so a panel cannot render a page itself.

Hence: an agent can, a plugin cannot, and the agent hand-off is the bridge.

Security notes

  • A tunnel is bound to your GitHub account. It is not a public URL — opening it prompts for the same sign-in. It does expose the paths you point it at, with a terminal, so treat it like SSH access to that machine.
  • Plugin backends are unsandboxed by design. This one only opens https links on vscode.dev, insiders.vscode.dev, github.dev, and github.com, and refuses everything else, so a link cannot become an arbitrary command.
  • Workspace directories are resolved and checked to be real directories before any git call.
  • No token is read or stored by the plugin; the VS Code CLI owns the tunnel credential under ~/.vscode/cli. Nothing is written to your repositories.

Limits

  • The GitHub link shows the pushed commit. A branch with no upstream gets no link, and the view says so rather than offering a 404.
  • The daemon machine must be awake for a tunnel link to resolve.
  • The tunnel machine name comes from the VS Code CLI (~/.vscode/cli/code_tunnel.json), falling back to the hostname. Rename with code tunnel rename <name>.
  • Sidebar ordering is Paseo's; a plugin contributes a title and a Lucide icon, not a position.
  • Branch state is cached for 45 seconds, so a fresh push can take that long to appear.

Why not code-server?

code-server never touches vscode.dev — it is a separate self-hosted server on 127.0.0.1:8080 with its own ?folder= URL. On the machine you are sitting at, that is a fine alternative. On a remote or headless daemon it is worse: you would have to reach that port over SSH, a VPN, or a reverse proxy, whereas a VS Code tunnel dials out and vscode.dev reaches it from anywhere with nothing opened. So the tunnel wins exactly where a web editor is most useful.

Development

npm run typecheck
paseo plugin reload vscode-web
paseo plugin logs vscode-web

*.client.tsx runs in the app (React Native), *.server.ts in a Node subprocess beside the daemon, *.shared.ts holds the Zod contracts between them. Paseo rejects cross-imports.

Machine-level state (CLI, account, tunnel) and workspace-level state (links) are deliberately separate contracts. That is what lets the sidebar show setup once and list every workspace from a single vscode.workspaces call resolved entirely on the daemon.

Two things worth knowing if you extend it. Surfaces do not share a React Query cache, so a panel cannot rely on another surface invalidating it — each polls to stay honest. And the workspace payload on the wire carries workspaceDirectory / projectRootPath, not the directory field that the client-side snapshot exposes.

Licence

MIT

About

Open any Paseo workspace in vscode.dev — real VS Code in the browser, attached to the live worktree over a VS Code tunnel.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages