- TypeScript 63.3%
- JavaScript 36.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Token count now sums input_tokens + output_tokens from the provider usage payload (cache read / write excluded -- they're internal accounting, not real spend). Streaming char estimate and high-water mark fallbacks preserved for ollama / minimax-m3 and last-resort. Add e2e-test.mjs / e2e-harness.ts: a tsx-driven harness that loads the real extension source via a mock ExtensionAPI, fires synthetic events (session_start, input, before_provider_request, message_update chunks, after_provider_response, agent_end, tick), and asserts the rendered row at each phase. Three scenarios: --provider (Anthropic / OpenAI / Google style), --ollama (streaming only), --hybrid (both). 14 assertions, all green. |
||
| src | ||
| .gitignore | ||
| e2e-harness.ts | ||
| e2e-test.mjs | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| smoke.mjs | ||
| tsconfig.json | ||
omp-time-token-spent
Oh My Pi extension that renders a single status line below the editor input box, Claude Code style: elapsed time on the left, tokens spent close beside it. Refreshes every second on a managed tick. Tokens are scoped per prompt -- they reset every time you press Enter on a new prompt, never accumulate across the session.
The widget sits between the input box and the agent's last reply, so it reads as part of the live UI rather than as a chat line in the transcript scrollback. The spinner label that omp renders alongside the working circle is left untouched. After the AI finishes responding, the row stays visible showing the final time + tokens until you press Enter on the next prompt.
Install
Local install (recommended -- works without bun on $PATH)
From a clone of this repo:
omp plugin install /absolute/path/to/omp-time-token-spent
omp will link the directory as a plugin and load src/index.ts automatically on next start (or after /reload).
Remote install (requires bun)
If you have Bun installed and your omp build accepts the github: scheme:
omp plugin install github:git.pandem.fr/outage.sh/omp-time-token-spent
Manual install (last resort)
Add the extension path to ~/.omp/agent/config.yml:
extensions:
- /absolute/path/to/omp-time-token-spent/src/index.ts
Then omp --reload.
What you'll see
A single line below the editor input box, updated every second. Example after 7 seconds of work on the current prompt and 2,459 tokens consumed by it:
7s 2.46k tokens
Ns-- wall time since you pressed Enter on the current prompt.N tokens-- tokens consumed by this prompt, summing input + output (Anthropic / OpenAI / Google provider-reported deltas, or streaming char estimate for ollama, or context-growth delta as last resort). Cache read / write tokens are excluded (internal accounting detail, not real spend). FormattedN,N.NNk(under 10k),Nk(under 1M),N.NNM(over 1M).(N calls)-- only shown when the turn made more than one provider request.
When agent_end fires, the timer freezes at the final value and the row stays on screen with the frozen tally. The next input event resets every counter and the row jumps to 0s 0 tokens immediately, then ticks up again as the new turn runs.
Why a widget, not a spinner label
omp renders working text alongside the spinner via setWorkingMessage, and footer slots via setStatus. Both are reserved surfaces that change the model's own UI text. We use setWidget(..., { placement: "belowEditor" }) so the time/tokens row sits in its own dedicated band between the input box and the last AI reply -- not in the transcript scrollback, and never leaking into the model's prompt.
Events used
| omp event | What we do |
|---|---|
input |
Snapshot ctx.getContextUsage().tokens so subsequent tick reads show only tokens spent by the current prompt. Resets all per-turn counters. |
before_provider_request |
Increment callCount for the (N calls) suffix. |
after_provider_response |
Sum provider-reported usage deltas (input_tokens / output_tokens / cache_read / cache_write). |
session_start |
Register a 1-second tick via ctx.setInterval that updates the widget. |
Testing
Pure-logic smoke tests (no runtime needed):
node smoke.mjs
13 cases cover: idle, in+out summed, ollama streaming, provider-trumps-streaming, hwm fallback, multi-call, minutes, input-reset, freeze-on-agent-end, streaming floor, frozen streaming, pure input counting, cache tokens ignored.
End-to-end tests with a mock ExtensionAPI driving the real extension source via tsx:
node e2e-test.mjs # default: provider scenario (Anthropic / OpenAI / Google)
node e2e-test.mjs --provider # usage deltas -- expects input + output summed
node e2e-test.mjs --ollama # streaming only -- expects char-based estimate
node e2e-test.mjs --hybrid # both signals -- provider deltas should win
Each scenario drives the full event sequence (session_start -> input -> before_provider_request -> message_update chunks -> after_provider_response with usage -> tick -> agent_end -> tick) and asserts the rendered row at each phase.
Privacy
- Reads no environment variables.
- Reads no API keys, tokens, hostnames, or file paths beyond what omp already passes in its own event payloads.
- Writes nothing to disk.
- Sends nothing off-host.
License
MIT -- see LICENSE.