ompchamber

agent
Guvenlik Denetimi
Basarisiz
Health Uyari
  • No license — Repository has no license file
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Community trust — 10 GitHub stars
Code Basarisiz
  • rm -rf — Recursive force deletion command in .github/bot/pr-review.yml
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Browser console and diagnostic chamber for Oh-My-Pi (omp) agent sessions — live timeline, steering, real PTY, git/diff/wiki panels, and a panel-plugin platform. Elysia + Preact on Bun.

README.md

OMPChamber

OMPChamber

Browser console and diagnostic chamber for Oh-My-Pi (omp) agent sessions.
Watch, steer and audit autonomous coding sessions from one paper-flat dashboard.

npm version Bun ≥ 1.4 Elysia Preact Shiki Version

Architecture · Design · Changelog · Plugins · Issues

OMPChamber — session sidebar, streaming timeline with tool cards, and the Files panel

What it is

The browser view onto a live omp install. It is not a terminal wrapper: the chamber reads
omp's own session JSONL, project registry, config and subagent transcripts, folds them into one
live view, and writes back through omp's RPC mode.

ompchamber serve                # production server + prebuilt client on :3000
bun run dev                     # source checkout: one process, HMR, no build step
  • One process. Elysia serves the API and the client bundle, and the agent event stream is a
    first-class WebSocket route on that same listener (SSE as fallback) — no second server, no proxy,
    no separate client watcher.
  • Two data modes. MOCK=false (the default, also when unset) runs on the real SQLite database
    and your real workspace; MOCK=true swaps in bundled demo datasets. Every screenshot and
    recording on this page was captured in MOCK=true.
  • Bun only. The server imports bun:sqlite and Bun.YAML, the client is bundled by Bun.build,
    and sessions, config and updates shell out to the omp binary.

Feature tour

Feature tour — timeline, Files, Search, Source Control, a real PTY, Context & Telemetry, Wiki, and the theme cycle

Full-quality recording (MP4, 57s): docs/assets/tour.mp4 · theme cycle in motion: docs/assets/themes.gif

Files panel
Files — project tree with git markers, cut/copy/paste, drag-to-move, and a file open in the editor
Search panel
Search — ripgrep-backed streaming results, virtualized list, in-file jump and replace preview
Source control
Source Control — changes, commits, branches, tracking, stash, and merge/rebase conflict resolution
Editor and diff
Editor & diff — split or unified, full-code or diff-only, word wrap, find/replace, command palette
Terminal
Terminal — a real PTY (one shell per terminal id, scrollback replayed on reload)
Context and telemetry
Context & telemetry — context window use, cache hit rate, cost, per-message breakdown
Wiki
Wiki — reads a project's GitHub/GitLab wiki over git, with its own _Sidebar as navigation
Todos
Todos — the session's live list, all five omp statuses, blockers and per-phase progress
Plan
Plan — the plan artifact an omp session is working from, rendered as a document
Usage
Usage — provider balance, plan windows, saved resets, per-model token spend
Your browser
Browser (yours) — a private iframe with real history and device presets
Agent browser
Browser agent — a live screencast of the tab the agent is driving
Panel plugins
Panel plugins — every view is a plugin, including the eleven bundled ones
Providers
Providers — dialects, discovery, hand-added models, credential removal
Mobile view
Mobile view — the same session on a phone: chat, sessions drawer, and the panel drawer
Light theme
Any palette — 45 light and dark themes, applied to the whole console
  • Chat timeline — streaming thinking blocks (with omp's narrated prose promoted to answer
    text), a structured renderer per tool (bash, LSP, AST edit, proc:// services, wait with a
    clock and progress, mermaid diagrams you can zoom), usage footers, a persistent follow-up queue,
    a jump rail that reaches any user turn, drag-and-drop attachments, one-shot auto-titling, and
    in-place undo that rewinds a session by truncating its JSONL behind a confirmation step.
  • Steering, not cancelling — a mid-run prompt is delivered INTO the running turn
    (POST /api/agent/:id {type:'steer'}); the answer you were watching survives. A subagent can be
    steered or stopped on its own, from the roster or its transcript.
  • Side questions (/btw [question]) — replaces the composer with a panel that answers from a
    private copy of the session file, so the parent transcript is never written; a topic can be
    promoted into a session of its own, and it runs the same tool cards, thinking and usage rows as
    the chat.
  • Workspace sidebar — folder → session tree off omp's project registry, with search, sort,
    archive, per-session stream status, subagent rows, scheduled tasks, and a New Workspace dialog
    that can register a directory or clone a repository.
  • Eleven right-panel views — context & telemetry, files, search, source control, wiki,
    terminal, your browser, the agent browser, usage, todos and plan; each remembers its own width
    per session, and the bar is drag-reorderable.
  • A plugin platform, not a fixed panel list — a panel plugin can ship a Preact view, a server
    half, its own storage, RPC methods, a realtime topic and declared settings. Three are bundled
    (Runner — 18 command sources across 15 toolchains — plus Stats and Session Info),
    and they are auto-synced on upgrade.
  • Full omp settings surface — engine config keys (generated from omp's own schema, 554
    settings across 10 tabs), providers, agents, AGENTS.md / RULES.md, slash commands, MCP
    servers with live connection tests, skills and the skills catalog, token usage, notifications,
    and dictation.
  • Self-update — ompchamber update installs the latest version from npm, and About → Updates
    does the same from the console, restarting the server and reloading the page into the new build.
  • Opt-in password, HTTPS and session revocation — the UI is open by default and locked only when
    you ask for it (--ui-password / OMPCHAMBER_UI_PASSWORD), with --tls for a self-signed HTTPS
    listener and POST /api/auth/revoke to end every session at once. See Security.
  • Tray icon and start at login — serve --tray / --startup, with the icon as a child of the
    server so it can never outlive it.
  • Dictation — the composer's mic button runs omp's own speech-to-text worker (no API key, a
    local model) and streams the transcript into the draft.

Themes

The palette picker in Settings → Appearance

Eight of the 45 palettes, same surface

45 palettes in one catalog (src/shared/lib/theme/): the chamber's own three (paper, contrast,
one-dark-pro-soft) and 21 light/dark pairs — Catppuccin, Gruvbox, Tokyo Night, Nord, Dracula,
Solarized, Kanagawa, Vesper, Ayu, Monokai and more. Switching repaints everything, syntax
highlighting and Mermaid diagrams included, through one writer (applyDocumentTheme) that sets
data-theme and announces it — so an open editor, a live terminal and a diagram in the transcript
all follow with no reload.

Theme cycle in motion

Install

OMPChamber is published to npm and runs on Bun, not Node:

Bun only — the server imports bun:sqlite and Bun.YAML, neither of which Node can load.
Bun 1.4 or newer is required.

omp required — every live capability (agent sessions, omp config, session state, updates)
shells out to the omp binary, so serve refuses to start when it is not on PATH (or set via
OMPCHAMBER_OMP_BIN). MOCK=true is the only mode that runs without a real omp install.

bun add -g ompchamber     # puts the `ompchamber` command on your PATH
ompchamber serve         # start the server on :3000

The published tarball carries the source, the CLI and the prebuilt client bundle, so there is no
build step after install. npm install -g ompchamber works too — the ompchamber bin is a Bun
script, so Bun still has to be on PATH.

From source

git clone https://github.com/rajebdev/ompchamber.git
cd ompchamber
bun install
bun run build                        # dist/client — the only build artifact

bun run start                        # NODE_ENV=production bun run src/server/index.ts
bun link                             # then: ompchamber status

Without dist/client the server answers 503 with the exact command to run — never a bare 500.

Updating

ompchamber update --check        # report only: current version, latest release
ompchamber update                # install it, then restart a running instance
ompchamber update --force        # reinstall even when already up to date

A bun global install is refreshed with bun add -g ompchamber@<version>; a git checkout is
fast-forwarded to the release tag and rebuilt. Uncommitted work is never merged over (the update asks
you to commit or stash first), and an install owned by another package manager is left alone and told
which command to run.

A server OMPChamber did not start is never restarted: bun run dev, bun run start, a manual
bun src/server/index.ts and instances owned by a supervisor (systemd, pm2, a container) all record
themselves as direct, so the update leaves them running and says so. --no-restart turns the
restart off entirely. The console's About → Updates runs the same install, restarts the instance
serving that console, and reloads the page once the replacement answers on the same port.

CLI

ompchamber [COMMAND] [OPTIONS] — serve is the default command.

Command Purpose
serve Start the web server (daemon by default)
update Install the latest version from npm, then restart the instance it started
stop Stop the instances the CLI started — --port/--all to stop any
restart Stop, then start again — leaving servers started from source alone
status Report whether an instance is running
logs Print or follow the server log
plugin Build, type, scaffold or list panel plugins
Option Purpose
-p, --port <port> Web server port (default 3000); scopes status/stop/restart/logs
--host <address> (--hostname) Bind address (default 127.0.0.1)
--lan Bind to 0.0.0.0 for LAN access (warns when the traffic is unencrypted)
--tls Serve HTTPS from a self-signed certificate, generated once into ~/.ompchamber/tls/
--tray Show a menu-bar/notification-area icon for this server
--startup Start the server at login (and show the tray icon now)
--no-startup Remove the login entry
--ui-password [value] Require a password for the browser UI; generates one when the value is omitted
--no-ui-password Ignore OMPCHAMBER_UI_PASSWORD for this start
--prod Accepted for compatibility — production is the default
--dev Run the dev server from source (HMR, unminified) instead
--foreground (--no-daemon) Run in the foreground (no daemon)
--all Apply the command to every running instance
-c, --check Report whether a newer release exists without installing it
--force Reinstall even when already up to date
--no-restart Do not restart a running instance after updating
-f, --follow / -n, --lines <count> Tail the log, optionally from a line count
--json / -q, --quiet Machine-readable output / suppress non-essential output

Tray icon and start at login

ompchamber serve --tray puts an OMPChamber icon in the menu bar (macOS) or the notification area
(Windows, Linux). --startup additionally registers the server to start at every login, and
--no-startup removes that registration:

ompchamber serve --tray          # start the server with a tray icon
ompchamber serve --startup       # also start at every login
ompchamber serve --no-startup    # remove the login entry

The icon's menu reflects the server as it is right now — rebuilt on every open: Open Console,
Open Logs, Start at Login / Disable Start at Login, Quit OMPChamber.

  • The tray is a child of the server. It exits when the server does (it watches its parent), so a
    killed or crashed server leaves no orphan icon, and Quit OMPChamber signals the server — the
    icon and the console end together. There is no pid file, no status/stop for the icon, and
    nothing to keep in sync: the process tree is the registration.
  • The login entry runs serve --foreground. launchd reaps a job's whole process tree when the
    job's program exits, so an entry that ran a detached serve (which returns once the health check
    passes) produced no server at all. In the foreground the CLI is the long-lived job, the server is
    its child, and the tray is the server's child. Verified against launchctl print: the entry is
    state = running with the CLI as its program.
  • macOS registers ~/Library/LaunchAgents/dev.ompchamber.tray.plist (RunAtLoad, no
    KeepAlive — a server stopped on purpose stays stopped). Linux writes
    $XDG_CONFIG_HOME/autostart/ompchamber-tray.desktop, which every freedesktop session reads.
    Windows sets the HKCU …\CurrentVersion\Run value through reg, so registering never needs
    elevation.
  • What draws the icon is the platform's own tray API: an AppKit status item on macOS (via
    osascript -l JavaScript), System.Windows.Forms.NotifyIcon on Windows, and yad/zenity
    --notification on Linux. Only Linux can lack a client — the icon is then skipped with the
    package to install in the log, and the server keeps running.
  • --startup without --tray is a supported shape: the login-started server comes up without an
    icon.

Ports and multiple instances

One port is served by exactly one process, and bun run dev, ompchamber serve and the production
server all default to 3000. A starting instance never stops another one — if the port is taken
by OMPChamber, the newcomer reports who holds it and exits non-zero so you can pick another port:

ompchamber serve --port 3001        # CLI flag
OMPCHAMBER_PORT=3001 ompchamber serve
PORT=3001 bun run dev              # or: PORT=3001 bun run start
  • Nothing is ever signalled to free a port; ompchamber stop --port 3000 is the deliberate way.
  • A process that is not OMPChamber is never signalled either: the server names the port and
    prints the lsof -nP -iTCP:<port> -sTCP:LISTEN command to identify it.
  • bun run dev hot reload is unaffected: --hot re-evaluates the entry inside the running
    process, and a port held by that process is recognized as its own.
  • status and logs report every live instance by default; --port <port> narrows them to one.
    stop acts on the instances the CLI started, and restart/update skip the rest: bun run dev
    is a supervisor whose only job is to mirror the script under it, so stopping that server exits
    the dev loop with it. Name the instance — ompchamber stop --port <port>, or --all — to end
    one anyway.

The server writes ~/.ompchamber/run/<port>.json (pid, host, mode, launchMode, startedAt)
once it owns the port, which is how status/stop/logs also see servers started by bun run dev
or --foreground. launchMode is derived from the process's argv, never from an environment
variable — env is inherited, so a bun run dev started inside an OMPChamber shell would otherwise be
replaced by an update. Records left by a killed server are pruned once their PID stops answering.

  • stop reports what actually happened. The record is dropped only once the process is gone, so a
    stop that could not end the server keeps it — ompchamber status still finds the instance, the
    message says so, and the command exits non-zero. Deleting the record over a live process was how a
    failed stop turned into a deadlock: status said "not running" while serve refused the port.
  • A PID whose identity cannot be read (Windows cannot read another process's command line, so every
    PID there reads back as unverified) is resolved through the port instead: /api/health names
    the instance serving it. Only when the port names a different PID — a recycled PID, a foreign
    listener — is the record treated as stale and cleared without signalling anything.

Configuration

Environment variables, read from .env (see .env.example):

Variable Default Purpose
PORT 3000 Server port
HOST localhost Server bind address
MOCK false true = demo datasets, false = real SQLite + workspace only
SYNC_WORKSPACE true Seed the workspace list from discovered omp projects — only while the list is still empty (one-time bootstrap; a curated list is never appended to)
OMPCHAMBER_DATA_DIR ~/.ompchamber CLI registry, logs, database, wiki mirrors and TLS certificate root
OMPCHAMBER_PORT / OMPCHAMBER_HOST — Defaults for the CLI when no flag is passed
OMPCHAMBER_UI_PASSWORD — Require a UI password (see Security); erased from the environment at boot
OMPCHAMBER_DB_PATH (DB_PATH) ~/.ompchamber/db.sqlite SQLite database path
OMPCHAMBER_BUN — Explicit Bun binary for the CLI to spawn
OMPCHAMBER_OMP_BIN — Explicit omp binary path
OMPCHAMBER_MARKETPLACE_DIR / OMPCHAMBER_BUNDLED_MARKETPLACE_DIR — Override where plugin marketplaces are read from
PI_CONFIG_DIR / PI_CODING_AGENT_DIR ~/.omp Oh-My-Pi config root overrides
SKILLS_API_URL https://skills.sh Skills catalog source
NPM_CONFIG_REGISTRY https://registry.npmjs.org Registry the update check reads

Runtime state, sessions and settings live in SQLite (~/.ompchamber/db.sqlite), the single source
of truth for persisted settings. Per-session UI state (open tabs, panel widths, the picked repo,
drafts) lives beside it, keyed by session id, so a reload restores the layout that session was left
in.

Security

Off by default. A plain bun run dev requires no password, and no file on disk can change that:
the password is supplied per run and hashed in memory. Only a session signing secret is persisted
(~/.ompchamber/auth.json, mode 0600), so bun run --hot does not sign you out on every save while
nothing durable can enable authentication on its own.

ompchamber serve --ui-password my-secret    # or OMPCHAMBER_UI_PASSWORD
ompchamber serve --ui-password              # generates one and prints it once
ompchamber serve --tls --ui-password …      # HTTPS, cookie gains Secure
  • Password — argon2id, verified in constant time. Login is throttled per socket address
    (10 per 5 minutes, then a 15-minute lockout); x-forwarded-for is deliberately not trusted, so a
    forged header cannot buy a fresh budget.
  • Sessions — a stateless HMAC token in a cookie named per port (omp_session_<port>), because
    browsers key a cookie jar by host and ignore the port. HttpOnly, SameSite=Lax, and Secure
    whenever the request arrived over https — including through a proxy that sets
    x-forwarded-proto. Changing the password or calling POST /api/auth/revoke rotates the signing
    secret and ends every session everywhere. The token binds to a key derived from the password, so a
    restart with the same password keeps your session and a different one kills it.
  • TLS — --tls generates a self-signed certificate (SAN covering localhost, 127.0.0.1 and
    the machine's LAN addresses) into ~/.ompchamber/tls/. Your browser warns once, then the
    connection is encrypted. Without it, a password and the session cookie cross the network in
    cleartext, which is what --lan warns about.
  • No TLS locally? A tunnel terminates TLS at the edge and needs no certificate:
    cloudflared tunnel --url http://127.0.0.1:3000. Verified end to end — login, the API gate, SSE
    and the terminal WebSocket all work through the public URL, in dev and in the production build.
  • Local sockets are origin-checked. The terminal PTY and dictation WebSockets verify the
    upgrade's origin with the same predicate, because both trigger real work on this machine.

Bind to loopback (the default) and reach it over SSH port-forwarding, a VPN, or a tunnel. Exposing
the port directly is the option with the least margin for error.

Architecture

src/
├─ client/    Preact UI — zero React packages
│  ├─ components/   layout, workspace, settings, mobile, common
│  ├─ hooks/        chat, workspace, ui, browser, models, settings
│  ├─ data/         mock datasets, samples, themes, model + agent catalogs
│  └─ tailwind.css  theme tokens (--theme-ink, --theme-paper, …)
├─ server/    Bun-only — Elysia routes, SQLite, omp RPC bridge, fs/git/terminal
│  ├─ routes/       one plugin per domain, mounted in routes/index.ts
│  ├─ lib/          omp session/subagent/config, panels, wiki, updates, lifecycle
│  └─ plugins/      SSR shell, static assets, compression, dev assets
├─ shared/    Imported by both — types, chat timeline folding, pure helpers
└─ cli/       ompchamber command (plain ESM, runs under Bun)

packages/
├─ plugin-sdk/  the panel-plugin contract (types, backend API, slots)
└─ ui/          the Preact kit a plugin builds against

marketplace/plugins/
├─ command-runner/  a repository's commands, 18 sources across 15 toolchains
├─ stats/           CPU and RAM of the whole omp stack
└─ session-info/    active workspace and a note per session

Non-obvious invariants worth reading before you edit:

  • src/shared/** must never import a node: builtin or bun:sqlite — it also runs in the
    browser. Server-only helpers belong in src/server/lib/**.
  • Route modules keep the ported Remix shape (loader for GET, action for mutations) so status
    codes stay identical; action owns method dispatch and returns its own 405.
  • Every internal import uses the @/ alias, folders mirror component names in kebab-case, and
    every .ts/.tsx file stays under 350 lines.
  • Every right-panel view is a plugin, including the eleven the chamber ships: one catalog merges
    the built-ins and the installed ones, so a view cannot be a table in one layout and a fetch in the
    other.

AGENTS.md is the normative reference for all of the above — the invariants behind the
panels, the BTW subsystem, the PTY terminal, the plugin platform and the build/dev loop.

Development

bun run dev            # watched server; the client bundle is built on demand
bun run dev:lan        # same, bound to the LAN

One process. Bun serves the HTML shell and bundles its assets itself, so there is no separate
client watcher to run and no build step before the first request: saving a server file restarts the
server, saving a client file is pushed to the browser over HMR (import.meta.hot, wired by Bun).
A dev server keeps the whole module graph open for HMR (~3.5k descriptors after one page load), so
GET /api/health reports fds.open and ompchamber status prints it — a long-lived session belongs
on a production server.

bun run build produces the production bundle — scripts/build-client.ts writes dist/client, which
ompchamber serve runs. The CLI builds it on demand the first time when it is missing, so a source
checkout needs no manual build; --dev skips the bundle entirely.

Verification gates, all mandatory:

bun run lint                                                  # tsc --noEmit
bunx tsc --noEmit --noUnusedLocals --noUnusedParameters        # no dead code
bun run build                                                 # production bundle
bun test                                                      # unit + route suites
bun run typecheck:e2e                                         # the Playwright specs

The browser layer is Playwright (tests/e2e/, 22 specs, *.e2e.ts) and boots a real server
subprocess against an isolated temp root per spec; bun run test:e2e runs it, and CI runs it on
every push to main plus label-gated pull requests.

The screenshots, GIFs and the MP4 in docs/assets are captured from a scratch
checkout running MOCK=true with Playwright, so they contain demo data only — never a real
workspace. The three logos in the badge row at the top live in
docs/assets/brand — Elysia and Shiki have no Simple Icons slug and shields
rejects a remote logo= URL, so each mark is inlined in the badge as a base64 data URI. Their
provenance, licences and recolouring are documented beside them, and
docs/assets/brand/badges.test.ts fails if a mark and its embedded payload drift apart.

Releasing

A push to main cuts the release — there is no manual version bump
(.github/workflows/release.yml). The workflow calls the same
reusable gates a pull request runs, then publishes as the last jobs of its own run:

gates → e2e → packages → packages-publish
                       → marketplace → app → app-publish
  1. semantic-release reads the Conventional Commits since the last tag, decides the next
    version, writes it into package.json and a ## [x.y.z] section of CHANGELOG.md, commits
    both, then creates the tag and the GitHub Release.
  2. publish-app.yml checks out the tag, verifies it against package.json, typechecks, runs
    bun test, builds dist/client, packs a dry run, and runs bun publish. Publishing is the
    last action of the release run rather than a second run dispatched beside it, so a release
    cannot report success while its publish is still queued, still waiting on the npm environment,
    or already failed.
  3. The same run releases @ompchamber/plugin-sdk and @ompchamber/ui FIRST (the app pins them, and
    a workspace: spec is substituted from bun.lock), then bumps the bundled marketplace plugins
    by their own commit history — a bundled plugin's manifest version is the whole delivery
    mechanism, since syncBundledPlugins applies a new copy only when the store's version is
    strictly newer.

What a commit does to the version is one table, in
release/release-rules.js, and release/bump.test.js asserts it:

Commit Version
feat minor
fix, perf, revert patch
feat! / fix! / perf! / refactor!, or a BREAKING CHANGE: footer major
docs, refactor, chore, style, test, build, ci no release

Two of those rows exist because the toolchain's own defaults are wrong here: a ! on a type that
cannot carry a breaking change (chore!, docs!, an unknown foo!) used to cut a major version
through the analyzer's built-in { breaking: true } fallback, and the parser's note regex is
case-insensitive, so any body line starting with breaking change — colon optional — counted as a
breaking note. The second one is not hypothetical: a docs commit whose body wrapped onto
breaking-change (!) footer cut v1.0.0 where the feats in the range asked for v0.9.0, and the
changelog printed that prose as its breaking note. Both are fixed in release/release-rules.js, and
both are locked by release/bump.test.js.

A BREAKING CHANGE: footer is therefore read only in uppercase, and only where the type allows it.

Two things to set up once and one to never do:

  • GitHub environment npm with a secret NPM_TOKEN: a granular npm token with read/write on
    ompchamber (and the two packages), 2FA bypass enabled and the publish and stage action (a
    stage-only token is rejected — Bun has no npm stage publish).
  • A failed publish is retried with gh workflow run publish.yml -f tag=vX.Y.Z, which leaves the
    release itself untouched. publish.yml is now only a TRIGGER (a published release, or that
    dispatch); the steps it runs live in publish-app.yml, which release.yml also calls.
  • Never write a skip marker ([skip ci], [ci skip], [no ci]) into a message pushed to
    main — it scans the whole message and silently skips the release run. Only the bot's release
    commit carries one, so that it does not re-trigger the workflow.

Contributing

Issues and pull requests both get an automated first pass: a GitHub App runs the omp agent headlessly
to triage a new issue and to review a pull request, and its verdict is published as a review:* label.

  • CONTRIBUTING.md is the policy — the four mandatory gates, the pull request
    sections the reviewer checks, what counts as evidence, and the label a review moves through.
  • The command surface is @<bot> help | review | summarize | triage | reproduce as the first line of a
    comment. Run the bot config check workflow to print the name this repository answers to.
  • Bugs go through the issue form; requests through the feature form. Design and scope stay the
    maintainer's call, and AGENTS.md is the normative reference for the code rules.

License

No license file is included. The source is public on GitHub and published to npm, but all rights are
reserved by the author — no license is granted for redistribution or reuse.

See CHANGELOG.md for the release history.

Yorumlar (0)

Sonuc bulunamadi