ocm CLI

ocm is a small CLI that attaches your local OpenCode TUI to a repo hosted on an OpenCode Manager. Prompts execute on the Manager's filesystem against a single shared OpenCode server, while your laptop terminal hosts the TUI.

Quickstart

  1. Get your Manager URL — the web UI address where your OpenCode Manager is running (e.g., https://manager.example.com)
  2. Copy your internal token — open Settings → OpenCode Config → Server maintenance, expand Manager Internal Token, and copy the token
  3. Install the CLI — pnpm add -g @opencode-manager/ocm-cli
  4. Log in — ocm login https://your-manager-url (paste the token when prompted)
  5. List repos — ocm list to see repos configured on the Manager
  6. Attach — ocm use <repo-id> to start an OpenCode session attached to that repo

Compatibility

ocmOpenCode ManagerLocal OpenCode
0.4.x>= 0.19.0 (/ocm-move needs a Manager with worktree moves, 0.22.0 or later)>= 2.0.15, < 3
0.3.x>= 0.19.0>= 2.0.15, < 3

ocm 0.3.0 and later require a local OpenCode 2 client (>= 2.0.15, same major), are configured through the OpenCode 2 cli.json plugins list, attach through the repo-scoped Manager route /api/opencode-proxy/repos/:repoId, and move sessions with OpenCode 2 session export/import. When the Manager does not serve the repo-scoped route (it answers 404), ocm and /ocm-move stop before attaching or pushing with:

OpenCode Manager at <url> is too old for ocm <version>; upgrade the Manager to >= 0.19.0

/ocm-move requires ocm 0.4.0 or later. A newer Manager answers older ocm CLIs with 410 cli_too_old ("upgrade to ocm-cli >= 0.4.0"). An older Manager is reported as "this OpenCode Manager is too old for /ocm-move worktrees; upgrade the Manager."

Keep ocm 0.2.x for OpenCode Manager < 0.19.0 and OpenCode 1.x: install it pinned with pnpm add -g @opencode-manager/[email protected], and pin the plugin entry to @opencode-manager/[email protected] in your OpenCode 1.x TUI config so it does not resolve to the latest release. ocm is published together with each OpenCode Manager release.

When the Manager rejects the stored token (401), returns another error, or cannot be reached while ocm prepares the repo-scoped route, ocm and /ocm-move stop before attaching with a message naming the cause. A rejected token points you to copy the current one from Manager Internal Token and run ocm login <url> again. A repo that is missing or not ready on the Manager (404 with Repo not found) is reported as repo <id> is not available on OpenCode Manager at <url> (missing or not ready). Other errors and network failures are retried three times with a short backoff before ocm gives up.

Architecture Overview

ComponentWhere it runsRole
ocm CLILaptop / local shellLists repos, attaches opencode against the Manager proxy, mirrors $PWD up/down
Manager backendManager serverExposes repo metadata + token-protected OpenCode proxy + mirror endpoints + internal session-goal and multi-run APIs used by the TUI plugin
Manager web UIManager serverReads from the shared OpenCode server; sessions created via ocm appear normally

There is no per-repo OpenCode process. All sessions share one OpenCode server on the Manager, with file-level isolation via the location directory (x-opencode-directory / location[directory]).


1. Install

The CLI is published as @opencode-manager/ocm-cli. There are four install paths.

Add the package to your OpenCode 2 CLI config and OpenCode will fetch it on next start. The package exposes a ./tui entrypoint, and OpenCode resolves that entrypoint automatically from the package name. The package postinstall script self-installs a ~/.local/bin/ocm symlink for local plugin installs, so the ocm binary becomes available on your PATH automatically.

// ~/.config/opencode/cli.json
{
  "$schema": "https://opencode.ai/v2/cli.json",
  "plugins": ["@opencode-manager/ocm-cli"]
}

The next time OpenCode starts it will run bun install for the plugin. The installer stays quiet so it does not break the TUI layout; after the plugin loads, OpenCode shows a one-time toast confirming where ocm was linked.

If ~/.local/bin is not on your PATH, add this to your shell rc:

export PATH="$HOME/.local/bin:$PATH"

The @opencode-manager/ocm-cli package entry registers the TUI commands /ocm-move, /ocm, /ocm-goal, and /ocm-multirun (see TUI commands).

Option B — global package manager install

If you don't use the OpenCode plugin loader, install globally:

pnpm add -g @opencode-manager/ocm-cli

This puts ocm on your PATH via the package manager's own bin shim. The ~/.local/bin symlink is skipped for global installs.

Option C — vendored, self-contained

The package needs no install step: dist/ocm.js bundles everything, and dist/tui.js bundles everything except @opencode/plugin/tui, @opentui/core, @opentui/solid, and solid-js, which OpenCode provides at runtime. ocm install copies the package into your OpenCode config directory, registers the plugin in cli.json, and links the binary onto your PATH:

pnpm dlx @opencode-manager/ocm-cli install

If ocm is already installed (Option A or B), run the same command directly:

ocm install

The command is idempotent: re-run it to upgrade after a new release, or run the wrapper script below to vendor a local build. It writes the plugin as ./plugin/ocm-cli/dist, a directory entry — OpenCode 2 loads tui.js from a directory entry and skips entries that name a file directly.

FlagEffect
--dir <path>Install into a different OpenCode config directory (default: $OPENCODE_CONFIG_DIR, else $XDG_CONFIG_HOME/opencode, else ~/.config/opencode)
--no-linkSkip the ~/.local/bin/ocm symlink
--forceReplace a non-symlink ocm already at the link path

From this repository, build the package and run the wrapper script instead:

pnpm --filter @opencode-manager/ocm-cli build
./ocm-cli/scripts/install.sh

Option D — from this repository (dev)

pnpm install
pnpm --filter @opencode-manager/ocm-cli build
node ocm-cli/scripts/postinstall.mjs

The package postinstall creates the ~/.local/bin/ocm symlink, but skips it when dist/ocm.js does not exist yet, which is the case during the first pnpm install of a fresh clone. Run it again after building, as above.

2. Log in

Use the URL where your Manager web UI is accessible:

ocm login https://your-manager-url
# paste your Manager internal token when prompted

Pass --name <name> to label the manager, for example ocm login https://box.example.com --name "Build box". The attached TUI shows that name where it would otherwise show the manager host. A name is dropped when you log in to a different manager URL without --name; logging in to the same URL again keeps it. ocm status prints the stored name.

The token is stored in a platform-specific token store: the macOS Keychain (service opencode-manager, account = manager URL) on macOS, or ~/.config/opencode-manager/credentials.json at mode 0600 on Linux. On Linux the token is plaintext JSON protected only by file permissions. Run ocm status to see the active store. The manager URL itself is persisted to ~/.config/opencode-manager/state.json.

Windows is not supported: the CLI falls back to the same file store, but the 0600 mode is not enforced there and hidden token entry requires bash.

View, copy, or rotate your internal token from Settings → OpenCode Config → Server maintenance → Manager Internal Token (Settings cog in the sidebar). A token exists by default: the disclosure shows it behind an eye toggle with a copy button. Click the refresh icon to rotate it; a second click confirms, invalidating the previous token and marking an OpenCode server restart as pending.


3. Commands

ocm                       Attach to the Manager repo matching $PWD's OpenCode
                          project id; inside a git repo with no match, launch
                          local opencode; outside a git repo, use the last
                          selected repo, else launch local opencode
ocm login <url> [token] [--name <name>]
                          Save manager URL + token (token via stdin if
                          omitted); --name labels the manager in the TUI
ocm logout                Forget saved token and state
ocm status                Show current manager URL and name, repo, and whether
                          token is set
ocm list                  List ready repos from the manager
ocm use <repoId|name>     Attach to a specific repo and remember it as last
ocm push [repoId] [--force] [--create] [--yes] [--full]   Mirror $PWD to the matching Manager repo (fast bundle/patch sync by default)
ocm pull [repoId] [--force] [--full]                      Mirror the matching Manager repo over $PWD (fast bundle/patch sync by default)
ocm install [--dir <path>] [--force] [--no-link]  Vendor the CLI + TUI plugin into the OpenCode config dir
ocm link [--url <url>] [--password <pw>] [--username <name>] [--name <name>]
         [--pair-host <host>] [--pair-port <port>] [--ttl <seconds>] [--no-qr]
                          Pair a raw OpenCode server with a Manager via QR or link
ocm --version             Show the installed ocm version
ocm --help                Show this help

ls is an alias for list, attach for use, help/-h for --help, and version/-v for --version.

How bare ocm resolves the target

  1. If $PWD is inside a git repo, compute its OpenCode project id (the same identity OpenCode uses: the normalized origin remote hash, else the cached <git-common-dir>/opencode id, else the sorted first root commit). If exactly one ready Manager repo shares that project id, attach to it and remember it as last.
  2. If multiple Manager repos share it, fail with a hint to use ocm use <repoId>, listing each match's id, kind (repo or worktree), branch, and path.
  3. If $PWD is inside a git repo but no Manager repo matches, launch local opencode; the last selected repo is not consulted.
  4. Only when $PWD is outside a git repo does ocm fall back to the previously used repo (last), and launch local opencode when there is no last.

Attach command equivalent

Under the hood, ocm execs (OpenCode 2 connects with --server and reads the password from OPENCODE_PASSWORD):

OCM_REMOTE_MANAGER_URL=https://manager.example.com \
OCM_REMOTE_REPO_NAME=my-repo \
OCM_REMOTE_REPO_ID=42 \
OPENCODE_PASSWORD=<manager-token> \
  opencode --server https://manager.example.com/api/opencode-proxy/repos/42

A warp after /ocm-move also passes --session <id> to open the moved session.

OPENCODE_PASSWORD is set only on this local opencode client invocation that ocm execs, from the stored Manager token; you do not need to export it yourself. Never put OPENCODE_PASSWORD in the OpenCode Manager server environment: the Manager strips it from its own env because it would override the Manager-managed OpenCode server password.

The repo-scoped proxy mount resolves the request location: a requested directory inside the Manager workspace root (for example a worktree) passes through unchanged, while a missing or out-of-workspace directory is replaced by the repo directory. This applies to the x-opencode-directory header (always set to the resolved directory), a location[directory] query, the session list directory filter, and a location in a JSON body, so the child TUI can run from any local working directory. It is convenience scoping, not isolation: routes addressed by session ID are not restricted to the repo, and the Manager token grants full access to the OpenCode API through the unscoped /api/opencode-proxy/* route as well. The child takes over the terminal (stdio: inherit); closing the TUI exits ocm but leaves the Manager-side session intact.

When the TUI plugin is installed, these internal child-process variables add a <host> · <repo> indicator to the prompt and home footers of Manager-attached TUI windows and tell /ocm, /ocm-goal, and /ocm-multirun which Manager and repo they act on. The host is replaced by the name given to ocm login --name when one is stored for that manager. Local launches show no indicator.

Mirror commands

ocm push uses a fast git bundle + working-tree patch by default to sync $PWD to the matching Manager repo; the fast path prints a single result line. Pass --full to use the legacy tarball mirror (skipping node_modules, dist, .next, .venv, __pycache__, .turbo, .DS_Store, ._*, and anything matched by .gitignore), which shows byte-count upload progress. If the fast path fails, ocm prompts before reverting to the tarball mirror (and proceeds automatically when there is no TTY to prompt). Creating a repo with --create always uploads a tarball.

ocm pull uses a fast git bundle + working-tree patch by default to sync the matching Manager repo over $PWD. Pass --full to use the legacy tarball mirror. If the fast path fails, ocm prompts before reverting to the tarball mirror (and proceeds automatically when there is no TTY to prompt). It refuses to overwrite uncommitted local changes unless --force is passed.

Safety aborts, such as uncommitted local changes on pull or a branch checked out in another worktree, stop the command instead of falling back to the tarball mirror. Without --force, push asks before discarding server-side work (commits not present locally, or uncommitted changes on the Manager), and pull asks before discarding local commits the Manager does not have. Without a TTY both refuse; pass --force to override.

When the Manager's main checkout is not on your local branch but a worktree of that repo is (including a <branch>-ocm / <branch>-ocm-N worktree created by /ocm-move), push and pull target that worktree instead. The exact branch is preferred, then -ocm, then -ocm-N. Pulling from a suffixed worktree updates your local branch, so no -ocm branch is created locally. A targeted worktree has no tarball fallback, and --full is refused because it would overwrite the main checkout. Against an older Manager that does not expose the checkout routes, both commands keep the previous behavior and target the main checkout.

A base repo and one of its worktrees can both be registered as ready Manager repos sharing the same OpenCode project id. ocm push [repoId] and ocm pull [repoId] then take the target repo id; it must belong to one of the repos matching the current project, and any ambiguity message lists each match with its id, kind (repo or worktree), branch, and path.

  • --force on push skips the confirmation before discarding server-side work; on pull it skips the uncommitted-changes refusal and the local-commits-ahead confirmation.
  • --create (on push) creates or reuses a Manager repo when no project match is found.
  • --yes skips the interactive create confirmation (required when stdin is not a TTY).

ocm link pairs a raw OpenCode server with a Manager, so you do not copy its password by hand. Run it on the machine that hosts the server; with the OpenCode background service (opencode serve --service) running there, no flags are needed:

ocm link

ocm link reads the service registration in $XDG_STATE_HOME/opencode/service.json (default ~/.local/state/opencode/service.json), then the opencode service config in $XDG_CONFIG_HOME/opencode/service.json (default ~/.config/opencode/service.json), which provides the hostname (default 127.0.0.1), the port (default 49374, or 45278 on Windows), and the password. A wildcard host such as 0.0.0.0 is replaced with the machine's LAN IPv4 address. A service that only listens on loopback gets a warning, because a remote Manager cannot reach it; run opencode service set hostname 0.0.0.0 to expose it.

An explicit --url or $OPENCODE_SERVER_URL skips that detection, so the detected service password is not reused and you pass one yourself:

OPENCODE_SERVER_PASSWORD=your-server-password ocm link --url http://192.168.1.20:4096

It checks GET <url>/api/info with Basic auth and prints a terminal QR code plus an ocm://link URL. Scan the code in Settings → Servers → Add server → Scan QR, or paste the link, then save the server. The command prints Linked and exits after the Manager redeems it.

FlagDefault
--url <url>$OPENCODE_SERVER_URL, else the detected OpenCode service URL, else http://<first non-internal IPv4>:4096; an explicit value skips service detection
--password <pw>$OPENCODE_SERVER_PASSWORD, else the detected OpenCode service password; an explicit URL needs one of the two
--username <name>$OPENCODE_SERVER_USERNAME, else opencode
--name <name>the machine hostname
--pair-host <host>the host of --url
--pair-port <port>0 (an ephemeral port)
--ttl <seconds>300
--no-qrprint only the link

A 401 stops the command with a credentials message. An unreachable server only warns and still prints a code, so you can pair first and fix reachability later.

The pairing listener binds 0.0.0.0 and serves one POST /redeem; it must be reachable from the Manager. The code is single use, expires when the TTL ends, and the listener exits after three wrong attempts. It prints Pairing code expired or too many invalid pairing attempts; no Manager was linked when it gives up. Links use version v=2, and a Manager with a different expected version rejects the link with a message to update one side.

The listener and the OpenCode server use plain HTTP unless you put them behind TLS. Use HTTPS or a private network. See Linked Servers for the full setup and what runs on a linked server.

TUI commands

The TUI plugin registers four commands, also listed in the command palette under OpenCode Manager.

/ocm-move

When the TUI plugin entry is installed, /ocm-move is available in local OpenCode sessions. Run it after ocm login and after the repo exists on the Manager (ocm push --create if needed). It pushes your local working tree (commits, staged, unstaged, and untracked files; gitignored files on the Manager are preserved) and never overwrites a server checkout or branch that holds work your local branch lacks. Other server branches present in your local repo are still updated to your local refs unless they are checked out in a worktree. The Manager checkout is replaced in place only when it is already on your branch, clean, and has nothing your local branch lacks. Otherwise the push lands in an OpenCode worktree of the same Manager repo: it reuses the first clean worktree on your branch or on a <branch>-ocm / <branch>-ocm-N branch whose commits are all in your local branch (including worktrees registered as Manager repos by older versions), or creates a new OpenCode worktree in one step (upload, import of your commits and working-tree changes, then the repo's worktree setup commands; the worktree is removed if the import fails). A new worktree uses your branch name unless that branch is already checked out on the server or the server branch has commits your local branch lacks; it then uses the next free <branch>-ocm[-N] branch, so the server branch is never rewound. A failed worktree setup is reported as a warning and the session still moves. A detached HEAD is refused before anything is pushed. A confirmation dialog gates the move before any push; it names the destination path and branch and, for a new worktree, why the server checkout was not used. -ocm worktrees are not removed automatically; delete them from the repo's Worktrees tab when done. When multiple Manager repos match, the one already on your branch is chosen; otherwise a select dialog lets you pick the destination. It then exports the active session through the local OpenCode 2 server (session.export), rewrites local repo directories and file attachment URIs to the Manager repo directory, imports the session's messages through the Manager proxy (session.import), and sends a synthetic reminder (session.synthetic) to the remote session (best-effort, never fails the move). The local session is retained. On success you can choose to warp — exit the local TUI and attach to the moved session on the Manager immediately (through the matched repo, opening the moved session in its checkout) — or keep the local copy with the previous toast behavior. While the move runs, a spinner with the current phase (bundling, uploading, server processing, patching, importing) and a progress bar is shown next to the prompt.

Before pushing anything, /ocm-move refuses a session that is already on the Manager (run ocm to attach to it) and a subagent session whose parent is not on the Manager (move the parent first).

/ocm

/ocm switches this TUI to another server, the same way the ocm CLI picks one. In a local TUI it finds the Manager repo that matches the current directory (by OpenCode project id) and asks to attach to it; when no repo or several repos match, it shows a picker of the Manager's ready repos, with matches listed first. In a TUI that is already attached, the picker offers the other Manager repos (the current one is listed as disabled) and Local opencode, which stays available even when the Manager cannot be reached. Switching exits the TUI and reattaches; switching to a repo also remembers it as the last repo, and switching to local launches opencode without the Manager attach variables. The current session stays where it is (use /ocm-move to push your local state and bring a local session along).

/ocm-goal

/ocm-goal [objective] starts a Manager-driven goal on the current top-level session of an attached Manager repo and sends the objective as the next message (queued if the session is busy). With no objective, a dialog asks for it, plus optional max turns (1-200) and token budget (a positive whole number); blank uses the Manager defaults. tab moves between fields and ctrl+s starts the goal.

The objective is sent as plain text with the session's current agent and model: the plugin cannot read the composer's selection, so send a message first if you switched agent or model, and @file mentions are not attached. If the objective cannot be sent, the goal is cancelled. Scheduled runs and subagent sessions are rejected by the Manager.

While a goal is open, a one-line status above the composer shows <status> · Turn n/max, token usage when the goal has a token budget, and the first line of the objective. The TUI keeps following the goal after you leave the session, and a toast reports the outcome with an option to open the session. Running /ocm-goal while a goal is open shows its live status with p to pause or resume and x (twice) to cancel.

/ocm-multirun

/ocm-multirun [prompt] runs one prompt across several models on the attached Manager repo. With a prompt it opens a launch dialog: prompt, name (defaults to the prompt's first line), a filterable model checklist (up to 5, enter toggles), isolated workspaces (recommended) or the shared repo directory, and, for isolated workspaces, an optional ref to start from (default: current HEAD). ctrl+s launches; the started sessions open in tabs, or the first one opens when tabs are off.

With no prompt, /ocm-multirun opens the runs browser for the attached repo:

WhereKeyAction
Run list↑ / ↓Select a run
Run listenter / →Open the run
Run list or runnStart a new multi-run
Run list or runrRefresh
Runenter / oOpen the entry or fusion session
RunspaceSelect a started result for fusion
Rund (twice)Discard an entry (and remove its workspace when isolated)
Run←Back to the run list
Run list or runfOpen the fusion form (with nothing selected, the run's started results are preselected)
Fusion formctrl+s / ctrl+bFuse / go back

A fusion needs 2-5 started results. The fusion form takes a synthesis model, an optional ref to start from, and optional instructions. A fusion always runs in a new isolated workspace.

Older Managers

/ocm-goal and /ocm-multirun need an OpenCode Manager release that exposes /api/internal/session-goals and /api/internal/multi-runs. An older Manager rejects these routes with 401 Unauthorized even for a valid token, or with a non-JSON 404; on a 401 the TUI checks the token against another Manager route and then reports that the Manager must be upgraded instead of asking you to log in again.

/ocm-move likewise needs a Manager that serves /api/internal/repos/:repoId/mirror/checkouts and /mirror/worktree; on a 401 with a valid token it reports that the Manager is too old for /ocm-move worktrees.

These commands, and /ocm, authenticate with the token stored for the attached Manager URL (or OCM_TOKEN), not with OPENCODE_PASSWORD; /ocm-multirun also needs OCM_REMOTE_REPO_ID from the attach.


4. Environment variables

The CLI's environment and token inputs:

VariableDescription
OPENCODE_MANAGER_URLManager base URL (e.g., https://manager.example.com). Not currently consumed by the CLI — use ocm login.
OPENCODE_PASSWORDSet by ocm on the local opencode child only (value = Manager token). Client-side only; never set it in the Manager server environment, which strips it.
OCM_REMOTE_MANAGER_URLInternal child-process context set by ocm attach; shows the remote TUI indicator and is the Manager that /ocm, /ocm-goal, and /ocm-multirun authenticate against (with the token stored for that URL, or OCM_TOKEN). Not a login setting.
OCM_REMOTE_REPO_NAMEInternal child-process context set by ocm attach; labels the remote TUI indicator. Not a login setting.
OCM_REMOTE_REPO_IDInternal child-process context set by ocm attach; the attached Manager repo id. Required by /ocm-multirun and marks the current repo in /ocm. Not a login setting.
OPENCODE_CONFIG_DIR, XDG_CONFIG_HOMESelect the OpenCode config directory ocm install writes to when --dir is not given.
OCM_TOKENRead-only override for the stored token; takes priority over the platform token store. ocm login never writes it, and ocm logout cannot remove it. Because the manager URL still comes from state.json, CI must run ocm login first, so this override does not yet avoid writing a token to disk.
Token store entry under <manager url>Token used for Bearer auth on Manager API calls and Basic auth on the OpenCode proxy. macOS Keychain (service opencode-manager) or ~/.config/opencode-manager/credentials.json (mode 0600) on Linux.

EndpointMethodPurpose
/api/internal/opencode-workspacesGETList ready repos with directory + originUrl
/api/internal/repos/:repoId/mirror/beginPOSTBegin a chunked tarball upload (main checkout only; pass repoId 0 with create to create the repo)
/api/internal/repos/:repoId/mirror/parts/:uploadId/:indexPUTUpload one tarball chunk (main checkout only)
/api/internal/repos/:repoId/mirror/commitPOSTCommit the uploaded chunks into the repo dir (main checkout only)
/api/internal/repos/:repoId/mirror/uploads/:uploadIdDELETEAbort an upload and clean up staging
/api/internal/repos/:repoId/mirrorGETStream a tarball of the repo dir (the --full pull; main checkout only)
/api/internal/repos/:repoId/mirror/bundlePOSTUpload a git bundle (fast push; optional directory query targets a worktree of the repo)
/api/internal/repos/:repoId/mirror/bundleGETDownload a git bundle (fast pull; optional directory)
/api/internal/repos/:repoId/mirror/patchGETSnapshot branch, HEAD, and working-tree patch (optional directory)
/api/internal/repos/:repoId/mirror/patchPOSTApply a working-tree patch (fast push; optional directory in the body)
/api/internal/repos/:repoId/mirror/headGETRead the server branch, HEAD, and dirty state (optional directory)
/api/internal/repos/:repoId/mirror/contains/:shaGETCheck whether a commit is in the server HEAD (optional directory)
/api/internal/repos/:repoId/mirror/checkouts?branch=GETMain checkout and candidate worktree states for a branch (/ocm-move, push/pull targeting)
/api/internal/repos/:repoId/mirror/worktreePOSTCreate an OpenCode worktree and import an uploaded bundle into it (/ocm-move)
/api/internal/repos/:repoId/mirror/targetGET, POSTRemoved; answers 410 cli_too_old
/api/internal/session-goals?sessionId=GETRead a session's latest goal (/ocm-goal, composer status)
/api/internal/session-goalsPOSTStart a goal
/api/internal/session-goals/:id/pausePOSTPause a goal
/api/internal/session-goals/:id/resumePOSTResume a goal
/api/internal/session-goals/:id/cancelPOSTCancel a goal
/api/internal/multi-runs?repoId=GETList a repo's multi-runs (/ocm-multirun runs browser)
/api/internal/multi-runsPOSTLaunch a multi-run
/api/internal/multi-runs/:id/fusionsPOSTFuse started results
/api/internal/multi-runs/:id/entries/:entryId/discardPOSTDiscard a multi-run entry
/api/opencode-proxy/*ALLToken-protected proxy from Manager to single OpenCode server (ticketed PTY WebSocket connects skip the token check)
/api/opencode-proxy/repos/:repoId/*ALLToken-protected proxy that resolves the request location to the repo directory unless a directory inside the Manager workspace is requested