@kairyou/agent-tools 0.10.0 → 0.10.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 +6 -4
- package/README.zh-CN.md +6 -4
- package/dist/usage/core.mjs +49 -4
- package/integrations/usage/lib/format.mjs +56 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -153,21 +153,23 @@ Output examples:
|
|
|
153
153
|
|
|
154
154
|
```text
|
|
155
155
|
# Plan limits (sub2api / openai-compatible).
|
|
156
|
-
D $0.0/$100 | W $0.0/$300 | Exp 07-08
|
|
156
|
+
D $0.0/$100 | W $0.0/$300 ⟳3d1h | Exp 07-08
|
|
157
157
|
|
|
158
158
|
# Multi-window limits (claude-code-hub).
|
|
159
159
|
5h $2.1/$10.0 | D $8.0/$20.0 | T $19.0/$100 | Exp 08-31
|
|
160
160
|
|
|
161
161
|
# Balance and usage (one-api / one-hub / done-hub / new-api / openrouter).
|
|
162
|
-
balance $15.0 | used $5.0/$20.0
|
|
162
|
+
balance $15.0 | used $5.0/$20.0 | ⟳3d1h
|
|
163
163
|
|
|
164
164
|
# Wallet and recent spend (sub2api).
|
|
165
165
|
balance $362 | today $61.7 | 30d $566
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
Fields: `5h/D/W/M/T` are five-hour/daily/weekly/monthly/total spend against
|
|
169
|
-
limits;
|
|
170
|
-
credit; `today` and `30d` are
|
|
169
|
+
limits; `⟳` is the time until a known limit reset; `Exp` is the plan expiry;
|
|
170
|
+
`balance` is wallet credit; `used` is consumed credit; `today` and `30d` are
|
|
171
|
+
today's and the last 30 days' API spend. A reset countdown is shown only when
|
|
172
|
+
the gateway returns enough timing information to determine it reliably.
|
|
171
173
|
|
|
172
174
|
#### Custom gateway routes
|
|
173
175
|
|
package/README.zh-CN.md
CHANGED
|
@@ -149,20 +149,22 @@ npx -y @kairyou/agent-tools@latest usage -a claude codex opencode
|
|
|
149
149
|
|
|
150
150
|
```text
|
|
151
151
|
# 套餐限额 (sub2api / openai-compatible).
|
|
152
|
-
D $0.0/$100 | W $0.0/$300 | Exp 07-08
|
|
152
|
+
D $0.0/$100 | W $0.0/$300 ⟳3d1h | Exp 07-08
|
|
153
153
|
|
|
154
154
|
# 多窗口限额 (claude-code-hub).
|
|
155
155
|
5h $2.1/$10.0 | D $8.0/$20.0 | T $19.0/$100 | Exp 08-31
|
|
156
156
|
|
|
157
157
|
# 余额与用量 (one-api / one-hub / done-hub / new-api / openrouter).
|
|
158
|
-
balance $15.0 | used $5.0/$20.0
|
|
158
|
+
balance $15.0 | used $5.0/$20.0 | ⟳3d1h
|
|
159
159
|
|
|
160
160
|
# 钱包与近期消耗 (sub2api).
|
|
161
161
|
balance $362 | today $61.7 | 30d $566
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
字段含义: `5h/D/W/M/T` 是 5 小时/日/周/月/总消耗与上限,
|
|
165
|
-
|
|
164
|
+
字段含义: `5h/D/W/M/T` 是 5 小时/日/周/月/总消耗与上限, `⟳` 后面是已知限额的
|
|
165
|
+
重置倒计时, `Exp` 是套餐到期日, `balance` 是钱包余额, `used` 是已用额度,
|
|
166
|
+
`today` / `30d` 是今日与近 30 天 API 消耗. 只有网关返回的信息足以可靠确定
|
|
167
|
+
重置时间时才会显示倒计时.
|
|
166
168
|
|
|
167
169
|
#### 自定义网关路由
|
|
168
170
|
|
package/dist/usage/core.mjs
CHANGED
|
@@ -1309,6 +1309,14 @@ async function requestJson(url, options = {}) {
|
|
|
1309
1309
|
|
|
1310
1310
|
// integrations/usage/lib/format.mjs
|
|
1311
1311
|
var ONE_API_HARD_LIMIT_SENTINEL_USD = 1e6;
|
|
1312
|
+
var RESET_TIME_FIELDS = Object.freeze({
|
|
1313
|
+
// Sub2API rate-limit entries returned alongside quota usage.
|
|
1314
|
+
SUB2API_RATE_LIMIT: "reset_at",
|
|
1315
|
+
// Sub2API /v1/usage response: subscription weekly-window anchor.
|
|
1316
|
+
SUB2API_SUBSCRIPTION_WEEKLY_START: "weekly_window_start",
|
|
1317
|
+
// OpenRouter /api/v1/key response: absolute key-limit reset.
|
|
1318
|
+
OPENROUTER_KEY_LIMIT: "limit_reset"
|
|
1319
|
+
});
|
|
1312
1320
|
function pickNumber(obj, keys) {
|
|
1313
1321
|
for (const key of keys) {
|
|
1314
1322
|
const value = obj?.[key];
|
|
@@ -1337,6 +1345,39 @@ function shortDate(value) {
|
|
|
1337
1345
|
const match = String(value).match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
1338
1346
|
return match ? `${match[2]}-${match[3]}` : "";
|
|
1339
1347
|
}
|
|
1348
|
+
function timestampMs(value) {
|
|
1349
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1350
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
1351
|
+
}
|
|
1352
|
+
const parsed = Date.parse(value);
|
|
1353
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1354
|
+
}
|
|
1355
|
+
function compactDurationUntil(value) {
|
|
1356
|
+
const resetMs = timestampMs(value);
|
|
1357
|
+
if (resetMs === void 0) return "";
|
|
1358
|
+
const remainingMs = resetMs - Date.now();
|
|
1359
|
+
if (remainingMs <= 0) return "";
|
|
1360
|
+
let seconds = Math.floor(remainingMs / 1e3);
|
|
1361
|
+
if (seconds <= 0) return "0m";
|
|
1362
|
+
const days = Math.floor(seconds / 86400);
|
|
1363
|
+
seconds %= 86400;
|
|
1364
|
+
const hours = Math.floor(seconds / 3600);
|
|
1365
|
+
seconds %= 3600;
|
|
1366
|
+
const minutes = Math.floor(seconds / 60);
|
|
1367
|
+
if (days) return `${days}d${hours}h`;
|
|
1368
|
+
if (hours) return `${hours}h${minutes}m`;
|
|
1369
|
+
return `${minutes}m`;
|
|
1370
|
+
}
|
|
1371
|
+
function rateLimitResetAt(entry) {
|
|
1372
|
+
const value = entry?.[RESET_TIME_FIELDS.SUB2API_RATE_LIMIT];
|
|
1373
|
+
return timestampMs(value) === void 0 ? void 0 : value;
|
|
1374
|
+
}
|
|
1375
|
+
function weeklyResetAt(subscription) {
|
|
1376
|
+
const startMs = timestampMs(
|
|
1377
|
+
subscription?.[RESET_TIME_FIELDS.SUB2API_SUBSCRIPTION_WEEKLY_START]
|
|
1378
|
+
);
|
|
1379
|
+
return startMs === void 0 ? void 0 : startMs + 7 * 86400 * 1e3;
|
|
1380
|
+
}
|
|
1340
1381
|
function hasSubscriptionLimits(root) {
|
|
1341
1382
|
const sub = root?.subscription || {};
|
|
1342
1383
|
return [
|
|
@@ -1437,7 +1478,7 @@ function formatOpenRouterLine(data) {
|
|
|
1437
1478
|
const limit = pickNumber(root, ["limit", "limit_remaining", "total_credits"]);
|
|
1438
1479
|
const remaining = pickNumber(root, ["limit_remaining", "remaining_credits"]);
|
|
1439
1480
|
const used = pickNumber(root, ["usage", "total_usage", "spend"]);
|
|
1440
|
-
const reset = root?.
|
|
1481
|
+
const reset = compactDurationUntil(root?.[RESET_TIME_FIELDS.OPENROUTER_KEY_LIMIT]);
|
|
1441
1482
|
const parts = [];
|
|
1442
1483
|
if (remaining !== void 0) parts.push(`balance ${formatMoney(remaining)}`);
|
|
1443
1484
|
if (used !== void 0 && limit !== void 0 && limit !== remaining) {
|
|
@@ -1445,7 +1486,7 @@ function formatOpenRouterLine(data) {
|
|
|
1445
1486
|
} else if (used !== void 0) {
|
|
1446
1487
|
parts.push(`used ${formatMoney(used)}`);
|
|
1447
1488
|
}
|
|
1448
|
-
if (reset) parts.push(
|
|
1489
|
+
if (reset) parts.push(`\u27F3${reset}`);
|
|
1449
1490
|
if (parts.length === 0) throw new Error("OpenRouter payload has no usage fields");
|
|
1450
1491
|
return parts.join(" | ");
|
|
1451
1492
|
}
|
|
@@ -1511,7 +1552,8 @@ function formatQuotaLimitedLine(root) {
|
|
|
1511
1552
|
const window = entry?.window;
|
|
1512
1553
|
const rateLimit = pickNumber(entry, ["limit"]);
|
|
1513
1554
|
const rateUsed = pickNumber(entry, ["used"]);
|
|
1514
|
-
|
|
1555
|
+
const reset = compactDurationUntil(rateLimitResetAt(entry));
|
|
1556
|
+
return window && rateLimit !== void 0 && rateUsed !== void 0 ? `${window} ${formatMoney(rateUsed)}/${formatMoney(rateLimit)}${reset ? ` \u27F3${reset}` : ""}` : "";
|
|
1515
1557
|
}).filter(Boolean);
|
|
1516
1558
|
if (rateParts.length > 0) parts.push(rateParts.join(", "));
|
|
1517
1559
|
}
|
|
@@ -1538,9 +1580,12 @@ function formatUsageLine(root) {
|
|
|
1538
1580
|
const monthlyLimit = pickNumber(sub, ["monthly_limit_usd"]);
|
|
1539
1581
|
const monthlyUsage = pickNumber(sub, ["monthly_usage_usd"]);
|
|
1540
1582
|
const expires = shortDate(sub.expires_at);
|
|
1583
|
+
const weeklyReset = compactDurationUntil(weeklyResetAt(sub));
|
|
1541
1584
|
const parts = [];
|
|
1542
1585
|
if (dailyLimit > 0 && dailyUsage !== void 0) parts.push(`D ${formatMoney(dailyUsage)}/${formatMoney(dailyLimit)}`);
|
|
1543
|
-
if (weeklyLimit > 0 && weeklyUsage !== void 0)
|
|
1586
|
+
if (weeklyLimit > 0 && weeklyUsage !== void 0) {
|
|
1587
|
+
parts.push(`W ${formatMoney(weeklyUsage)}/${formatMoney(weeklyLimit)}${weeklyReset ? ` \u27F3${weeklyReset}` : ""}`);
|
|
1588
|
+
}
|
|
1544
1589
|
if (monthlyLimit > 0 && monthlyUsage !== void 0) parts.push(`M ${formatMoney(monthlyUsage)}/${formatMoney(monthlyLimit)}`);
|
|
1545
1590
|
if (expires) parts.push(`Exp ${expires}`);
|
|
1546
1591
|
return parts.join(" | ");
|
|
@@ -6,6 +6,15 @@ import { newApiQuotaScale, providerUsageDays } from "./config.mjs";
|
|
|
6
6
|
|
|
7
7
|
const ONE_API_HARD_LIMIT_SENTINEL_USD = 1_000_000;
|
|
8
8
|
|
|
9
|
+
const RESET_TIME_FIELDS = Object.freeze({
|
|
10
|
+
// Sub2API rate-limit entries returned alongside quota usage.
|
|
11
|
+
SUB2API_RATE_LIMIT: "reset_at",
|
|
12
|
+
// Sub2API /v1/usage response: subscription weekly-window anchor.
|
|
13
|
+
SUB2API_SUBSCRIPTION_WEEKLY_START: "weekly_window_start",
|
|
14
|
+
// OpenRouter /api/v1/key response: absolute key-limit reset.
|
|
15
|
+
OPENROUTER_KEY_LIMIT: "limit_reset",
|
|
16
|
+
});
|
|
17
|
+
|
|
9
18
|
export function pickNumber(obj, keys) {
|
|
10
19
|
for (const key of keys) {
|
|
11
20
|
const value = obj?.[key];
|
|
@@ -40,6 +49,45 @@ function shortDate(value) {
|
|
|
40
49
|
return match ? `${match[2]}-${match[3]}` : "";
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
function timestampMs(value) {
|
|
53
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
54
|
+
return value < 1e12 ? value * 1000 : value;
|
|
55
|
+
}
|
|
56
|
+
const parsed = Date.parse(value);
|
|
57
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function compactDurationUntil(value) {
|
|
61
|
+
const resetMs = timestampMs(value);
|
|
62
|
+
if (resetMs === undefined) return "";
|
|
63
|
+
|
|
64
|
+
const remainingMs = resetMs - Date.now();
|
|
65
|
+
if (remainingMs <= 0) return "";
|
|
66
|
+
|
|
67
|
+
let seconds = Math.floor(remainingMs / 1000);
|
|
68
|
+
if (seconds <= 0) return "0m";
|
|
69
|
+
const days = Math.floor(seconds / 86400);
|
|
70
|
+
seconds %= 86400;
|
|
71
|
+
const hours = Math.floor(seconds / 3600);
|
|
72
|
+
seconds %= 3600;
|
|
73
|
+
const minutes = Math.floor(seconds / 60);
|
|
74
|
+
if (days) return `${days}d${hours}h`;
|
|
75
|
+
if (hours) return `${hours}h${minutes}m`;
|
|
76
|
+
return `${minutes}m`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function rateLimitResetAt(entry) {
|
|
80
|
+
const value = entry?.[RESET_TIME_FIELDS.SUB2API_RATE_LIMIT];
|
|
81
|
+
return timestampMs(value) === undefined ? undefined : value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function weeklyResetAt(subscription) {
|
|
85
|
+
const startMs = timestampMs(
|
|
86
|
+
subscription?.[RESET_TIME_FIELDS.SUB2API_SUBSCRIPTION_WEEKLY_START]
|
|
87
|
+
);
|
|
88
|
+
return startMs === undefined ? undefined : startMs + 7 * 86400 * 1000;
|
|
89
|
+
}
|
|
90
|
+
|
|
43
91
|
function hasSubscriptionLimits(root) {
|
|
44
92
|
const sub = root?.subscription || {};
|
|
45
93
|
return [
|
|
@@ -163,7 +211,7 @@ export function formatOpenRouterLine(data) {
|
|
|
163
211
|
const limit = pickNumber(root, ["limit", "limit_remaining", "total_credits"]);
|
|
164
212
|
const remaining = pickNumber(root, ["limit_remaining", "remaining_credits"]);
|
|
165
213
|
const used = pickNumber(root, ["usage", "total_usage", "spend"]);
|
|
166
|
-
const reset = root?.
|
|
214
|
+
const reset = compactDurationUntil(root?.[RESET_TIME_FIELDS.OPENROUTER_KEY_LIMIT]);
|
|
167
215
|
const parts = [];
|
|
168
216
|
|
|
169
217
|
if (remaining !== undefined) parts.push(`balance ${formatMoney(remaining)}`);
|
|
@@ -172,7 +220,7 @@ export function formatOpenRouterLine(data) {
|
|
|
172
220
|
} else if (used !== undefined) {
|
|
173
221
|
parts.push(`used ${formatMoney(used)}`);
|
|
174
222
|
}
|
|
175
|
-
if (reset) parts.push(
|
|
223
|
+
if (reset) parts.push(`⟳${reset}`);
|
|
176
224
|
|
|
177
225
|
if (parts.length === 0) throw new Error("OpenRouter payload has no usage fields");
|
|
178
226
|
return parts.join(" | ");
|
|
@@ -254,8 +302,9 @@ function formatQuotaLimitedLine(root) {
|
|
|
254
302
|
const window = entry?.window;
|
|
255
303
|
const rateLimit = pickNumber(entry, ["limit"]);
|
|
256
304
|
const rateUsed = pickNumber(entry, ["used"]);
|
|
305
|
+
const reset = compactDurationUntil(rateLimitResetAt(entry));
|
|
257
306
|
return window && rateLimit !== undefined && rateUsed !== undefined
|
|
258
|
-
? `${window} ${formatMoney(rateUsed)}/${formatMoney(rateLimit)}`
|
|
307
|
+
? `${window} ${formatMoney(rateUsed)}/${formatMoney(rateLimit)}${reset ? ` ⟳${reset}` : ""}`
|
|
259
308
|
: "";
|
|
260
309
|
})
|
|
261
310
|
.filter(Boolean);
|
|
@@ -290,10 +339,13 @@ function formatUsageLine(root) {
|
|
|
290
339
|
const monthlyLimit = pickNumber(sub, ["monthly_limit_usd"]);
|
|
291
340
|
const monthlyUsage = pickNumber(sub, ["monthly_usage_usd"]);
|
|
292
341
|
const expires = shortDate(sub.expires_at);
|
|
342
|
+
const weeklyReset = compactDurationUntil(weeklyResetAt(sub));
|
|
293
343
|
|
|
294
344
|
const parts = [];
|
|
295
345
|
if (dailyLimit > 0 && dailyUsage !== undefined) parts.push(`D ${formatMoney(dailyUsage)}/${formatMoney(dailyLimit)}`);
|
|
296
|
-
if (weeklyLimit > 0 && weeklyUsage !== undefined)
|
|
346
|
+
if (weeklyLimit > 0 && weeklyUsage !== undefined) {
|
|
347
|
+
parts.push(`W ${formatMoney(weeklyUsage)}/${formatMoney(weeklyLimit)}${weeklyReset ? ` ⟳${weeklyReset}` : ""}`);
|
|
348
|
+
}
|
|
297
349
|
if (monthlyLimit > 0 && monthlyUsage !== undefined) parts.push(`M ${formatMoney(monthlyUsage)}/${formatMoney(monthlyLimit)}`);
|
|
298
350
|
if (expires) parts.push(`Exp ${expires}`);
|
|
299
351
|
return parts.join(" | ");
|
package/package.json
CHANGED