open-usage 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -102,6 +102,9 @@ The check runs at most once a day, and [Configuration](#configuration) below cov
102
102
  | Codex | a sandboxed `codex app-server` | `~/.codex/sessions` |
103
103
  | OpenCode Go | local estimate, or the dashboard cookie | `opencode.db` |
104
104
 
105
+ Those three are what ships today.
106
+ More providers are planned, so if a plan you pay for is missing, [open an issue](https://github.com/arionrefat/open-usage/issues) and say which one.
107
+
105
108
  `open-usage` is read-only, and there is no account to create and no key to paste.
106
109
  It reuses the logins your CLIs already have: Claude and Codex limits come from their own signed-in CLIs, so their credentials are never read.
107
110
  The one credential file it opens is OpenCode's `auth.json`, and only to show connection status - the key is masked on read and never displayed, logged, or sent anywhere.
@@ -113,7 +116,7 @@ The other asks `registry.npmjs.org` whether a newer version has been published,
113
116
  Set `OPEN_USAGE_NO_UPDATE_CHECK` to switch it off.
114
117
 
115
118
  OpenCode Go does not publish per-account limits, so its percentages are local estimates and are labelled as such in the UI.
116
- Configuring the cookie below replaces those estimates with exact figures, and is enough on its own: OpenCode itself need not be installed, though without it the Go card has no token history to chart.
119
+ [Exact OpenCode Go limits](#exact-opencode-go-limits) below covers the optional cookie that replaces them with the dashboard's own figures.
117
120
  [docs/PROVIDERS.md](docs/PROVIDERS.md) explains how each number is derived.
118
121
 
119
122
  ## Configuration
@@ -129,6 +132,40 @@ They persist to `~/.config/open-usage/preferences.json` (or `$XDG_CONFIG_HOME/op
129
132
  | `OPEN_USAGE_OPENCODE_COOKIE` | exact OpenCode Go windows, no install needed |
130
133
  | `OPEN_USAGE_NO_UPDATE_CHECK` | set to anything to stop the daily version check |
131
134
 
135
+ ### Exact OpenCode Go limits
136
+
137
+ OpenCode publishes Go plan usage to its dashboard but not to any public API, so the exact numbers sit behind your signed-in `opencode.ai` session.
138
+ Hand `open-usage` that session cookie and the Go card swaps its local estimate for the dashboard's own rolling, weekly, and monthly figures.
139
+
140
+ 1. Sign in at [opencode.ai](https://opencode.ai) and open the dashboard.
141
+ 2. Open devtools and find the cookie store: **Application → Cookies** in Chrome and Edge, **Storage → Cookies** in Firefox and Safari.
142
+ 3. Select `https://opencode.ai` and copy the value of the `auth` cookie - `__Host-auth` if that is the name your browser holds.
143
+ 4. Give it to `open-usage`, either in `~/.config/open-usage/config.json` - a file you create, separate from `preferences.json`:
144
+
145
+ ```json
146
+ { "opencodeCookie": "auth=<value>" }
147
+ ```
148
+
149
+ or per-shell:
150
+
151
+ ```bash
152
+ export OPEN_USAGE_OPENCODE_COOKIE='auth=<value>'
153
+ ```
154
+
155
+ The config file is re-read on every poll, so a cookie pasted there lands within a minute - press `r` to skip the wait.
156
+ The environment variable is read once at launch, so exporting it means restarting the app.
157
+
158
+ The cookie is optional, and it is also sufficient on its own.
159
+ Without it the Go card still works, on the local estimate; with it, OpenCode need not be installed at all, though a machine with no `opencode.db` has no token history to chart and the card says so.
160
+
161
+ Only the `auth` / `__Host-auth` pair is ever sent, and anything else in a pasted header is stripped before the request leaves your machine.
162
+ The cookie carries its own expiry, and the card warns you through its final seven days and again once it lapses, so a dead session cannot quietly pass for a live one.
163
+ If the dashboard changes shape underneath it, the card falls back to the estimate with a note rather than showing a figure it can no longer stand behind.
164
+
165
+ Treat the value like a password: it is a full dashboard credential, not a usage-scoped token.
166
+ Prefer the config file over the environment variable to keep it out of your shell history, never paste it into a bug report, and know that nobody should ever ask you for it.
167
+ This is deliberately a manual step - `open-usage` never reads your browser's cookie jar for you.
168
+
132
169
  ## Development
133
170
 
134
171
  Requires [Bun](https://bun.sh) 1.0 or newer.
package/bin/open-usage.js CHANGED
@@ -3,7 +3,9 @@
3
3
  // The binary embeds Bun, so nothing needs to be installed alongside it.
4
4
  // Deliberately dependency-free and syntax-conservative so it runs on any Node.
5
5
  import { createRequire } from "node:module";
6
- import { spawnSync } from "node:child_process";
6
+ import { spawn } from "node:child_process";
7
+ import { realpathSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
7
9
 
8
10
  const PLATFORM_PACKAGES = {
9
11
  "darwin-arm64": "@open-usage/darwin-arm64",
@@ -44,13 +46,42 @@ function resolvePlatformBinary() {
44
46
  }
45
47
  }
46
48
 
47
- const result = spawnSync(resolvePlatformBinary(), process.argv.slice(2), { stdio: "inherit" });
49
+ export function launch(binary = resolvePlatformBinary(), args = process.argv.slice(2)) {
50
+ const child = spawn(binary, args, { stdio: "inherit" });
51
+ const signals = ["SIGTERM", "SIGINT", "SIGHUP"];
52
+ const handlers = new Map();
48
53
 
49
- if (result.error) {
50
- fail("failed to start the platform binary", result.error.message);
51
- }
52
- // Mirror the child's termination so shells and CI see the real outcome.
53
- if (result.signal) {
54
- process.kill(process.pid, result.signal);
54
+ function cleanup() {
55
+ for (const [signal, handler] of handlers) process.off(signal, handler);
56
+ }
57
+
58
+ for (const signal of signals) {
59
+ const handler = () => {
60
+ try {
61
+ child.kill(signal);
62
+ } catch {
63
+ // The child may already be exiting; its exit event remains authoritative.
64
+ }
65
+ };
66
+ handlers.set(signal, handler);
67
+ process.on(signal, handler);
68
+ }
69
+
70
+ child.once("error", (error) => {
71
+ cleanup();
72
+ fail("failed to start the platform binary", error.message);
73
+ });
74
+ child.once("exit", (code, signal) => {
75
+ cleanup();
76
+ if (signal) {
77
+ process.kill(process.pid, signal);
78
+ return;
79
+ }
80
+ process.exit(code ?? 1);
81
+ });
55
82
  }
56
- process.exit(result.status ?? 1);
83
+
84
+ if (
85
+ process.argv[1] &&
86
+ realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])
87
+ ) launch();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-usage",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "A terminal dashboard for unified AI plan usage across Claude Code, Codex, and OpenCode Go",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-only",
@@ -41,6 +41,7 @@
41
41
  "shot": "bun scripts/shot.tsx",
42
42
  "build": "bun build --compile --minify src/index.tsx --outfile dist/open-usage",
43
43
  "build:npm": "bun scripts/build-npm-packages.ts",
44
+ "prepublishOnly": "bun scripts/check-prepublish.ts",
44
45
  "version:set": "bun scripts/set-version.ts",
45
46
  "test": "bun test",
46
47
  "typecheck": "tsc --noEmit"
@@ -54,10 +55,10 @@
54
55
  "typescript": "^5"
55
56
  },
56
57
  "optionalDependencies": {
57
- "@open-usage/darwin-arm64": "0.3.1",
58
- "@open-usage/linux-arm64": "0.3.1",
59
- "@open-usage/linux-x64": "0.3.1",
60
- "@open-usage/win32-arm64": "0.3.1",
61
- "@open-usage/win32-x64": "0.3.1"
58
+ "@open-usage/darwin-arm64": "0.4.0",
59
+ "@open-usage/linux-arm64": "0.4.0",
60
+ "@open-usage/linux-x64": "0.4.0",
61
+ "@open-usage/win32-arm64": "0.4.0",
62
+ "@open-usage/win32-x64": "0.4.0"
62
63
  }
63
64
  }