ompchamber
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.
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.
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.
Architecture · Design · Changelog · Plugins · Issues

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=trueswaps in bundled demo datasets. Every screenshot and
recording on this page was captured inMOCK=true. - Bun only. The server imports
bun:sqliteandBun.YAML, the client is bundled byBun.build,
and sessions, config and updates shell out to theompbinary.
Feature tour

Full-quality recording (MP4, 57s): docs/assets/tour.mp4 · theme cycle in motion: docs/assets/themes.gif
![]() Files — project tree with git markers, cut/copy/paste, drag-to-move, and a file open in the editor |
![]() Search — ripgrep-backed streaming results, virtualized list, in-file jump and replace preview |
![]() Source Control — changes, commits, branches, tracking, stash, and merge/rebase conflict resolution |
![]() Editor & diff — split or unified, full-code or diff-only, word wrap, find/replace, command palette |
![]() Terminal — a real PTY (one shell per terminal id, scrollback replayed on reload) |
![]() Context & telemetry — context window use, cache hit rate, cost, per-message breakdown |
![]() Wiki — reads a project's GitHub/GitLab wiki over git, with its own _Sidebar as navigation |
![]() Todos — the session's live list, all five omp statuses, blockers and per-phase progress |
![]() Plan — the plan artifact an omp session is working from, rendered as a document |
![]() Usage — provider balance, plan windows, saved resets, per-model token spend |
![]() Browser (yours) — a private iframe with real history and device presets |
![]() Browser agent — a live screencast of the tab the agent is driving |
![]() Panel plugins — every view is a plugin, including the eleven bundled ones |
![]() Providers — dialects, discovery, hand-added models, credential removal |
![]() Mobile view — the same session on a phone: chat, sessions drawer, and the panel drawer |
![]() 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,waitwith 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 — plusStatsandSession 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 updateinstalls 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--tlsfor a self-signed HTTPS
listener andPOST /api/auth/revoketo 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


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 setsdata-theme and announces it — so an open editor, a live terminal and a diagram in the transcript
all follow with no reload.

Install
OMPChamber is published to npm and runs on Bun, not Node:
Bun only — the server imports
bun:sqliteandBun.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 theompbinary, soserverefuses to start when it is not onPATH(or set viaOMPCHAMBER_OMP_BIN).MOCK=trueis 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/clientthe server answers503with the exact command to run — never a bare500.
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 manualbun 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, nostatus/stopfor 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 detachedserve(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 againstlaunchctl print: the entry isstate = runningwith the CLI as its program. - macOS registers
~/Library/LaunchAgents/dev.ompchamber.tray.plist(RunAtLoad, noKeepAlive— 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\Runvalue throughreg, 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.NotifyIconon Windows, andyad/zenity--notificationon 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. --startupwithout--trayis 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 3000is the deliberate way. - A process that is not OMPChamber is never signalled either: the server names the port and
prints thelsof -nP -iTCP:<port> -sTCP:LISTENcommand to identify it. bun run devhot reload is unaffected:--hotre-evaluates the entry inside the running
process, and a port held by that process is recognized as its own.statusandlogsreport every live instance by default;--port <port>narrows them to one.stopacts on the instances the CLI started, andrestart/updateskip 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.
stopreports 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 statusstill 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:statussaid "not running" whileserverefused 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/healthnames
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-foris 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, andSecure
whenever the request arrived over https — including through a proxy that setsx-forwarded-proto. Changing the password or callingPOST /api/auth/revokerotates 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 —
--tlsgenerates a self-signed certificate (SAN coveringlocalhost,127.0.0.1and
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--lanwarns 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 anode:builtin orbun:sqlite— it also runs in the
browser. Server-only helpers belong insrc/server/lib/**.- Route modules keep the ported Remix shape (
loaderfor GET,actionfor mutations) so status
codes stay identical;actionowns method dispatch and returns its own405. - Every internal import uses the
@/alias, folders mirror component names in kebab-case, and
every.ts/.tsxfile 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), soGET /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, whichompchamber 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 indocs/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, anddocs/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
semantic-releasereads the Conventional Commits since the last tag, decides the next
version, writes it intopackage.jsonand a## [x.y.z]section ofCHANGELOG.md, commits
both, then creates the tag and the GitHub Release.publish-app.ymlchecks out the tag, verifies it againstpackage.json, typechecks, runsbun test, buildsdist/client, packs a dry run, and runsbun 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 thenpmenvironment,
or already failed.- The same run releases
@ompchamber/plugin-sdkand@ompchamber/uiFIRST (the app pins them, and
aworkspace:spec is substituted frombun.lock), then bumps the bundled marketplace plugins
by their own commit history — a bundled plugin's manifest version is the whole delivery
mechanism, sincesyncBundledPluginsapplies a new copy only when the store's version is
strictly newer.
What a commit does to the version is one table, inrelease/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 ontobreaking-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
npmwith a secretNPM_TOKEN: a granular npm token with read/write onompchamber(and the two packages), 2FA bypass enabled and thepublish and stageaction (a
stage-only token is rejected — Bun has nonpm 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.ymlis now only a TRIGGER (a published release, or that
dispatch); the steps it runs live inpublish-app.yml, whichrelease.ymlalso calls. - Never write a skip marker (
[skip ci],[ci skip],[no ci]) into a message pushed tomain— 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.mdis 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 | reproduceas 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, andAGENTS.mdis 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)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi















