Files
quiz152 95338dbbb1 test(cli): deterministic torn-write regression coverage; document module
Address the openchamber-ai review's non-blocking notes:

- Concurrency evidence: the torn-write test now injects a slow, chunked
  writeFile (one open handle, file grows prefix->full) so a torn read is
  deterministically observable in the 30ms window. A companion test runs the
  naive direct writer under the same load and asserts torn reads ARE produced,
  proving the atomicity test can actually fail on the pre-fix writer.
- Windows fallback comment: no longer claims the copyFile fallback is atomic;
  it is called out as a last resort confined to Windows.
- Module map: document cli-settings-accessors.js in bin/lib/DOCUMENTATION.md.
2026-08-09 11:39:42 +08:00

9.0 KiB

CLI Module Map

This directory contains the non-entrypoint implementation for the OpenChamber CLI. packages/web/bin/cli.js should stay thin: it owns bootstrap, command wiring, top-level dispatch, signal/cancel handling, and compatibility exports. Domain logic belongs in these modules.

Entrypoint Boundary

  • ../cli.js
    • Owns process bootstrap, package/version lookup, command table wiring, signal handlers, top-level error handling, and legacy exports used by tests or external consumers.
    • Injects runtime dependencies into command factories, such as serveCommand, stopCommand, package-manager loading, cancel cleanup, and foreground server state setters.
    • Should not grow command-specific behavior. If a new branch needs more than dispatch/wiring, move it here into a command or helper module instead.

Command Modules

Command modules implement user-facing commands and preserve output contracts across interactive, non-TTY, --quiet, and --json modes. They should use ../cli-output.js for presentation helpers and keep safety validation in command logic, not prompts.

  • commands-serve.js

    • Implements openchamber serve.
    • Owns OpenCode CLI checks, port resolution, log rotation, PID/instance registry writes, foreground/background server launch, startup summaries, and foreground shutdown behavior.
  • commands-lifecycle.js

    • Implements openchamber stop and openchamber restart.
    • Owns lifecycle stop/restart semantics, desktop-managed port rejection, unmanaged instance shutdown attempts, PID/instance cleanup, and restart reuse of stored instance options.
  • commands-status.js

    • Implements openchamber status.
    • Formats discovered instances and tunnel readiness/status for human, quiet, and JSON output.
  • commands-session.js

    • Implements openchamber session create, send, fork, list, status, and messages.
    • Maps CLI options to shared control-service inputs and owns only human, quiet, and JSON presentation.
    • Message projection matches Export Markdown semantics: only ordered text parts are exposed; tool, reasoning, file, and other parts are omitted.
    • The server control service owns create/worktree/prompt orchestration, official OpenCode reads, Goal Mode, wait semantics, and partial failures.
  • commands-schedule.js

    • Implements scheduled task status/list/create/run/delete/enable/disable.
    • Maps options to control-service inputs and renders results; project resolution, validation, persistence, and execution remain server-owned.
  • commands-models.js

    • Prints OpenChamber default, favorite, and recent model settings.
  • commands-projects.js

    • Prints configured project labels, ids, and directories for later control-plane calls.
  • commands-logs.js

    • Implements openchamber logs.
    • Resolves log files, tails recent lines, and follows log output.
  • commands-startup.js

    • Implements openchamber startup.
    • Handles startup subcommand dispatch and presentation around the lower-level startup service helpers.
  • commands-connect-url.js

    • Implements openchamber connect-url.
    • Finds or starts a local instance and prints the browser/connect URL according to the selected output mode.
    • Emits a pairing v2 link (openchamber://connect?v=2&p=<base64url>): it creates a one-time pairing session in the shared store (client-pairing-sessions.json) and encodes the pairing id + secret + transport candidates. The client redeems the secret over whichever candidate connects first (/api/client-auth/pairing/redeem). No standalone token is embedded — the QR itself is the single-use credential.
    • The default form advertises the resolved server URL as a direct (lan/tunnel) candidate and folds in a relay candidate when the host relay is enabled, so one link works on-LAN and off-network.
    • --relay builds a relay-only pairing link (the sole candidate is the relay transport), for sharing with a device that is not on the host's network — no server URL, no auto-start. The relay endpoint follows OPENCHAMBER_RELAY_URL / the stored setting / the default, matching the running host; the host must be running with the relay enabled to serve the redeem over the tunnel.
  • commands-update.js

    • Implements openchamber update.
    • Loads the package-manager helper, performs update flow, and coordinates restart behavior after updates.
  • commands-tunnel.js

    • Implements openchamber tunnel and its subcommands: profile, providers, ready, doctor, status, start, stop, and completion.
    • Owns tunnel-specific command flow, interactive prompt decisions, managed-local/managed-remote startup, QR display rules, tunnel start/stop API calls, and tunnel profile command handling.
    • Receives serveCommand and stopCommand by dependency injection. Do not reach back into cli.js command globals from this module.

Shared Helper Modules

These modules hold reusable, non-presentational logic for commands.

  • cli-args.js

    • Argument parsing, defaults, help text, completion script generation, and typo suggestions.
  • cli-errors.js

    • CLI exit codes and typed tunnel CLI errors.
  • cli-paths.js

    • Data, run, log, settings, tunnel profile, and managed-local config paths.
  • cli-settings-accessors.js

    • Minimal settings.json read/write for CLI contexts that must not load the full web settings runtime (connect-url relay identity resolution).
    • Mirrors the settings runtime's guarantees so a CLI read-modify-write can never corrupt shared state: atomic tmp+rename writes (no concurrent reader in the running app can observe a torn file), a strict read that throws on corrupt/unreadable payloads, and the same 0600 file mode.
    • The strict read gates relay identity regeneration exactly like the server runtime: a swallowed read failure can never mint a replacement signing or encryption keypair, which would change serverId and orphan every paired device and push binding.
  • cli-process.js

    • PID files, instance registry files, process identity checks, runtime metadata checks, and process termination helpers.
  • cli-lifecycle.js

    • Instance discovery, live health probing, attachability checks, provider discovery, and status aggregation used by lifecycle/status/tunnel commands.
  • cli-http.js

    • HTTP helpers for health checks, shutdown requests, JSON API calls, tunnel provider fetches, and system info fetches.
    • Owns local desktop bearer auth and managed CLI-instance UI password retry for control-plane requests.
  • cli-control.js

    • Sends one typed action request to the authenticated OpenChamber control endpoint and maps HTTP failures to CLI exit behavior.
    • Must not reproduce session, scheduled-task, project-resolution, or wait orchestration.
  • cli-api-target.js

    • Resolves the target OpenChamber runtime for control-plane commands, preferring desktop unless a port is explicit.
  • cli-goal.js

    • Owns shared Goal Mode token-budget validation for session and schedule commands.
  • cli-network.js

    • Host resolution, URL building, LAN detection, unsafe browser port validation, and UI password/network exposure checks.
  • cli-ports.js

    • Port availability checks and available-port resolution.
  • cli-log-files.js

    • Log rotation, tail reads, and file-follow streaming.
  • cli-executables.js

    • Executable path resolution and PATH lookup helpers.
  • cli-startup.js

    • Native startup service detection, install/uninstall/status helpers, and platform-specific startup command execution.
  • cli-tunnel-profiles.js

    • Tunnel profile normalization, token resolution/redaction, profile storage, migration, file-permission warnings, and managed-remote pair persistence.
  • cli-tunnel-utils.js

    • Tunnel-specific command string builders, TTL parsing/formatting, and replay command helpers.
  • cli-tunnel-capabilities.js

    • Built-in tunnel provider capability fallbacks used when a live server cannot provide tunnel metadata.

Placement Rules

  • Add new CLI commands as commands-*.js modules and wire them from cli.js.
  • Add reusable logic to the narrow helper module that owns the domain. Create a new helper module before mixing unrelated domains into an existing one.
  • Keep command modules responsible for user-visible behavior and mode-specific output. Keep helper modules mostly output-free unless the helper exists specifically for CLI rendering.
  • Preserve output contracts when moving code:
    • --json emits JSON only.
    • --quiet emits concise essential output.
    • Prompts are gated by canPrompt(options).
    • Validation and policy run in every mode.
  • Prefer dependency injection from cli.js for cross-command behavior, especially when one command needs another command's implementation.
  • Do not import cli.js from modules in this directory. The dependency direction is cli.js -> command modules -> helper modules.

Verification

For CLI behavior changes, run the focused CLI suite from packages/web:

bun run test -- bin/cli.test.js

Before finalizing source changes that affect CLI behavior, also run:

bun run type-check
bun run lint