aieracard 0.1.0 → 0.1.1
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 +3 -1
- package/dist/index.js +64 -19
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -11,7 +11,9 @@ The CLI collects usage **locally** from:
|
|
|
11
11
|
- **Claude Code** — local JSONL logs (`~/.claude/projects`), fully automatic
|
|
12
12
|
- **Cursor** — all-time usage via your own cursor.com session (token resolved
|
|
13
13
|
locally, sent only to cursor.com); CSV export as fallback
|
|
14
|
-
- **OpenRouter** —
|
|
14
|
+
- **OpenRouter** — management API key (read-only `/credits` + `/activity`).
|
|
15
|
+
Tokens are last 30 days; all-time spend stays on the source object and is
|
|
16
|
+
**not** rolled into the card's aggregate "compute spent"
|
|
15
17
|
|
|
16
18
|
It then shows you the **exact JSON** that would be uploaded — aggregate
|
|
17
19
|
numbers only: token counts, cost totals, active days, streaks, model names.
|
package/dist/index.js
CHANGED
|
@@ -4084,8 +4084,11 @@ var claudeCodeSourceSchema = external_exports.object({
|
|
|
4084
4084
|
models
|
|
4085
4085
|
});
|
|
4086
4086
|
var openRouterSourceSchema = external_exports.object({
|
|
4087
|
+
// Tokens/requests/models are last-30-day (/activity). totalCostUsd is
|
|
4088
|
+
// all-time spend from /credits — nullable if that endpoint fails.
|
|
4089
|
+
// Aggregate card totals intentionally omit this cost (mixed windows).
|
|
4087
4090
|
totalTokens: tokens,
|
|
4088
|
-
totalCostUsd: costUsd,
|
|
4091
|
+
totalCostUsd: costUsd.nullable(),
|
|
4089
4092
|
requestCount: count,
|
|
4090
4093
|
activeDays: count,
|
|
4091
4094
|
windowDays: external_exports.literal(30),
|
|
@@ -4339,41 +4342,69 @@ async function validateOpenRouterKey(key) {
|
|
|
4339
4342
|
return false;
|
|
4340
4343
|
}
|
|
4341
4344
|
}
|
|
4345
|
+
function parseCostUsd(credits) {
|
|
4346
|
+
if (!credits || typeof credits !== "object") return null;
|
|
4347
|
+
const data = credits.data;
|
|
4348
|
+
const raw = data?.total_usage;
|
|
4349
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? Math.round(raw * 100) / 100 : null;
|
|
4350
|
+
}
|
|
4351
|
+
function parseActivityRows(activity) {
|
|
4352
|
+
if (!activity || typeof activity !== "object") return [];
|
|
4353
|
+
const data = activity.data;
|
|
4354
|
+
return Array.isArray(data) ? data : [];
|
|
4355
|
+
}
|
|
4342
4356
|
async function collectOpenRouter(key) {
|
|
4343
|
-
const
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4357
|
+
const warnings = [];
|
|
4358
|
+
let credits = null;
|
|
4359
|
+
try {
|
|
4360
|
+
credits = await get("/credits", key);
|
|
4361
|
+
} catch (e) {
|
|
4362
|
+
warnings.push(
|
|
4363
|
+
`/credits failed (${e.message}) \u2014 all-time spend unknown; tokens still from last 30 days`
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4366
|
+
let activity;
|
|
4367
|
+
try {
|
|
4368
|
+
activity = await get("/activity", key);
|
|
4369
|
+
} catch (e) {
|
|
4370
|
+
throw new Error(
|
|
4371
|
+
`OpenRouter /activity failed (${e.message}). Need a management API key with activity access.`
|
|
4372
|
+
);
|
|
4373
|
+
}
|
|
4374
|
+
const totalCostUsd = parseCostUsd(credits);
|
|
4375
|
+
if (credits != null && totalCostUsd == null) {
|
|
4376
|
+
warnings.push("/credits returned an unexpected shape \u2014 spend left unknown");
|
|
4377
|
+
}
|
|
4378
|
+
const rows = parseActivityRows(activity);
|
|
4349
4379
|
let totalTokens = 0;
|
|
4350
4380
|
let requestCount = 0;
|
|
4351
4381
|
const activeDates = /* @__PURE__ */ new Set();
|
|
4352
4382
|
const models2 = /* @__PURE__ */ new Set();
|
|
4353
4383
|
for (const row of rows) {
|
|
4354
|
-
totalTokens += (row.prompt_tokens
|
|
4355
|
-
requestCount += row.requests
|
|
4384
|
+
totalTokens += (Number(row.prompt_tokens) || 0) + (Number(row.completion_tokens) || 0) + (Number(row.reasoning_tokens) || 0);
|
|
4385
|
+
requestCount += Number(row.requests) || 0;
|
|
4356
4386
|
if (typeof row.date === "string") {
|
|
4357
4387
|
const d = row.date.slice(0, 10);
|
|
4358
4388
|
if (/^\d{4}-\d{2}-\d{2}$/.test(d)) activeDates.add(d);
|
|
4359
4389
|
}
|
|
4360
4390
|
if (typeof row.model === "string" && row.model) models2.add(row.model);
|
|
4361
|
-
if (typeof row.model_permaslug === "string" && row.model_permaslug)
|
|
4391
|
+
else if (typeof row.model_permaslug === "string" && row.model_permaslug)
|
|
4362
4392
|
models2.add(row.model_permaslug);
|
|
4363
4393
|
}
|
|
4364
|
-
if (totalTokens === 0 &&
|
|
4394
|
+
if (totalTokens === 0 && requestCount === 0 && totalCostUsd == null) {
|
|
4365
4395
|
return null;
|
|
4366
4396
|
}
|
|
4367
4397
|
return {
|
|
4368
4398
|
source: {
|
|
4369
4399
|
totalTokens: Math.round(totalTokens),
|
|
4370
|
-
totalCostUsd
|
|
4400
|
+
totalCostUsd,
|
|
4371
4401
|
requestCount,
|
|
4372
4402
|
activeDays: activeDates.size,
|
|
4373
4403
|
windowDays: 30,
|
|
4374
4404
|
models: [...models2].sort().slice(0, 50)
|
|
4375
4405
|
},
|
|
4376
|
-
activeDates
|
|
4406
|
+
activeDates,
|
|
4407
|
+
warnings
|
|
4377
4408
|
};
|
|
4378
4409
|
}
|
|
4379
4410
|
|
|
@@ -4629,7 +4660,7 @@ async function collectCursorApi(cookie, onProgress) {
|
|
|
4629
4660
|
}
|
|
4630
4661
|
|
|
4631
4662
|
// src/merge.ts
|
|
4632
|
-
var CLI_VERSION = "0.1.
|
|
4663
|
+
var CLI_VERSION = "0.1.1";
|
|
4633
4664
|
function buildPayload(opts) {
|
|
4634
4665
|
const { claudeCode, openrouter, cursor, handle } = opts;
|
|
4635
4666
|
const allDates = /* @__PURE__ */ new Set();
|
|
@@ -4639,7 +4670,6 @@ function buildPayload(opts) {
|
|
|
4639
4670
|
const totalTokens = (claudeCode?.source.totalTokens ?? 0) + (openrouter?.source.totalTokens ?? 0) + (cursor?.source.totalTokens ?? 0);
|
|
4640
4671
|
const costs = [
|
|
4641
4672
|
claudeCode?.source.estimatedCostUsd,
|
|
4642
|
-
openrouter?.source.totalCostUsd,
|
|
4643
4673
|
cursor?.source.totalCostUsd
|
|
4644
4674
|
].filter((c) => typeof c === "number");
|
|
4645
4675
|
const totalCostUsd = costs.length > 0 ? Math.round(costs.reduce((a, b) => a + b, 0) * 100) / 100 : null;
|
|
@@ -4775,12 +4805,27 @@ Options:
|
|
|
4775
4805
|
s2.start("Validating OpenRouter key");
|
|
4776
4806
|
if (await validateOpenRouterKey(key)) {
|
|
4777
4807
|
s2.message("Fetching OpenRouter usage");
|
|
4778
|
-
|
|
4808
|
+
try {
|
|
4809
|
+
openrouter = await collectOpenRouter(key);
|
|
4810
|
+
for (const w of openrouter?.warnings ?? []) {
|
|
4811
|
+
p.log.warn(`OpenRouter: ${w}`);
|
|
4812
|
+
}
|
|
4813
|
+
if (openrouter) {
|
|
4814
|
+
const spend = openrouter.source.totalCostUsd != null ? `$${openrouter.source.totalCostUsd} all-time spend (not rolled into card total)` : "spend unknown";
|
|
4815
|
+
s2.stop(
|
|
4816
|
+
`OpenRouter: ${fmt(openrouter.source.totalTokens)} tokens (last 30 days), ${spend}`
|
|
4817
|
+
);
|
|
4818
|
+
} else {
|
|
4819
|
+
s2.stop("OpenRouter: no usage found \u2014 skipping");
|
|
4820
|
+
}
|
|
4821
|
+
} catch (e) {
|
|
4822
|
+
openrouter = null;
|
|
4823
|
+
s2.stop(`OpenRouter: ${e.message} \u2014 skipping`);
|
|
4824
|
+
}
|
|
4825
|
+
} else {
|
|
4779
4826
|
s2.stop(
|
|
4780
|
-
|
|
4827
|
+
"OpenRouter: key invalid or not a management key \u2014 skipping"
|
|
4781
4828
|
);
|
|
4782
|
-
} else {
|
|
4783
|
-
s2.stop("OpenRouter: key invalid \u2014 skipping");
|
|
4784
4829
|
}
|
|
4785
4830
|
}
|
|
4786
4831
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aieracard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Generate a shareable card from your AI tool usage. Local parsing, aggregate numbers only.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"aieracard": "
|
|
7
|
+
"aieracard": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
11
|
"README.md"
|
|
12
12
|
],
|
|
13
13
|
"license": "MIT",
|
|
14
|
+
"homepage": "https://ai-era-card.vercel.app",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/sinsmilex/ai-era-card.git",
|
|
18
|
+
"directory": "apps/cli"
|
|
19
|
+
},
|
|
20
|
+
"bugs": "https://github.com/sinsmilex/ai-era-card/issues",
|
|
14
21
|
"keywords": [
|
|
15
22
|
"ai",
|
|
16
23
|
"usage",
|
|
@@ -27,7 +34,8 @@
|
|
|
27
34
|
"scripts": {
|
|
28
35
|
"build": "tsup",
|
|
29
36
|
"start": "node dist/index.js",
|
|
30
|
-
"dev": "tsup --watch"
|
|
37
|
+
"dev": "tsup --watch",
|
|
38
|
+
"test": "vitest run"
|
|
31
39
|
},
|
|
32
40
|
"dependencies": {
|
|
33
41
|
"@clack/prompts": "^0.11.0",
|
|
@@ -37,6 +45,7 @@
|
|
|
37
45
|
"@aieracard/schema": "workspace:*",
|
|
38
46
|
"@types/node": "^22",
|
|
39
47
|
"tsup": "^8.3.5",
|
|
40
|
-
"typescript": "^5.6.3"
|
|
48
|
+
"typescript": "^5.6.3",
|
|
49
|
+
"vitest": "^2.1.9"
|
|
41
50
|
}
|
|
42
51
|
}
|