Go package + CLI service for tracking AI coding subscription usage across providers.
- A reusable Go package that queries provider plugins and returns typed Go structs.
- A JSON API service (
serve) built with Cobra. - A CLI client (
query) that calls the JSON API.
This repo intentionally focuses on backend/plugin querying. The UI app lives under openusage/ and is used as the plugin reference source.
antigravityclaudecodexcopilotcursormockwindsurf
go test ./...Start API server (default Unix socket):
go run . serveQuery all plugins (auto-detects default Unix socket if present):
go run . queryQuery one plugin over the default Unix socket:
go run . query antigravityOr run over TCP:
go run . serve --addr 127.0.0.1:8080
go run . query --url http://127.0.0.1:8080Query one plugin over TCP:
go run . query antigravity --url http://127.0.0.1:8080User-level systemd setup guide:
contrib/systemd/README.md
Runs the JSON API service.
go run . serve [flags]Flags:
--addr(defaultunix://$XDG_RUNTIME_DIR/gopenusage/gopenusage.sock; fallback:/run/user/<uid>/gopenusage/gopenusage.sockor${TMPDIR}/gopenusage/gopenusage.sock)--plugins-dir(optional path to plugin manifests/icons)--data-dir(default${XDG_CONFIG_HOME}/gopenusage)
Calls the running JSON API and prints pretty JSON.
go run . query [plugin-id] [flags]Flags:
--url(defaulthttp://127.0.0.1:8080; auto-detected socket is only used when--urlis not explicitly set)--plugin(alternative to positional plugin id)--socket(optional unix socket path; when set, requests are sent over this socket)--timeout(default15s)
Socket precedence for query:
- If
--socketis set, use that Unix socket. - Else if
--urlis explicitly set, use URL over TCP/HTTP(S). - Else auto-detect the default Unix socket path and use it if present.
- Else fall back to
--urldefault (http://127.0.0.1:8080).
Returns:
{"ok": true}Returns all plugin outputs.
Optional query param:
plugins=codex,copilot(comma-separated plugin ids)
Returns one plugin output.
Manager example (pkg/openusage):
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/deicod/gopenusage/pkg/openusage"
"github.com/deicod/gopenusage/pkg/openusage/builtin"
)
func main() {
manager, err := openusage.NewManager(openusage.Options{
// Optional:
// PluginsDir: "/path/to/openusage/plugins",
// DataDir: "/path/to/state",
}, builtin.Plugins())
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
outputs, err := manager.QueryAll(ctx, nil) // nil = all plugins
if err != nil {
log.Fatal(err)
}
for _, out := range outputs {
if out.Error != "" {
fmt.Printf("%s: error: %s\n", out.ProviderID, out.Error)
continue
}
fmt.Printf("%s: plan=%s, lines=%d\n", out.ProviderID, out.Plan, len(out.Lines))
}
}Main types:
openusage.Manageropenusage.PluginOutputopenusage.MetricLine
Reusable API client example (pkg/openusage/client):
package main
import (
"context"
"fmt"
"log"
"time"
openusageclient "github.com/deicod/gopenusage/pkg/openusage/client"
)
func main() {
client, err := openusageclient.New(openusageclient.Options{
// TCP server:
BaseURL: "http://127.0.0.1:8080",
// Or Unix socket:
// SocketPath: "/run/user/1000/gopenusage/gopenusage.sock",
Timeout: 15 * time.Second,
})
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
one, err := client.QueryOne(ctx, "copilot")
if err != nil {
log.Fatal(err)
}
fmt.Printf("one: %s plan=%s\n", one.ProviderID, one.Plan)
selected, err := client.QueryPlugins(ctx, []string{"copilot", "codex"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("selected plugins: %d\n", len(selected))
}copilot: rungh auth login.codex: requires Codex auth file (CODEX_HOME/auth.json,~/.config/codex/auth.json, or~/.codex/auth.json).claude: requires~/.claude/.credentials.jsonor Keychain credentials.cursor: requires Cursorstate.vscdbwith auth tokens.antigravity: requires Antigravity app running (local language server available).windsurf: requires Windsurf/Windsurf Next running and auth status in SQLite.mock: no prerequisites.
cmd/: Cobra commands (serve,query).contrib/systemd/: user-level systemd unit + setup instructions.internal/api/: HTTP server handlers.pkg/openusage/: reusable core package.pkg/openusage/client/: reusable JSON API client package.pkg/openusage/plugins/*: provider-specific implementations.openusage/plugins/*: source plugin manifests/icons used for metadata.
- Plugin outputs include provider errors as structured data (
error+lines), so API consumers can display partial results safely. - Some providers are reverse-engineered and may change behavior without notice.