Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added
- Currency: Korean won (KRW) in the preferred-currency picker, with correct zero-decimal formatting (#2669, refs #2449). Thanks @kes02!
- Dashboard v1 snapshot: Claude rows now include all claude-swap accounts (windows, pace, active flag, redacted identity) when the integration is enabled.
- CLI: add a built-in auto-refreshing web dashboard at `/` to `codexbar serve`.
- CLI: expose Codex cost-history completeness in JSON and add an experimental provider-native-only scan mode (#2520). Thanks @NickGuAI!
- Usage & Spend: add an accessible daily, weekly, and cumulative token-activity heatmap backed by the existing persistent cost scan cache (#2548). Thanks @Yuxin-Qiao!
- Settings: the sidebar is now resizable — drag the divider between 200–380pt; the width persists across launches.
Expand Down
8 changes: 5 additions & 3 deletions Sources/CodexBarCLI/CLIHelp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,21 @@ extension CodexBarCLI {

Description:
Start a foreground HTTP server that exposes existing CLI JSON payloads and a
token-gated dashboard snapshot. The server binds to 127.0.0.1 by default;
`localhost` is normalized to 127.0.0.1.
token-gated dashboard snapshot, with a built-in web UI at /. The static web UI
is always open; it sends a browser-entered token only when fetching snapshot data.
The server binds to 127.0.0.1 by default; `localhost` is normalized to 127.0.0.1.
GET /dashboard/v1/snapshot requires "Authorization: Bearer YOUR_TOKEN" and fails
closed (401) when no token is configured. Set the token with --dashboard-token or,
preferably, the CODEXBAR_DASHBOARD_TOKEN environment variable (argv leaks via ps).
Transport is plain HTTP: the token crosses the network in cleartext on every
request. A non-loopback --host therefore requires both a dashboard token and
--allow-plain-http, which records that you accept that trade-off. On a
non-loopback host the token also gates /usage and /cost (account data);
/health is always open. Use a TLS-terminating reverse proxy for anything
/ and /health are always open. Use a TLS-terminating reverse proxy for anything
beyond a trusted network segment.

Endpoints:
GET / Built-in web dashboard
GET /health
GET /usage
GET /usage?provider=claude
Expand Down
7 changes: 6 additions & 1 deletion Sources/CodexBarCLI/CLIServeCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct ServeOptions: CommanderParsable {
}

enum CLIServeRoute: Equatable {
case webUI
case health
case usage(provider: String?)
case cost(provider: String?)
Expand All @@ -59,6 +60,8 @@ enum CLIServeRouter {
let normalizedProvider = provider?.isEmpty == false ? provider : nil

switch path {
case "/":
return .webUI
case "/health":
return .health
case "/usage":
Expand Down Expand Up @@ -109,7 +112,7 @@ struct ServeRuntime {
let dashboardAuth: CLIServeDashboardAuth
/// True for non-loopback binds: every data route (`/usage`, `/cost`,
/// `/dashboard/v1/snapshot`) then requires the bearer token, so account data
/// is never exposed to the network unauthenticated. `/health` stays open.
/// is never exposed to the network unauthenticated. `/` and `/health` stay open.
/// Resolved once at startup from the bind host.
let dataRoutesRequireAuth: Bool

Expand Down Expand Up @@ -823,6 +826,8 @@ extension CodexBarCLI {
}

switch route {
case .webUI:
return CLIServeWebUI.response()
case .health:
return Self.serveHealthResponse(version: runtime.healthVersion)
case let .usage(provider):
Expand Down
Loading