OpenCode marks an MCP server failed when it does not come up at startup or when a live connection drops, and never retries. A new managed plugin reconnects those servers with a per-server backoff (1s doubling to a 30s cap), wakes early on mcp.tools.changed, and stops on dispose. Only the failed state is retried; disabled and auth states stay untouched. The OPENCODE_CONFIG_CONTENT merge that agent-tool and system-prompt each carried is now one shared helper so the three managed plugins compose. Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
436 lines
33 KiB
Markdown
436 lines
33 KiB
Markdown
# OpenCode Module Documentation
|
|
|
|
## Purpose
|
|
This module provides OpenCode server integration utilities for the web server runtime, including configuration management and provider authentication.
|
|
|
|
## Entrypoints and structure
|
|
- `packages/web/server/lib/opencode/index.js`: public entrypoint (currently baseline placeholder).
|
|
- `packages/web/server/lib/opencode/auth.js`: provider authentication file operations.
|
|
- `packages/web/server/lib/opencode/auth-state-runtime.js`: managed OpenCode server auth password/header runtime.
|
|
- `packages/web/server/lib/opencode/cli-options.js`: CLI/environment option parsing for server startup arguments.
|
|
- `packages/web/server/lib/opencode/cli-entry-runtime.js`: CLI entrypoint runtime that detects direct execution, parses CLI options, and starts server bootstrap.
|
|
- `packages/web/server/lib/opencode/routes.js`: OpenCode/provider settings and auth-related route registration.
|
|
- `packages/web/server/lib/opencode/lifecycle.js`: OpenCode process lifecycle runtime (startup, restart, readiness, health monitoring). After readiness it warms the most recently used directories (`getWarmupDirectories` dep, sequential and best-effort) because OpenCode initializes each directory lazily on first request and that cost would otherwise be paid by the user's first interactive session open.
|
|
- `packages/web/server/lib/opencode/provider-env-aliases.js`: mirrors known provider credential env aliases into the managed OpenCode process environment (for example `GEMINI_API_KEY` → `GOOGLE_GENERATIVE_AI_API_KEY`) so OpenCode connection detection and the upstream AI SDK agree on the same key names. Canonical implementation shared by web lifecycle and the VS Code managed spawn path (`packages/vscode/src/provider-env-aliases.ts` re-exports this module).
|
|
- `packages/web/server/lib/opencode/env-runtime.js`: OpenCode CLI/binary resolution and shell environment runtime.
|
|
- `packages/web/server/lib/opencode/env-config.js`: OpenCode-related environment variable parsing and validation (host/port/hostname).
|
|
- `packages/web/server/lib/opencode/hmr-state-runtime.js`: HMR-persistent runtime state initialization, auth-state bootstrap, and HMR sync helpers.
|
|
- `packages/web/server/lib/opencode/bootstrap-runtime.js`: base app bootstrap runtime for status/auth/tts/notification/OpenChamber route wiring.
|
|
- `packages/web/server/lib/opencode/network-runtime.js`: OpenCode URL construction, health-probe readiness checks, and API prefix runtime.
|
|
- `packages/web/server/lib/opencode/project-directory-runtime.js`: request-scoped and settings-backed project directory resolution/validation runtime.
|
|
- `packages/web/server/lib/opencode/config-entity-routes.js`: route registration for agent/command/MCP config orchestration with deferred-apply semantics (`restartDeferred` payloads; explicit apply via `POST /api/config/reload`).
|
|
- `packages/web/server/lib/opencode/config-mutation-response.js`: shared response builders for deferred OpenCode restarts and external manual-restart guidance.
|
|
- `packages/web/server/lib/opencode/snippets.js`: opencode-snippets-compatible snippet file CRUD, discovery, and hashtag expansion.
|
|
- `packages/web/server/lib/opencode/cli-options.js`: CLI/environment option parsing for server startup arguments.
|
|
- `packages/web/server/lib/opencode/core-routes.js`: server status/system routes, auth/access guard routes, and settings utility route registration.
|
|
- `packages/web/server/lib/opencode/shutdown-runtime.js`: graceful shutdown orchestration runtime for watcher/session/terminal/process/server teardown.
|
|
- `packages/web/server/lib/opencode/server-startup-runtime.js`: server listen/startup tunnel flow and process/signal handler orchestration runtime.
|
|
- `packages/web/server/lib/opencode/static-routes-runtime.js`: static asset/SPA fallback route registration and manifest route wiring.
|
|
- `packages/web/server/lib/opencode/feature-routes-runtime.js`: feature route composition runtime for dynamic import-backed config/skill/provider route registration.
|
|
- `packages/web/server/lib/opencode/opencode-resolution-runtime.js`: OpenCode binary resolution snapshot runtime for settings routes and diagnostics.
|
|
- `packages/web/server/lib/opencode/upgrade-capability.js`: authoritative upgrade ownership policy for the active OpenCode runtime. Bundled, external, and unresolved runtimes fail closed; only managed non-bundled runtimes delegate upgrades to OpenCode.
|
|
- `packages/web/server/lib/opencode/tunnel-wiring-runtime.js`: tunnel service/routes composition runtime and active-port wiring for main server startup.
|
|
- `packages/web/server/lib/opencode/startup-pipeline-runtime.js`: server startup tail orchestration runtime for terminal/proxy/static/start-listen flow.
|
|
- `packages/web/server/lib/opencode/startup-performance.js`: opt-in startup phase diagnostics with fixed labels and numeric metadata allowlists.
|
|
- `packages/web/server/lib/agent-tool/runtime.js`: managed OpenCode custom-tool materialization, environment injection, loopback authentication, and fixed CLI action dispatch.
|
|
- `packages/web/server/lib/system-prompt/runtime.js`: opt-in managed OpenCode system-prompt optimizer materialization and plugin injection.
|
|
- `packages/web/server/lib/mcp-reconnect/runtime.js`: always-on managed OpenCode plugin that reconnects MCP servers OpenCode marked `failed`, with per-server backoff.
|
|
- `packages/web/server/lib/opencode/managed-plugin-config.js`: the one `OPENCODE_CONFIG_CONTENT` merge every managed plugin (agent tools, system prompt optimizer, MCP reconnect) appends itself through.
|
|
- `packages/web/server/lib/opencode/server-utils-runtime.js`: shared server runtime utilities for OpenCode proxy wiring, OpenCode port/readiness helpers, and snapshot fetchers.
|
|
- `packages/web/server/lib/opencode/openchamber-routes.js`: OpenChamber update and models metadata route registration.
|
|
- `packages/web/server/lib/opencode/pwa-manifest-routes.js`: PWA manifest route registration with recent-session shortcut resolution and short-lived caching.
|
|
- `packages/web/server/lib/opencode/project-icon-routes.js`: project icon upload/read/discovery route registration and icon storage orchestration.
|
|
- `packages/web/server/lib/opencode/skill-routes.js`: route registration for skill config CRUD, supporting files, and skills catalog scan/install flows.
|
|
- `packages/web/server/lib/opencode/settings-runtime.js`: Settings persistence runtime (disk IO, migrations, normalization, project validation, and persisted update serialization).
|
|
- `packages/web/server/lib/opencode/settings-helpers.js`: Settings payload sanitization/format helpers runtime for response shaping and persisted merge prep.
|
|
- `packages/web/server/lib/opencode/settings-normalization-runtime.js`: path/settings/tunnel normalization and sanitization helpers runtime used by settings/routes/config wiring.
|
|
- `packages/web/server/lib/opencode/theme-runtime.js`: custom theme JSON validation and theme directory loading runtime for settings utility routes.
|
|
- `packages/web/server/lib/opencode/proxy.js`: OpenCode API/SSE forwarding and readiness-gate route registration.
|
|
- `packages/web/server/lib/opencode/session-runtime.js`: session status/attention/activity runtime for OpenCode SSE events.
|
|
- `packages/web/server/lib/opencode/watcher.js`: global SSE watcher runtime for push/session event fanout.
|
|
- `packages/web/server/lib/opencode/shared.js`: shared utilities for config, markdown, skills, and git helpers.
|
|
- `packages/web/server/lib/ui-auth/ui-auth.js`: UI session authentication runtime (outside OpenCode module).
|
|
- `packages/web/server/lib/ui-auth/ui-passkeys.js`: UI passkey storage and WebAuthn registration/authentication helpers (outside OpenCode module).
|
|
|
|
## Public exports (auth.js)
|
|
- `readAuthFile()`: Reads and parses `~/.local/share/opencode/auth.json`.
|
|
- `writeAuthFile(auth)`: Writes auth file with automatic backup.
|
|
- `removeProviderAuth(providerId)`: Removes a provider's auth entry.
|
|
- `getProviderAuth(providerId)`: Returns auth for a specific provider or null.
|
|
- `listProviderAuths()`: Returns list of provider IDs with configured auth.
|
|
- `AUTH_FILE`: Auth file path constant.
|
|
- `OPENCODE_DATA_DIR`: OpenCode data directory path constant.
|
|
|
|
## Public exports (providers.js)
|
|
- `getProviderSources(providerId, workingDirectory)`: Resolves which OpenCode config layers define a provider.
|
|
- `upsertProviderConfig(providerId, config, workingDirectory, scope?, options?)`: Validates and writes a custom OpenAI-compatible provider block (`npm`, `name`, `options.baseURL`, `models`, optional `env`/`headers`) into the user/project/custom config layer. Does not store API keys. Requires `config.env` or `options.hasStoredAuth` (auth already written via OpenCode `auth.set`). Edit flows must pass the provider's effective existing layer (`custom` > `project` > `user`) so updates do not create a global user override.
|
|
- `validateCustomProviderConfig(providerId, config, options?)`: Structural validation for custom provider payloads (id format, http(s) base URL, models, credentials via `env` or `hasStoredAuth`).
|
|
- `removeProviderConfig(providerId, workingDirectory, scope?)`: Removes a provider block from the selected config layer.
|
|
|
|
## Public exports (shared.js)
|
|
- `OPENCODE_CONFIG_DIR`, `AGENT_DIR`, `COMMAND_DIR`, `SKILL_DIR`, `CONFIG_FILE`: Path constants. `OPENCODE_CONFIG` is resolved at call time for the custom config layer path.
|
|
- `AGENT_SCOPE`, `COMMAND_SCOPE`, `SKILL_SCOPE`: Scope constants with USER and PROJECT values.
|
|
- `ensureDirs()`: Creates required OpenCode directories.
|
|
- `parseMdFile(filePath)`, `writeMdFile(filePath, frontmatter, body)`: Markdown file operations with YAML frontmatter.
|
|
- `getConfigPaths(workingDirectory)`, `readConfigLayers(workingDirectory)`, `readConfig(workingDirectory)`: Config file operations with layer merging (user, project, custom). `readConfigLayers` isolates `INVALID_JSONC` per layer: a broken file is omitted from the merge (`{}` for that layer only), recorded on `layerErrors`, and does not block valid sibling layers. Writes still refuse to overwrite the broken file.
|
|
- `readConfigFile(filePath)`: Reads one config file. Missing, whitespace-only, and comment-only files return `{}`; a comment-only file is recognized by `ValueExpected` being the only parse error. A `jsonc-parser` error that produces a partial or non-object tree throws `INVALID_JSONC` — partial parse trees must never be treated as authoritative (avoids rewriting a `$schema`-only stub over a full config). Content that yields no JSON value for any other reason (YAML, plain text) also throws instead of reading as empty.
|
|
- `readConfigLayer(filePath)`: Same parse as `readConfigFile`, but isolates `INVALID_JSONC` to `{ config: {}, error }` so plugin/MCP/agent readers can skip one broken layer without aborting valid siblings. Writes still refuse to overwrite the broken file.
|
|
- `writeConfig(config, filePath)`: Writes config with automatic backup. Refuses to overwrite an existing non-empty file that fails the same JSONC parse check.
|
|
- `getJsonEntrySource(layers, sectionKey, entryName)`: Resolves which config layer provides an entry. A failed custom or user layer throws `INVALID_JSONC` instead of treating that file as empty. A failed project layer is skipped so a valid user/custom entry can still be found.
|
|
- `getJsonWriteTarget(layers, preferredScope)`: Determines write target for config updates. Throws `INVALID_JSONC` when the chosen target file is the unparseable layer.
|
|
- `getAncestors(startDir, stopDir)`, `findWorktreeRoot(startDir)`: Git worktree helpers.
|
|
- `isPromptFileReference(value)`, `resolvePromptFilePath(reference)`, `writePromptFile(filePath, content)`: Prompt file reference handling.
|
|
- `walkSkillMdFiles(rootDir)`: Recursively finds all SKILL.md files.
|
|
- `addSkillFromMdFile(skillsMap, skillMdPath, scope, source)`: Parses and indexes a skill file.
|
|
- `resolveSkillSearchDirectories(workingDirectory)`: Returns skill search path order (config, project, home, custom).
|
|
- `listSkillSupportingFiles(skillDir)`, `readSkillSupportingFile(skillDir, relativePath)`, `writeSkillSupportingFile(skillDir, relativePath, content)`, `deleteSkillSupportingFile(skillDir, relativePath)`: Skill supporting file management.
|
|
|
|
## Public exports (routes.js)
|
|
- `registerOpenCodeRoutes(app, dependencies)`: Registers OpenCode-owned HTTP routes and internal module runtime:
|
|
- `GET /api/config/settings`
|
|
- `PUT /api/config/settings`
|
|
- `GET /api/config/opencode-resolution`
|
|
- `POST /api/opencode/upgrade` (enforces the active runtime's upgrade capability, serializes supported OpenCode upgrades, then restarts managed OpenCode so the new binary is active)
|
|
- `GET /api/opencode/upgrade-status` (returns version availability plus the authoritative `upgrade.supported`, `upgrade.manager`, and `upgrade.reason` capability)
|
|
- `POST /api/opencode/directory` (validates and activates an existing project directory; `{ create: true }` explicitly creates the requested project directory before activation, including outside the previously active workspace)
|
|
- `GET /api/provider/:providerId/source`
|
|
- `PUT /api/provider` (create/update custom OpenAI-compatible provider config in OpenCode user/project/custom layers via `scope`; secrets stay in auth via the OpenCode auth API)
|
|
- `DELETE /api/provider/:providerId/auth`
|
|
- Owns lazy auth library loading for provider auth checks/removal.
|
|
- Keeps route behavior independent from composition root; `index.js` now supplies dependencies only.
|
|
|
|
## Public exports (session-runtime.js)
|
|
- `createSessionRuntime({ writeSseEvent, getNotificationClients, broadcastEvent? })`: creates runtime-owned state machine and APIs for session status.
|
|
- Returned API:
|
|
- `processOpenCodeSsePayload(payload)`
|
|
- `getSessionActivitySnapshot()`
|
|
- `getActiveSessionCount()`
|
|
- `getSessionStateSnapshot()`
|
|
- `getSessionAttentionSnapshot()`
|
|
- `getSessionState(sessionId)`
|
|
- `getSessionAttentionState(sessionId)`
|
|
- `markSessionViewed(sessionId, clientId)`
|
|
- `markSessionUnviewed(sessionId, clientId)`
|
|
- `markUserMessageSent(sessionId)`
|
|
- `resetAllSessionActivityToIdle()`
|
|
- `interruptBusySessionsAfterRestart()`: settles every session whose authoritative status is `busy`/`retry` or whose activity phase is still busy, broadcasts `openchamber:session-status` idle plus an OpenCode-shaped `session.error`, resets leftover activity/cooldowns, and returns the interrupted session IDs in stable order.
|
|
- `dispose()`
|
|
|
|
The runtime maintains active-session count incrementally from idempotent activity phase transitions. Upstream stall-timeout and lifecycle health checks read it in O(1); the hourly cleanup removes activity phases older than 24 hours without broadcasting synthetic state transitions. Snapshot generation remains reserved for the session-activity API.
|
|
|
|
## Public exports (lifecycle.js)
|
|
- `createOpenCodeLifecycleRuntime(dependencies)`: creates lifecycle runtime for managed/external OpenCode process orchestration. The optional `onOpenCodeRestarted` dependency (default `null`) is fired after a successful managed restart. `index.js` rebinds event-stream readers to the possibly-new port (#2638), then calls `interruptBusySessionsAfterRestart()` and broadcasts one `opencode-restart-interrupted` UI notification when interrupted turns exist (#2943).
|
|
- Returned API:
|
|
- `startOpenCode()`
|
|
- `restartOpenCode()`
|
|
- `waitForOpenCodeReady(timeoutMs?, intervalMs?)`
|
|
- `waitForAgentPresence(agentName, timeoutMs?, intervalMs?)`
|
|
- `refreshOpenCodeAfterConfigChange(reason, options?)`
|
|
- `bootstrapOpenCodeAtStartup()`
|
|
- `startHealthMonitoring(healthCheckIntervalMs)`
|
|
- `waitForPortRelease(port, timeoutMs, hostname?)`
|
|
- `killProcessOnPort(port)`
|
|
|
|
Managed OpenCode launch also merges the environment returned by the agent-tool
|
|
runtime, the opt-in system prompt optimizer, and the always-on MCP reconnect
|
|
plugin, each appending its `file://` entry to the previous one's config. PATH and `OPENCODE_SERVER_PASSWORD` remain lifecycle-owned and cannot
|
|
be replaced by injected values. External OpenCode processes receive no
|
|
OpenChamber tool injection. Managed launch env strips AppImage `ARGV0` before
|
|
spawn so zsh-backed OpenCode tools do not rewrite child argv[0] to the AppImage
|
|
path (#2588).
|
|
|
|
Before spawn, `applyProviderEnvAliases` fills unset Google credential aliases
|
|
from any present sibling (`GOOGLE_GENERATIVE_AI_API_KEY`, `GOOGLE_API_KEY`,
|
|
`GEMINI_API_KEY`) so a shell that only exports `GEMINI_API_KEY` still satisfies
|
|
the Generative AI SDK path used at chat time. Existing non-empty values are
|
|
never overwritten.
|
|
|
|
Set `OPENCHAMBER_STARTUP_PERF=1` to emit bounded startup phase records for server listen, managed OpenCode preparation/readiness, and proxy readiness holds. Every OpenCode bootstrap emits one terminal `opencode.bootstrap.ready` or `opencode.bootstrap.error` event, including reused and external server paths. Records contain controlled phase/outcome/route labels and timing values only; they never contain request URLs, runtime keys, directories, session IDs, credentials, or content.
|
|
|
|
macOS `say` voice enumeration starts concurrently with server composition. The server listener and managed OpenCode startup do not wait for it; `/api/tts/say/status` awaits the same authoritative capability promise when queried before enumeration completes.
|
|
|
|
Transport-triggered health checks share the periodic monitor's failure accounting interval. Rapid WS reconnect callbacks therefore cannot exhaust the managed-process restart threshold using one cached unhealthy result; an exited managed process still restarts immediately.
|
|
|
|
Managed health failures are classified as `timeout`, `connection_refused`, `connection_reset`, `invalid_response`, or `error`. The lifecycle retains the latest counted failure with a bounded detail string and source. Managed process wrappers continue capturing a sanitized, bounded stderr tail after readiness and retain exit code/signal. Before replacing a managed process, lifecycle snapshots the reason, latest health failure, process diagnostics/aliveness, busy-session count, and timestamp into `lastOpenCodeRestartDiagnostics`; successful startup does not clear this snapshot, and `/health` exposes it for post-restart diagnosis without process environment or credentials.
|
|
|
|
## Public exports (env-runtime.js)
|
|
- `createOpenCodeEnvRuntime(dependencies)`: creates runtime that owns OpenCode CLI environment and binary discovery state.
|
|
- OpenCode CLI resolution order is persisted settings, environment overrides, bundled Desktop CLI when available, PATH, known install locations, then platform shell discovery.
|
|
- Returned API:
|
|
- `applyLoginShellEnvSnapshot()`
|
|
- `getLoginShellEnvSnapshot()`
|
|
- `ensureOpencodeCliEnv()`
|
|
- `applyOpencodeBinaryFromSettings()`
|
|
- `resolveOpencodeCliPath()`
|
|
- `resolveManagedOpenCodeLaunchSpec(opencodePath)`: resolves the effective managed OpenCode launch target, unwrapping Windows package-manager shims to a direct native binary or explicit runtime+script when possible.
|
|
- `resolveGitBinaryForSpawn()`
|
|
- `resolveWslExecutablePath()`
|
|
- `buildWslExecArgs(execArgs, distroOverride?)`
|
|
- `isExecutable(filePath)`
|
|
- `searchPathFor(binaryName, searchPath?)`: resolves an executable from the supplied PATH value, defaulting to the process PATH.
|
|
- `clearResolvedOpenCodeBinary()`
|
|
|
|
## Public exports (env-config.js)
|
|
- `resolveOpenCodeEnvConfig(options?)`: resolves and validates OpenCode host/port/hostname environment configuration.
|
|
- Returned object fields:
|
|
- `configuredOpenCodePort`
|
|
- `configuredOpenCodeHost`
|
|
- `effectivePort`
|
|
- `configuredOpenCodeHostname`
|
|
|
|
## Public exports (hmr-state-runtime.js)
|
|
- `createHmrStateRuntime(dependencies)`: creates runtime for HMR state container initialization and runtime<->HMR state synchronization.
|
|
- Returned API:
|
|
- `getOrCreateHmrState()`
|
|
- `ensureUserProvidedOpenCodePassword(hmrState)`
|
|
- `getUserProvidedOpenCodePassword(hmrState)`
|
|
- `resolveOpenCodeAuthFromState({ hmrState, userProvidedOpenCodePassword })`
|
|
- `syncStateFromRuntime(hmrState, runtime)`
|
|
- `restoreRuntimeFromState({ hmrState, userProvidedOpenCodePassword })`
|
|
|
|
## Public exports (bootstrap-runtime.js)
|
|
- `createBootstrapRuntime(dependencies)`: creates runtime for base app route bootstrap and UI auth controller initialization.
|
|
- Returned API:
|
|
- `setupBaseRoutes(app, options)`
|
|
|
|
## Public exports (network-runtime.js)
|
|
- `createOpenCodeNetworkRuntime(dependencies)`: creates runtime for OpenCode network and URL concerns.
|
|
- Returned API:
|
|
- `waitForReady(url, timeoutMs?)`
|
|
- `normalizeApiPrefix(prefix)`
|
|
- `setDetectedOpenCodeApiPrefix()`
|
|
- `buildOpenCodeUrl(path, prefixOverride?)`
|
|
- `ensureOpenCodeApiPrefix()`
|
|
- `scheduleOpenCodeApiDetection()`
|
|
|
|
## Public exports (settings-runtime.js)
|
|
- `createSettingsRuntime(dependencies)`: creates settings lifecycle runtime for read/migrate/persist concerns.
|
|
- Returned API:
|
|
- `readSettingsFromDisk()`
|
|
- `readSettingsFromDiskMigrated()`
|
|
- `writeSettingsToDisk(settings)`
|
|
- `persistSettings(changes)`
|
|
- Persistent permission auto-accept policy is stored under `permissionAutoAccept`; execution ownership lives in `lib/permission-auto-accept/`.
|
|
- Queued follow-up messages live in `<data-dir>/message-queue.json`, not in settings; execution ownership lives in `lib/message-queue/`.
|
|
- Shared sidebar preferences are stored as validated top-level fields: `sidebarProjectDisplayMode`, `sidebarSessionGroupingMode`, `sidebarProjectSortOrder`, and `sidebarShowRecentSection`. Device-local picker selection and sticky-header state do not enter `settings.json`.
|
|
|
|
## Public exports (settings-helpers.js)
|
|
- `createSettingsHelpers(dependencies)`: creates settings helper runtime for settings request/response shaping.
|
|
- Returned API:
|
|
- `normalizePwaAppName(value, fallback?)`
|
|
- `sanitizeSettingsUpdate(payload)`
|
|
- `mergePersistedSettings(current, changes)`
|
|
- `formatSettingsResponse(settings)`
|
|
|
|
## Public exports (settings-normalization-runtime.js)
|
|
- `createSettingsNormalizationRuntime(dependencies)`: creates normalization/sanitization runtime for shared settings and tunnel helper logic.
|
|
- Returned API:
|
|
- `normalizeDirectoryPath(value)`
|
|
- `normalizePathForPersistence(value)`
|
|
- `normalizeSettingsPaths(input)`
|
|
- `normalizeTunnelBootstrapTtlMs(value)`
|
|
- `normalizeTunnelSessionTtlMs(value)`
|
|
- `normalizeManagedRemoteTunnelHostname(value)`
|
|
- `normalizeManagedRemoteTunnelPresets(value)`
|
|
- `normalizeManagedRemoteTunnelPresetTokens(value)`
|
|
- `isUnsafeSkillRelativePath(value)`
|
|
- `sanitizeTypographySizesPartial(input)`
|
|
- `normalizeStringArray(input)`
|
|
- `sanitizeModelRefs(input, limit)`
|
|
- `sanitizeSkillCatalogs(input)`
|
|
- `sanitizeProjects(input)`
|
|
|
|
## Public exports (theme-runtime.js)
|
|
- `createThemeRuntime(dependencies)`: creates custom theme runtime for on-disk theme discovery and JSON normalization/validation.
|
|
- Returned API:
|
|
- `normalizeThemeJson(raw)`
|
|
- `readCustomThemesFromDisk()`
|
|
|
|
## Public exports (project-directory-runtime.js)
|
|
- `createProjectDirectoryRuntime(dependencies)`: creates runtime for request/project directory candidate normalization and validation.
|
|
- Returned API:
|
|
- `resolveDirectoryCandidate(value)`
|
|
- `validateDirectoryPath(candidate)`
|
|
- `resolveProjectDirectory(req)`
|
|
- `resolveOptionalProjectDirectory(req)`
|
|
|
|
## Public exports (config-entity-routes.js)
|
|
- `registerConfigEntityRoutes(app, dependencies)`: registers configuration entity routes:
|
|
- Agents: `/api/config/agents/:name` and `/api/config/agents/:name/config`
|
|
- Commands: `/api/config/commands/:name`
|
|
- MCP servers: `/api/config/mcp` and `/api/config/mcp/:name`
|
|
- Snippets: `/api/config/snippets`, `/api/config/snippets/:name`, and `/api/config/snippets/expand`
|
|
- Agent/command/MCP write routes persist config to disk and return a deferred-restart payload (`requiresReload: false`, `requiresRestart: true`, `restartDeferred: true`) instead of restarting OpenCode immediately. The UI accumulates these changes and applies them with `POST /api/config/reload`.
|
|
|
|
## Public exports (config-mutation-response.js)
|
|
- `buildDeferredRestartResponse(message)`: success payload for config mutations that are saved on disk but waiting for an explicit Apply & Restart (`restartDeferred: true`).
|
|
- `buildExternalManualRestartResponse(message)`: success payload when OpenCode is an external process and the operator must restart it manually (`requiresManualRestart: true`).
|
|
|
|
## Public exports (auth-state-runtime.js)
|
|
- `createOpenCodeAuthStateRuntime(dependencies)`: creates runtime for managed OpenCode auth password state and request headers.
|
|
- Returned API:
|
|
- `getOpenCodeAuthHeaders()`
|
|
- `isOpenCodeConnectionSecure()`
|
|
- `ensureLocalOpenCodeServerPassword(options?)`
|
|
|
|
## Public exports (core-routes.js)
|
|
- `registerServerStatusRoutes(app, dependencies)`: registers status/system endpoints:
|
|
- `GET /health`
|
|
- `POST /api/system/shutdown`
|
|
- `GET /api/system/info`
|
|
- `registerAuthAndAccessRoutes(app, dependencies)`: registers browser auth/session exchange and API access middleware:
|
|
- `GET /auth/session`
|
|
- `POST /auth/session`
|
|
- `GET /auth/passkey/status`
|
|
- `POST /auth/passkey/authenticate/options`
|
|
- `POST /auth/passkey/authenticate/verify`
|
|
- `POST /auth/passkey/register/options`
|
|
- `POST /auth/passkey/register/verify`
|
|
- `GET /api/passkeys`
|
|
- `DELETE /api/passkeys/:id`
|
|
- `POST /api/auth/reset`
|
|
- `GET /connect`
|
|
- `POST /api/system/probe-url`
|
|
- `app.use('/api', ...)` auth/tunnel guard
|
|
- `registerSettingsUtilityRoutes(app, dependencies)`: registers small settings utility endpoints:
|
|
- `GET /api/config/themes`
|
|
- `POST /api/config/reload` — applies accumulated deferred OpenCode config changes. Managed OpenCode restarts and returns `requiresReload: true`. External OpenCode returns `requiresManualRestart: true` (changes are already on disk; the connected server must be restarted outside OpenChamber).
|
|
- `registerCommonRequestMiddleware(app, dependencies)`: registers shared request middleware stack:
|
|
- conditional JSON body parser behavior for `/api/*` vs non-API requests
|
|
- URL-encoded parser setup
|
|
- request logging middleware
|
|
|
|
## Public exports (cli-options.js)
|
|
- `parseServeCliOptions(options)`: parses serve CLI flags and environment-derived defaults:
|
|
- Port/host/ui-password
|
|
- Tunnel provider/mode/config/token/hostname
|
|
- Legacy `--tunnel` shorthand normalization
|
|
|
|
## Public exports (cli-entry-runtime.js)
|
|
- `runCliEntryIfMain(dependencies)`: detects direct CLI execution and runs server startup with parsed CLI options.
|
|
|
|
## Public exports (server-utils-runtime.js)
|
|
- `createServerUtilsRuntime(dependencies)`: creates server utility runtime for OpenCode orchestration helpers.
|
|
- Returned API:
|
|
- `setOpenCodePort(port)`
|
|
- `waitForOpenCodePort(timeoutMs?)`
|
|
- `buildAugmentedPath()`
|
|
- `parseSseDataPayload(block)`
|
|
- `fetchAgentsSnapshot()`
|
|
- `fetchProvidersSnapshot()`
|
|
- `fetchModelsSnapshot()`
|
|
- `setupProxy(app)`
|
|
|
|
## Public exports (shutdown-runtime.js)
|
|
- `createGracefulShutdownRuntime(dependencies)`: creates graceful shutdown runtime for managed OpenCode and web server teardown sequencing.
|
|
- Returned API:
|
|
- `gracefulShutdown(options?)`
|
|
|
|
## Public exports (server-startup-runtime.js)
|
|
- `createServerStartupRuntime(dependencies)`: creates runtime for server bind/startup tunnel and process handler wiring.
|
|
- Returned API:
|
|
- `resolveBindHost(host)`
|
|
- `startListeningAndMaybeTunnel(options)`
|
|
- `attachProcessHandlers(options)`
|
|
|
|
## Public exports (static-routes-runtime.js)
|
|
- `createStaticRoutesRuntime(dependencies)`: creates runtime for static dist resolution and static route registration.
|
|
- Returned API:
|
|
- `registerStaticRoutes(app)`
|
|
|
|
## Public exports (feature-routes-runtime.js)
|
|
- `createFeatureRoutesRuntime(dependencies)`: creates runtime for main feature route registration orchestration.
|
|
- Returned API:
|
|
- `registerRoutes(app, routeDependencies)`
|
|
|
|
## Public exports (opencode-resolution-runtime.js)
|
|
- `createOpenCodeResolutionRuntime(dependencies)`: creates runtime for OpenCode binary/source snapshot resolution.
|
|
- Returned API:
|
|
- `getOpenCodeResolutionSnapshot(settings)`: returns configured/resolved OpenCode binary details plus effective managed-launch fields (`launchBinary`, `launchArgs`, `launchWrapperType`) when applicable.
|
|
|
|
## Public exports (tunnel-wiring-runtime.js)
|
|
- `createTunnelWiringRuntime(dependencies)`: creates runtime for tunnel service construction and tunnel route registration.
|
|
- Returned API:
|
|
- `initialize(app, initialPort)`
|
|
|
|
## Public exports (startup-pipeline-runtime.js)
|
|
- `createStartupPipelineRuntime(dependencies)`: creates runtime for terminal wiring, proxy/bootstrap scheduling, static route registration, and server startup/listen flow.
|
|
- Returned API:
|
|
- `run(options)`
|
|
|
|
The pipeline binds the OpenChamber listener and publishes its active port
|
|
before starting managed OpenCode. The managed custom tool therefore receives
|
|
an authoritative loopback callback URL even when OpenChamber binds port `0`.
|
|
|
|
## Public exports (openchamber-routes.js)
|
|
- `registerOpenChamberRoutes(app, dependencies)`: registers OpenChamber endpoints:
|
|
- `GET /api/openchamber/update-check`
|
|
- `POST /api/openchamber/update-install`
|
|
- Foreground servers running under a systemd user unit queue installation in
|
|
a separate transient unit and restart the configured service afterwards.
|
|
`OPENCHAMBER_SYSTEMD_UNIT` overrides the default `openchamber.service`.
|
|
- `GET /api/openchamber/models-metadata`
|
|
- `GET /api/zen/models`
|
|
|
|
## Public exports (pwa-manifest-routes.js)
|
|
- `registerPwaManifestRoute(app, dependencies)`: registers PWA manifest endpoint with dynamic app-name resolution and recent-session shortcuts:
|
|
- `GET /manifest.webmanifest`
|
|
|
|
## Public exports (project-icon-routes.js)
|
|
- `registerProjectIconRoutes(app, dependencies)`: registers project icon routes and owns icon storage/discovery flow:
|
|
- `GET /api/projects/:projectId/icon`
|
|
- `PUT /api/projects/:projectId/icon`
|
|
- `DELETE /api/projects/:projectId/icon`
|
|
- `POST /api/projects/:projectId/icon/discover`
|
|
|
|
## Public exports (skill-routes.js)
|
|
- `registerSkillRoutes(app, dependencies)`: registers skills-related routes:
|
|
- Skills config CRUD and metadata under `/api/config/skills*`
|
|
- Skill rename via `PATCH /api/config/skills/:name` with `{ renameTo }` (directory rename preserves `SKILL.md` body and supporting files; restricted to managed skill roots under `.opencode/skills|skill`, `.claude/skills`, and `.agents/skills`)
|
|
- Skill list responses include authoritative `renamable` derived from the same managed-root policy used by rename
|
|
- Skills catalog listing/source pagination, scan, and install routes
|
|
- Supporting skill file read/write/delete routes
|
|
- Directory resolution prefers an explicit request directory, then soft-falls
|
|
back to the active project / `lastDirectory` so repository-local
|
|
`.agents/skills` and `.opencode/skills` remain discoverable when the client
|
|
omits `directory`. Requests without any project still list user-scoped skills.
|
|
|
|
## Public exports (proxy.js)
|
|
- `registerOpenCodeProxy(app, dependencies)`: registers OpenCode proxy routes and middleware.
|
|
- Owns:
|
|
- SSE forwarders: `GET /api/global/event`, `GET /api/event`
|
|
- Downstream heartbeats keep clients and intermediaries alive, while a separate upstream-only stall watchdog closes the downstream response when OpenCode stops producing bytes so clients reconnect instead of trusting synthetic heartbeats indefinitely. Each watchdog reset uses the current load-aware timeout, matching the shared event transport.
|
|
- Session message forwarder: `POST /api/session/:sessionId/message`
|
|
- Interactive OAuth forwarder: `POST /api/provider/:providerID/oauth/callback`
|
|
- Upstream blocks inside this call for the whole browser sign-in (device-code polling or a loopback redirect), so it is exempt from the ordinary request deadline and uses a 15-minute proxy timeout instead of `LONG_REQUEST_TIMEOUT_MS`. All other `/api/provider/*` routes, including `oauth/authorize`, keep the ordinary deadline.
|
|
- Generic `/api/*` forwarding with hop-by-hop header filtering
|
|
- Windows `/session` merge fallback path behavior
|
|
- OpenCode readiness gate for proxied `/api` requests
|
|
|
|
## Public exports (watcher.js)
|
|
- `createOpenCodeWatcherRuntime(dependencies)`: creates global event watcher runtime backed by the shared upstream SSE reader.
|
|
- Returned API:
|
|
- `start()`
|
|
- `stop()`
|
|
- Behavior:
|
|
- Waits for OpenCode readiness before attaching the watcher.
|
|
- In production wiring, subscribes to the shared global message-stream hub instead of opening its own `/global/event` connection.
|
|
- Can still create its own `/global/event` reader when no shared hub is provided, which keeps module tests and isolated reuse simple.
|
|
- Reuses event-stream parsing, `Last-Event-ID`, stall timeout, and reconnect behavior.
|
|
- Forwards unwrapped global event payloads into notification/session side effects.
|
|
|
|
## Storage and configuration
|
|
- Provider auth: `~/.local/share/opencode/auth.json`.
|
|
- User config: `~/.config/opencode/opencode.json`.
|
|
- Project config: `<workingDirectory>/.opencode/opencode.json` or `opencode.json`.
|
|
- Custom config: `OPENCODE_CONFIG` env var path.
|
|
- Rate limit config: `OPENCHAMBER_RATE_LIMIT_MAX_ATTEMPTS`, `OPENCHAMBER_RATE_LIMIT_NO_IP_MAX_ATTEMPTS` env vars.
|
|
|
|
## Notes for contributors
|
|
- This module serves as foundation for OpenCode-related server utilities.
|
|
- Route ownership moved to module-level `routes.js`; `index.js` wires dependencies only.
|
|
- All file writes include automatic backup before modification.
|
|
- Config merging follows priority: custom > project > user.
|
|
- UI auth uses scrypt for password hashing with constant-time comparison.
|
|
- Tunnel auth treats `host.docker.internal` as local-only when the socket remote IP is private/loopback.
|