@sapiom/opencode
v0.1.1
Published
Studio-managed headless OpenCode runtime with a private credential bridge.
Downloads
411
Readme
@sapiom/opencode
Pinned OpenCode 1.18.29 and its matching plugin dependencies are installed with the package. Each native launch links that installed plugin into its private configuration, so opening another Assistant session does not run an npm install or wait for the package registry. Studio owns authorization, working directory, state directory, credentials, and shutdown. OpenCode owns conversations and agent execution.
const config = createSapiomOpenCodeConfig({ bridgeUrl, runtimeToken });
const server = await startOpenCodeServer({ cwd, stateRoot, config, signal });
// Only call this after Studio has authorized the selected workspace and user.
await server.close();The bridge URL must be loopback. Only its revocable credential enters the model
and remote MCP configuration. The default model is sapiom/gpt-luna, using
the bundled Responses provider at the bridge's /llm/v1/responses route.
Low reasoning effort, encrypted reasoning history, and store: false keep
reasoning and tool use compatible with the Sapiom router across turns.
The runtime inherits an allowlist of platform
environment variables; provider keys and the Electron esbuild pin are excluded
from the runtime. A controlled native plugin removes the bridge configuration
and runtime-admin credential from the runtime environment before tool execution,
and startup fails if that plugin does not initialize or if native HTTP
authentication stops rejecting unauthenticated requests. OpenCode project
configuration, global configuration, default plugins, Claude configuration, and
external skills are excluded; the sole configured plugin is created in an
ephemeral config root. The native process also receives an ephemeral home so
OpenCode cannot discover $HOME/.opencode; the controlled shell hook restores
the caller's original home variables for user tools and resolves an absent
HOME from USERPROFILE, HOMEDRIVE/HOMEPATH, or the OS account instead of
exposing the native isolation directory. Runtime state stays below the supplied
directory.
The runtime credential has no independent time-to-live. Its lifetime is bounded by the Studio grant: grant expiry, access revocation, or runtime retirement revokes it at the bridge, and a replacement runtime receives a rotated credential. This protects normal child-process and browser boundaries; it is not an operating-system sandbox. An unrestricted process running as the same user can inspect runtime memory or files and is outside this trust boundary.
Startup and shutdown have deadlines. Studio can durably protect the generated
supervisor through beforeLaunch; native work starts only after that callback
resolves. POSIX cleanup first stops the native launcher, then stops and removes
its birth-validated native-managed descendants before publishing a run-scoped
cleanup proof. A missing proof fails closed so another runtime cannot write the
same state. The proof remains outside the ephemeral launch directory until the
runtime lock consumes it. Windows uses its native process-tree termination for
normal close; installed-platform validation remains tracked by SAP-3297.
Linux process scans tolerate entries that disappear during the read; unreadable
entries still fail cleanup, and a missing tracked process cannot prove it stopped.
Binary paths inside app.asar resolve to their unpacked counterparts; the
generated supervisor needs no source loader and runs under Electron with
ELECTRON_RUN_AS_NODE without forwarding that variable to native or tools.
pnpm must allow the opencode-ai install script so the platform binary exists
before Studio starts. The workspace allowlist includes it. No UI is bundled in
this package.
pnpm test runs the suites that use the in-repo fixture runtime. The
*.native.test.ts files launch the pinned OpenCode binary itself and run in a
separate sequential pass, pnpm test:native, so a cold native startup never
competes with other suites for CPU. In CI that pass is the OpenCode native
workflow, which runs only when this package or the shared build inputs change.
