opencode-usage 0.3.1 → 0.3.3
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/dist/cli.d.ts +2 -4
- package/dist/codex-client.d.ts +6 -2
- package/dist/config-commands.d.ts +0 -4
- package/dist/config.d.ts +0 -8
- package/dist/dashboard-solid.d.ts +0 -1
- package/dist/dashboard.d.ts +0 -1
- package/dist/index.d.ts +4 -7
- package/dist/index.js +78 -123
- package/dist/index.js.map +9 -9
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -9,9 +9,7 @@ export type CliArgs = {
|
|
|
9
9
|
json?: boolean;
|
|
10
10
|
monthly?: boolean;
|
|
11
11
|
watch?: boolean;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
config?: "set-codex-token" | "show";
|
|
15
|
-
configToken?: string;
|
|
12
|
+
stats?: boolean;
|
|
13
|
+
config?: "show";
|
|
16
14
|
};
|
|
17
15
|
export declare function parseArgs(): CliArgs;
|
package/dist/codex-client.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { QuotaSnapshot } from "./types.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
|
|
3
|
+
* Resolve Codex token: explicit override > ~/.codex/auth.json auto-read
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveCodexToken(explicitToken?: string): Promise<string | undefined>;
|
|
6
|
+
/**
|
|
7
|
+
* Fetch Codex usage quota from ChatGPT API.
|
|
8
|
+
* Auto-reads token from ~/.codex/auth.json if not provided explicitly.
|
|
5
9
|
*/
|
|
6
10
|
export declare function loadCodexQuota(token?: string): Promise<QuotaSnapshot[]>;
|
package/dist/config.d.ts
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration file loader for opencode-usage
|
|
3
|
-
*/
|
|
4
|
-
export type Config = {
|
|
5
|
-
codexToken?: string;
|
|
6
|
-
};
|
|
7
1
|
export declare function getConfigPath(): string;
|
|
8
|
-
export declare function loadConfig(): Promise<Config>;
|
|
9
|
-
export declare function saveConfig(config: Config): Promise<void>;
|
package/dist/dashboard.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -3,12 +3,9 @@
|
|
|
3
3
|
* OpenCode Usage - CLI tool for tracking OpenCode AI usage and costs
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* bunx opencode-usage
|
|
7
|
-
* bunx opencode-usage --
|
|
8
|
-
* bunx opencode-usage --
|
|
9
|
-
* bunx opencode-usage --
|
|
10
|
-
* bunx opencode-usage --monthly --json
|
|
11
|
-
* bunx opencode-usage --watch
|
|
12
|
-
* bunx opencode-usage --dashboard
|
|
6
|
+
* bunx opencode-usage (dashboard, default)
|
|
7
|
+
* bunx opencode-usage --stats (table mode)
|
|
8
|
+
* bunx opencode-usage --stats -d 30
|
|
9
|
+
* bunx opencode-usage --stats --monthly --json
|
|
13
10
|
*/
|
|
14
11
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
var __require = import.meta.require;
|
|
4
3
|
|
|
5
4
|
// src/cli.ts
|
|
6
5
|
import { parseArgs as nodeParseArgs } from "util";
|
|
@@ -47,10 +46,8 @@ function parseArgs() {
|
|
|
47
46
|
json: { type: "boolean", short: "j" },
|
|
48
47
|
monthly: { type: "boolean", short: "m" },
|
|
49
48
|
watch: { type: "boolean", short: "w" },
|
|
50
|
-
|
|
51
|
-
"codex-token": { type: "string" },
|
|
49
|
+
stats: { type: "boolean", short: "S" },
|
|
52
50
|
config: { type: "string" },
|
|
53
|
-
token: { type: "string" },
|
|
54
51
|
help: { type: "boolean", short: "h" }
|
|
55
52
|
},
|
|
56
53
|
strict: true
|
|
@@ -67,10 +64,8 @@ function parseArgs() {
|
|
|
67
64
|
json: values.json,
|
|
68
65
|
monthly: values.monthly,
|
|
69
66
|
watch: values.watch,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
config: values.config,
|
|
73
|
-
configToken: values.token
|
|
67
|
+
stats: values.stats,
|
|
68
|
+
config: values.config
|
|
74
69
|
};
|
|
75
70
|
} catch (error) {
|
|
76
71
|
if (error instanceof Error && error.message.includes("Unknown option")) {
|
|
@@ -88,43 +83,33 @@ opencode-usage - Track OpenCode AI coding assistant usage and costs
|
|
|
88
83
|
Usage:
|
|
89
84
|
bunx opencode-usage [options]
|
|
90
85
|
|
|
86
|
+
Modes:
|
|
87
|
+
(default) Interactive dashboard (Bun only)
|
|
88
|
+
-S, --stats Stats table mode (works with Node.js too)
|
|
89
|
+
|
|
91
90
|
Options:
|
|
92
91
|
-p, --provider <name> Filter by provider (anthropic, openai, google, opencode)
|
|
93
92
|
-d, --days <n> Show only last N days
|
|
94
93
|
-s, --since <date> Start date (YYYYMMDD, YYYY-MM-DD, or 7d/1w/1m)
|
|
95
94
|
-u, --until <date> End date (YYYYMMDD, YYYY-MM-DD, or 7d/1w/1m)
|
|
96
|
-
-j, --json Output as JSON
|
|
97
|
-
-m, --monthly Aggregate by month
|
|
98
|
-
-w, --watch Watch mode - refresh every 5 minutes
|
|
99
|
-
|
|
100
|
-
--codex-token <t> Codex API token for quota display in dashboard
|
|
101
|
-
--config <cmd> Config commands: set-codex-token, show
|
|
95
|
+
-j, --json Output as JSON (stats mode only)
|
|
96
|
+
-m, --monthly Aggregate by month (stats mode only)
|
|
97
|
+
-w, --watch Watch mode - refresh every 5 minutes (stats mode only)
|
|
98
|
+
--config show Show current configuration
|
|
102
99
|
-h, --help Show this help message
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
Codex Quota:
|
|
102
|
+
Dashboard auto-reads Codex auth from ~/.codex/auth.json.
|
|
103
|
+
Run 'codex login' to authenticate.
|
|
107
104
|
|
|
108
105
|
Examples:
|
|
109
106
|
bunx opencode-usage
|
|
110
|
-
bunx opencode-usage --
|
|
111
|
-
bunx opencode-usage
|
|
112
|
-
bunx opencode-usage --
|
|
113
|
-
bunx opencode-usage --since 7d
|
|
114
|
-
bunx opencode-usage --
|
|
115
|
-
bunx opencode-usage --watch
|
|
116
|
-
bunx opencode-usage -w -d 1
|
|
117
|
-
bunx opencode-usage --dashboard
|
|
118
|
-
bunx opencode-usage --dashboard --codex-token <token>
|
|
107
|
+
bunx opencode-usage --stats
|
|
108
|
+
bunx opencode-usage --stats --provider anthropic
|
|
109
|
+
bunx opencode-usage --stats -p openai -d 30
|
|
110
|
+
bunx opencode-usage --stats --since 7d --monthly --json
|
|
111
|
+
bunx opencode-usage --stats -w -d 1
|
|
119
112
|
bunx opencode-usage --config show
|
|
120
|
-
bunx opencode-usage --config set-codex-token --token sk-...
|
|
121
|
-
|
|
122
|
-
How to get Codex token:
|
|
123
|
-
1. Open chatgpt.com in browser
|
|
124
|
-
2. Open DevTools (F12 or Cmd+Option+I)
|
|
125
|
-
3. Go to Network tab and reload page
|
|
126
|
-
4. Find request to 'backend-api/wham/usage'
|
|
127
|
-
5. Copy 'Authorization' header value (starts with 'Bearer ')
|
|
128
113
|
`);
|
|
129
114
|
}
|
|
130
115
|
|
|
@@ -28302,15 +28287,38 @@ async function loadAntigravityQuota() {
|
|
|
28302
28287
|
}
|
|
28303
28288
|
|
|
28304
28289
|
// src/codex-client.ts
|
|
28290
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
28291
|
+
import { homedir as homedir3 } from "os";
|
|
28292
|
+
import { join as join4 } from "path";
|
|
28293
|
+
var isBun3 = typeof globalThis.Bun !== "undefined";
|
|
28305
28294
|
var CODEX_API_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
28295
|
+
var CODEX_AUTH_PATH = join4(homedir3(), ".codex", "auth.json");
|
|
28296
|
+
async function readCodexAuthToken() {
|
|
28297
|
+
try {
|
|
28298
|
+
const content = isBun3 ? await Bun.file(CODEX_AUTH_PATH).text() : await readFile3(CODEX_AUTH_PATH, "utf-8");
|
|
28299
|
+
const auth = JSON.parse(content);
|
|
28300
|
+
if (auth.OPENAI_API_KEY) {
|
|
28301
|
+
return auth.OPENAI_API_KEY;
|
|
28302
|
+
}
|
|
28303
|
+
return auth.tokens?.access_token ?? undefined;
|
|
28304
|
+
} catch {
|
|
28305
|
+
return;
|
|
28306
|
+
}
|
|
28307
|
+
}
|
|
28308
|
+
async function resolveCodexToken(explicitToken) {
|
|
28309
|
+
if (explicitToken)
|
|
28310
|
+
return explicitToken;
|
|
28311
|
+
return readCodexAuthToken();
|
|
28312
|
+
}
|
|
28306
28313
|
async function loadCodexQuota(token) {
|
|
28307
|
-
|
|
28314
|
+
const resolvedToken = await resolveCodexToken(token);
|
|
28315
|
+
if (!resolvedToken) {
|
|
28308
28316
|
return [
|
|
28309
28317
|
{
|
|
28310
28318
|
source: "codex",
|
|
28311
28319
|
label: "Codex",
|
|
28312
28320
|
used: 0,
|
|
28313
|
-
error: "
|
|
28321
|
+
error: "Not logged in. Run: codex login"
|
|
28314
28322
|
}
|
|
28315
28323
|
];
|
|
28316
28324
|
}
|
|
@@ -28318,17 +28326,18 @@ async function loadCodexQuota(token) {
|
|
|
28318
28326
|
const response = await fetch(CODEX_API_URL, {
|
|
28319
28327
|
method: "GET",
|
|
28320
28328
|
headers: {
|
|
28321
|
-
Authorization: `Bearer ${
|
|
28329
|
+
Authorization: `Bearer ${resolvedToken}`,
|
|
28322
28330
|
"Content-Type": "application/json"
|
|
28323
28331
|
}
|
|
28324
28332
|
});
|
|
28325
28333
|
if (!response.ok) {
|
|
28334
|
+
const hint = response.status === 401 ? " (token expired? Run: codex login)" : "";
|
|
28326
28335
|
return [
|
|
28327
28336
|
{
|
|
28328
28337
|
source: "codex",
|
|
28329
28338
|
label: "Codex",
|
|
28330
28339
|
used: 0,
|
|
28331
|
-
error: `API error: ${response.status}`
|
|
28340
|
+
error: `API error: ${response.status}${hint}`
|
|
28332
28341
|
}
|
|
28333
28342
|
];
|
|
28334
28343
|
}
|
|
@@ -29052,18 +29061,16 @@ function Dashboard(props) {
|
|
|
29052
29061
|
error: `Load error: ${err}`
|
|
29053
29062
|
});
|
|
29054
29063
|
}
|
|
29055
|
-
|
|
29056
|
-
|
|
29057
|
-
|
|
29058
|
-
|
|
29059
|
-
|
|
29060
|
-
|
|
29061
|
-
|
|
29062
|
-
|
|
29063
|
-
|
|
29064
|
-
|
|
29065
|
-
});
|
|
29066
|
-
}
|
|
29064
|
+
try {
|
|
29065
|
+
const codex = await loadCodexQuota();
|
|
29066
|
+
results.push(...codex);
|
|
29067
|
+
} catch (err) {
|
|
29068
|
+
results.push({
|
|
29069
|
+
source: "codex",
|
|
29070
|
+
label: "Codex",
|
|
29071
|
+
used: 0,
|
|
29072
|
+
error: `Load error: ${err}`
|
|
29073
|
+
});
|
|
29067
29074
|
}
|
|
29068
29075
|
setQuotas(results);
|
|
29069
29076
|
};
|
|
@@ -29199,9 +29206,6 @@ function Dashboard(props) {
|
|
|
29199
29206
|
}
|
|
29200
29207
|
async function runSolidDashboard(options) {
|
|
29201
29208
|
await render(() => createComponent2(Dashboard, {
|
|
29202
|
-
get codexToken() {
|
|
29203
|
-
return options.codexToken;
|
|
29204
|
-
},
|
|
29205
29209
|
get providerFilter() {
|
|
29206
29210
|
return options.providerFilter;
|
|
29207
29211
|
},
|
|
@@ -29218,57 +29222,32 @@ async function runSolidDashboard(options) {
|
|
|
29218
29222
|
});
|
|
29219
29223
|
}
|
|
29220
29224
|
|
|
29225
|
+
// src/config-commands.ts
|
|
29226
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
29227
|
+
import { homedir as homedir5 } from "os";
|
|
29228
|
+
import { join as join6 } from "path";
|
|
29229
|
+
|
|
29221
29230
|
// src/config.ts
|
|
29222
|
-
import {
|
|
29223
|
-
import {
|
|
29224
|
-
import { join as join4 } from "path";
|
|
29225
|
-
var isBun3 = typeof globalThis.Bun !== "undefined";
|
|
29231
|
+
import { homedir as homedir4 } from "os";
|
|
29232
|
+
import { join as join5 } from "path";
|
|
29226
29233
|
function getConfigPath() {
|
|
29227
|
-
const configDir = process.env.XDG_CONFIG_HOME ??
|
|
29228
|
-
return
|
|
29229
|
-
}
|
|
29230
|
-
async function loadConfig() {
|
|
29231
|
-
try {
|
|
29232
|
-
const configPath = getConfigPath();
|
|
29233
|
-
const content = isBun3 ? await Bun.file(configPath).text() : await readFile3(configPath, "utf-8");
|
|
29234
|
-
return JSON.parse(content);
|
|
29235
|
-
} catch {
|
|
29236
|
-
return {};
|
|
29237
|
-
}
|
|
29238
|
-
}
|
|
29239
|
-
async function saveConfig(config) {
|
|
29240
|
-
const configPath = getConfigPath();
|
|
29241
|
-
const configDir = join4(configPath, "..");
|
|
29242
|
-
const { mkdir, writeFile } = await import("fs/promises");
|
|
29243
|
-
await mkdir(configDir, { recursive: true });
|
|
29244
|
-
const content = JSON.stringify(config, null, 2) + `
|
|
29245
|
-
`;
|
|
29246
|
-
if (isBun3) {
|
|
29247
|
-
await Bun.write(configPath, content);
|
|
29248
|
-
} else {
|
|
29249
|
-
await writeFile(configPath, content, "utf-8");
|
|
29250
|
-
}
|
|
29234
|
+
const configDir = process.env.XDG_CONFIG_HOME ?? join5(homedir4(), ".config");
|
|
29235
|
+
return join5(configDir, "opencode-usage", "config.json");
|
|
29251
29236
|
}
|
|
29252
29237
|
|
|
29253
29238
|
// src/config-commands.ts
|
|
29254
|
-
|
|
29255
|
-
const config = await loadConfig();
|
|
29256
|
-
config.codexToken = token;
|
|
29257
|
-
await saveConfig(config);
|
|
29258
|
-
console.log(`\u2713 Codex token saved to ${getConfigPath()}`);
|
|
29259
|
-
}
|
|
29239
|
+
var CODEX_AUTH_PATH2 = join6(homedir5(), ".codex", "auth.json");
|
|
29260
29240
|
async function showConfig() {
|
|
29261
|
-
const config = await loadConfig();
|
|
29262
29241
|
const configPath = getConfigPath();
|
|
29263
29242
|
console.log(`
|
|
29264
|
-
Configuration file: ${configPath}
|
|
29265
|
-
|
|
29266
|
-
|
|
29267
|
-
const
|
|
29268
|
-
|
|
29269
|
-
|
|
29270
|
-
|
|
29271
|
-
}
|
|
29243
|
+
Configuration file: ${configPath}`);
|
|
29244
|
+
let hasCodexAuth = false;
|
|
29245
|
+
try {
|
|
29246
|
+
const content = await readFile4(CODEX_AUTH_PATH2, "utf-8");
|
|
29247
|
+
const auth = JSON.parse(content);
|
|
29248
|
+
hasCodexAuth = !!auth.tokens?.access_token;
|
|
29249
|
+
} catch {}
|
|
29250
|
+
console.log(` Codex auth: ${hasCodexAuth ? "~/.codex/auth.json (auto)" : "(not found \u2014 run: codex login)"}`);
|
|
29272
29251
|
console.log();
|
|
29273
29252
|
}
|
|
29274
29253
|
|
|
@@ -29323,37 +29302,13 @@ Loading OpenCode usage data from: ${options.storagePath}`);
|
|
|
29323
29302
|
}
|
|
29324
29303
|
}
|
|
29325
29304
|
async function main2() {
|
|
29326
|
-
const {
|
|
29327
|
-
provider,
|
|
29328
|
-
days,
|
|
29329
|
-
since,
|
|
29330
|
-
until,
|
|
29331
|
-
json,
|
|
29332
|
-
monthly,
|
|
29333
|
-
watch,
|
|
29334
|
-
dashboard,
|
|
29335
|
-
codexToken,
|
|
29336
|
-
config,
|
|
29337
|
-
configToken
|
|
29338
|
-
} = parseArgs();
|
|
29305
|
+
const { provider, days, since, until, json, monthly, watch, stats, config } = parseArgs();
|
|
29339
29306
|
if (config === "show") {
|
|
29340
29307
|
await showConfig();
|
|
29341
29308
|
return;
|
|
29342
29309
|
}
|
|
29343
|
-
if (
|
|
29344
|
-
if (!configToken) {
|
|
29345
|
-
console.error("Error: --config set-codex-token requires --token <value>");
|
|
29346
|
-
console.error("Usage: opencode-usage --config set-codex-token --token <token>");
|
|
29347
|
-
process.exit(1);
|
|
29348
|
-
}
|
|
29349
|
-
await setCodexToken(configToken);
|
|
29350
|
-
return;
|
|
29351
|
-
}
|
|
29352
|
-
const configData = await loadConfig();
|
|
29353
|
-
const effectiveCodexToken = codexToken ?? configData.codexToken;
|
|
29354
|
-
if (dashboard) {
|
|
29310
|
+
if (!stats) {
|
|
29355
29311
|
await runSolidDashboard({
|
|
29356
|
-
codexToken: effectiveCodexToken,
|
|
29357
29312
|
refreshInterval: 300,
|
|
29358
29313
|
providerFilter: provider,
|
|
29359
29314
|
initialDays: days
|
|
@@ -29391,4 +29346,4 @@ async function main2() {
|
|
|
29391
29346
|
}
|
|
29392
29347
|
main2().catch(console.error);
|
|
29393
29348
|
|
|
29394
|
-
//# debugId=
|
|
29349
|
+
//# debugId=4F3AFA8CCC597E5564756E2164756E21
|