A beautiful Bubble Tea terminal interface for the odek agent.
██████ ██████ ██████ ███████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██ ██ ██ ██ █████ █████
██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██████ ██████ ███████ ██ ██
bodek is a pure front-end. It launches (or attaches to) an odek serve
instance and renders the agent's live stream — reasoning, tokens, tool calls,
approvals, skills, and memory — as a polished TUI. Every bit of agent
behaviour (tools, danger gating, sandbox, skills, memory, sessions) comes from
odek itself; bodek never re-implements any of it.
odek already ships a streaming WebSocket protocol (the one its Web UI speaks). bodek reuses that exact protocol from the terminal, which means:
- Zero duplicated logic — tools, the
dangerapproval engine, the Docker sandbox, skills, and memory all run inside odek, unchanged. - Full fidelity — token streaming, per-tool activity, and security prompts appear in the TUI exactly as the engine emits them.
- One source of truth — upgrade odek and bodek gets the new behaviour for free.
- Minimal core footprint — odek stays deliberately dependency-light, while the rich terminal UI lives here with its Bubble Tea, glamour, and other front-end dependencies.
┌──────────────┐ WebSocket (RFC 6455, JSON) ┌──────────────────┐
│ bodek │ ◄────────────────────────────► │ odek serve │
│ (Bubble Tea) │ tokens · tools · approvals │ (ReAct engine, │
│ TUI client │ │ tools, sandbox) │
└──────────────┘ └──────────────────┘
# Install odek (the engine) and bodek (the TUI)
go install github.com/BackendStack21/odek/cmd/odek@latest
go install github.com/BackendStack21/bodek/cmd/bodek@latest
# Provide an LLM key (any OpenAI-compatible provider)
export ODEK_API_KEY=sk-...
bodekbodek looks for odek on your PATH. To point at a specific binary use
--odek-bin, or skip spawning entirely with --url.
bodek # launch odek serve and start chatting
bodek --sandbox # run tool calls inside odek's Docker sandbox
bodek --url 'http://127.0.0.1:8080/?token=…' # attach with the token URL odek serve printed
bodek --url http://127.0.0.1:8080 --token d3adb33f # attach with an explicit token
bodek --odek-bin ./odek # use a specific odek binary
bodek -- --prompt-caching # pass extra flags through to `odek serve`odek serve protects its WebSocket and REST APIs with a per-instance token it
prints to stderr at startup (odek serve ⚡ http://…/?token=…). When bodek
spawns the server it picks the token up automatically; when you attach to one
that's already running, paste the token URL into --url or pass the token
itself via --token. Older odek versions without token enforcement are
detected and connected to as before.
Configuration (model, base URL, API key, MCP servers, memory, skills) is read
by odek serve from its usual chain — ~/.odek/config.json → ./odek.json →
ODEK_* env vars — so bodek inherits whatever you've already set up.
| Key | Action |
|---|---|
⏎ |
Send the prompt (or run a /command) |
/ |
Open the command palette (see below) |
@ |
Attach a file (see below) |
^R |
Browse & resume saved sessions |
^O |
Switch the model |
^T |
Toggle extended thinking for the next turn |
^J |
Insert a newline in the input |
^L |
Clear the conversation |
Esc |
Cancel the running turn |
↑ / ↓ / PgUp / PgDn / wheel |
Scroll the transcript |
^C |
Quit |
Type / at the start of the input for a command palette. ↑/↓ to choose,
⇥ to complete, ⏎ to run, esc to dismiss. You can also just type the full
command and press ⏎.
| Command | Action |
|---|---|
/help |
Show available commands and key bindings |
/clear |
Clear the conversation |
/stats |
Show session metrics & context-window gauge |
/sessions |
Browse & resume saved sessions |
/model [name] |
Switch model (opens a picker with no argument) |
/thinking [on|off] |
Toggle extended thinking for the next turn |
/cancel |
Cancel the running turn |
/quit |
Exit bodek |
Type @ to attach a file. bodek searches the working tree and shows a
completion popup; ↑/↓ to choose, ⏎ or ⇥ to insert, esc to dismiss.
> summarize @internal/client/client.go and explain the protocol
odek resolves and inlines the file content server-side (wrapped in its
untrusted-content boundary), so attachments go through the same security model
as any other external input — bodek doesn't special-case them. (Saved sessions
are resumed via /sessions or ^R, not @.)
When the agent requests approval for a dangerous operation, answer inline:
| Key | Action |
|---|---|
a |
Approve once |
d |
Deny |
t |
Trust this risk class for the session (when offered) |
- Streaming answers rendered as Markdown (glamour).
- Tool activity — every
tool_call/tool_resultshown live with a glyph per tool, a spinner, an argument preview, and a result excerpt rendered as a tree (⎿) — multi-line, blank-stripped, capped with a+N more linesfooter, and tinted with a✗when the call fails. - Sub-agents — delegations are labelled and their
subagent_logactivity nests beneath the delegating call, so a sub-agent's progress reads as its own branch of the step tree. - Security approvals — odek's
dangerengine prompts surface as an inline panel; your answer is sent straight back over the socket. - Live reasoning — the model's pre-tool thinking streams in dimmed text, with a running elapsed timer and cycling status while it works.
- Command palette (
/) and file attachments (@) — live, navigable popups. - Context-aware progress — while the agent works, the status badge shows
what it's actually doing (
🧪 running tests,📖 reading client.go,🚀 pushing) with a live elapsed timer. - Session browser (
^R) — resume, replay, or delete past conversations. - Model switcher (
^O) — change the model for the next turn. - Cancellation (
Esc) — abort a running turn via odek's cancel API. - Sandbox aware — the header shows
🛡 sandboxedor⚠ host access; pass--sandboxto run tool calls inside odek's Docker isolation. - Telemetry — session token totals and last-turn latency in the chrome.
- Fluent by default — gradient wordmark and hairline, smooth braille spinner, smart autoscroll that never yanks you while you read history, and a scroll-position indicator.
- Engine notices — skill loads, memory merges, and agent signals appear as quiet status lines.
make build # → bin/bodek
make run # build and launch
make test # go test -race ./...
make cover # coverage report for internal packages
make lint # golangci-lint (if installed)
make vet
make tidyContinuous integration runs build, go vet, golangci-lint, and the race-
enabled test suite on every push (see .github/workflows).
Tagged releases (vX.Y.Z) are built and published automatically by
GoReleaser.
Project layout:
| Path | Responsibility |
|---|---|
cmd/bodek |
CLI entry point: flags, lifecycle, wiring |
internal/server |
Launch / attach to odek serve, resolve the auth token |
internal/client |
odek serve WebSocket protocol (transport + REST + decoding) |
internal/tokens |
Local persistence of per-session auth tokens |
internal/tui |
The Bubble Tea model, update loop, panels, and view |
bodek is a pure client, so it is highly testable: the WebSocket protocol, REST
endpoints, token store, and the full Bubble Tea update/view loop are exercised
by unit and integration tests against an in-process odek serve stand-in.
Internal-package statement coverage is ~99% (client 100%, tui 99%, tokens
98%, server 95% — the remainder is unreachable OS-error handling).
MIT — see LICENSE.