@kairyou/agent-tools 0.7.4 → 0.8.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 +4 -4
- package/README.zh-CN.md +4 -4
- package/dist/usage/core.mjs +24 -27
- package/dist/usage/opencode-plugin.mjs +1 -4
- package/dist/usage/opencode-tui.mjs +1 -4
- package/integrations/usage/lib/format.mjs +23 -25
- package/integrations/usage/lib/routes.mjs +3 -3
- package/integrations/usage/opencode-plugin.mjs +1 -5
- package/integrations/usage/opencode-tui.mjs +1 -5
- package/package.json +3 -2
- package/scripts/publish.mjs +108 -0
- package/scripts/release.mjs +122 -29
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ The installer writes `statusLine` to `~/.claude/settings.json`. Example output:
|
|
|
97
97
|
⎇ main | Opus 4.8 | 5h 7% ⟳2h54m | w 41% ⟳3d1h
|
|
98
98
|
|
|
99
99
|
# With a compatible API relay, its quota info is shown instead, e.g.:
|
|
100
|
-
⎇ main | Opus 4.8 |
|
|
100
|
+
⎇ main | Opus 4.8 | balance $362 | today $61.7 | 30d $566
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Here `5h` and `w` are Claude's rolling usage windows and `⟳` is the reset
|
|
@@ -153,10 +153,10 @@ Output examples:
|
|
|
153
153
|
|
|
154
154
|
```text
|
|
155
155
|
# Relay plan quota.
|
|
156
|
-
|
|
156
|
+
D $0.0/$100 | W $0.0/$300 | Exp 07-08
|
|
157
157
|
|
|
158
158
|
# Wallet balance.
|
|
159
|
-
|
|
159
|
+
balance $362 | today $61.7 | 30d $566
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
Fields: `D/W/M` are daily/weekly/monthly spend against plan limits; `Exp` is
|
|
@@ -203,7 +203,7 @@ export async function run(context, { requestJson, agentConfig }) {
|
|
|
203
203
|
const me = await requestJson(`${context.baseUrl}/api/user/self`, {
|
|
204
204
|
headers: { authorization: `Bearer ${session?.data?.accessToken}` },
|
|
205
205
|
});
|
|
206
|
-
return { text: `
|
|
206
|
+
return { text: `balance ¥${me?.data?.balance}` };
|
|
207
207
|
}
|
|
208
208
|
```
|
|
209
209
|
|
package/README.zh-CN.md
CHANGED
|
@@ -94,7 +94,7 @@ npx -y @kairyou/agent-tools@latest statusline -a claude
|
|
|
94
94
|
⎇ main | Opus 4.8 | 5h 7% ⟳2h54m | w 41% ⟳3d1h
|
|
95
95
|
|
|
96
96
|
# 使用兼容的 API 中转时, 显示中转的额度信息, 比如:
|
|
97
|
-
⎇ main | Opus 4.8 |
|
|
97
|
+
⎇ main | Opus 4.8 | balance $362 | today $61.7 | 30d $566
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
其中 `5h` / `w` 是 Claude 的滚动用量窗口, `⟳` 后面是重置倒计时;
|
|
@@ -144,10 +144,10 @@ provider 的 `base_url` 和密钥; Claude Code: 读取 `ANTHROPIC_BASE_URL` 与
|
|
|
144
144
|
|
|
145
145
|
```text
|
|
146
146
|
# 中转套餐额度.
|
|
147
|
-
|
|
147
|
+
D $0.0/$100 | W $0.0/$300 | Exp 07-08
|
|
148
148
|
|
|
149
149
|
# 钱包余额.
|
|
150
|
-
|
|
150
|
+
balance $362 | today $61.7 | 30d $566
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
字段含义: `D/W/M` 是日/周/月套餐消耗与上限, `Exp` 是套餐到期日,
|
|
@@ -192,7 +192,7 @@ export async function run(context, { requestJson, agentConfig }) {
|
|
|
192
192
|
const me = await requestJson(`${context.baseUrl}/api/user/self`, {
|
|
193
193
|
headers: { authorization: `Bearer ${session?.data?.accessToken}` },
|
|
194
194
|
});
|
|
195
|
-
return { text: `
|
|
195
|
+
return { text: `balance ¥${me?.data?.balance}` };
|
|
196
196
|
}
|
|
197
197
|
```
|
|
198
198
|
|
package/dist/usage/core.mjs
CHANGED
|
@@ -1276,9 +1276,6 @@ function formatMaybeMoney(value, unit = "USD") {
|
|
|
1276
1276
|
if (unit === "USD" || unit === "$") return formatMoney(value);
|
|
1277
1277
|
return `${value.toLocaleString("en-US", { maximumFractionDigits: 1 })} ${unit}`;
|
|
1278
1278
|
}
|
|
1279
|
-
function usageParts() {
|
|
1280
|
-
return ["API"];
|
|
1281
|
-
}
|
|
1282
1279
|
async function formatNewApiQuota(value) {
|
|
1283
1280
|
const scale = await newApiQuotaScale();
|
|
1284
1281
|
if (Number.isFinite(scale) && scale > 0) return formatMoney(value / scale);
|
|
@@ -1328,26 +1325,26 @@ function hasV1UsageFields(root) {
|
|
|
1328
1325
|
"usage"
|
|
1329
1326
|
]) !== void 0 || pickNumber(root?.quota, ["limit", "quota", "used", "quota_used", "remaining"]) !== void 0 || pickNumber(root?.usage?.today, ["actual_cost", "cost"]) !== void 0 || Array.isArray(root?.daily_usage) && root.daily_usage.length > 0;
|
|
1330
1327
|
}
|
|
1331
|
-
async function formatQuota(
|
|
1328
|
+
async function formatQuota(data) {
|
|
1332
1329
|
const root = usageRoot(data);
|
|
1333
1330
|
const unit = root?.unit || "USD";
|
|
1334
1331
|
const remaining = pickNumber(root, ["remaining"]);
|
|
1335
1332
|
const hardLimit = pickNumber(root, ["hard_limit_usd", "hard_limit", "total_granted", "quota"]);
|
|
1336
1333
|
const used = pickNumber(root, ["total_usage", "used", "usage"]);
|
|
1337
1334
|
const balance = pickNumber(root, ["balance", "remaining", "remain", "available"]);
|
|
1338
|
-
if (isQuotaLimitedUsage(root)) return formatQuotaLimitedLine(
|
|
1339
|
-
if (isSubscriptionUsage(root)) return formatUsageLine(
|
|
1340
|
-
if (isWalletUsage(root)) return await formatWalletLine(
|
|
1341
|
-
if (remaining !== void 0) return formatUsageLine(
|
|
1342
|
-
if (balance !== void 0) return `
|
|
1335
|
+
if (isQuotaLimitedUsage(root)) return formatQuotaLimitedLine(root);
|
|
1336
|
+
if (isSubscriptionUsage(root)) return formatUsageLine(root);
|
|
1337
|
+
if (isWalletUsage(root)) return await formatWalletLine(root);
|
|
1338
|
+
if (remaining !== void 0) return formatUsageLine(root);
|
|
1339
|
+
if (balance !== void 0) return `balance ${formatMaybeMoney(balance, unit)}`;
|
|
1343
1340
|
if (hardLimit !== void 0 && used !== void 0) {
|
|
1344
|
-
return `
|
|
1341
|
+
return `remaining ${formatMaybeMoney(Math.max(0, hardLimit - used), unit)}`;
|
|
1345
1342
|
}
|
|
1346
|
-
if (hardLimit !== void 0) return `
|
|
1343
|
+
if (hardLimit !== void 0) return `total ${formatMaybeMoney(hardLimit, unit)}`;
|
|
1347
1344
|
const keys = Object.keys(root || {}).slice(0, 4).join(", ");
|
|
1348
|
-
return keys ? `
|
|
1345
|
+
return keys ? `received (${keys})` : `checked ${unit}`;
|
|
1349
1346
|
}
|
|
1350
|
-
async function formatNewApiTokenLine(
|
|
1347
|
+
async function formatNewApiTokenLine(data) {
|
|
1351
1348
|
const root = usageRoot(data);
|
|
1352
1349
|
const unlimited = root?.unlimited_quota === true || root?.unlimitedQuota === true;
|
|
1353
1350
|
const quota = pickNumber(root, ["quota", "limit", "total_quota", "totalQuota"]);
|
|
@@ -1359,7 +1356,7 @@ async function formatNewApiTokenLine(label, data) {
|
|
|
1359
1356
|
if (!unlimited && quota === void 0 && used === void 0 && remaining === void 0) {
|
|
1360
1357
|
throw new Error("NewAPI token usage payload has no quota fields");
|
|
1361
1358
|
}
|
|
1362
|
-
const parts =
|
|
1359
|
+
const parts = [];
|
|
1363
1360
|
if (unlimited) parts.push("unlimited");
|
|
1364
1361
|
if (remaining !== void 0) parts.push(`balance ${await formatNewApiQuota(remaining)}`);
|
|
1365
1362
|
if (used !== void 0 && quota !== void 0) {
|
|
@@ -1369,13 +1366,13 @@ async function formatNewApiTokenLine(label, data) {
|
|
|
1369
1366
|
}
|
|
1370
1367
|
return parts.join(" | ");
|
|
1371
1368
|
}
|
|
1372
|
-
function formatOpenRouterLine(
|
|
1369
|
+
function formatOpenRouterLine(data) {
|
|
1373
1370
|
const root = usageRoot(data);
|
|
1374
1371
|
const limit = pickNumber(root, ["limit", "limit_remaining", "total_credits"]);
|
|
1375
1372
|
const remaining = pickNumber(root, ["limit_remaining", "remaining_credits"]);
|
|
1376
1373
|
const used = pickNumber(root, ["usage", "total_usage", "spend"]);
|
|
1377
1374
|
const reset = root?.limit_reset || root?.reset_at ? shortDate(root.limit_reset || root.reset_at) : "";
|
|
1378
|
-
const parts =
|
|
1375
|
+
const parts = [];
|
|
1379
1376
|
if (remaining !== void 0) parts.push(`balance ${formatMoney(remaining)}`);
|
|
1380
1377
|
if (used !== void 0 && limit !== void 0 && limit !== remaining) {
|
|
1381
1378
|
parts.push(`used ${formatMoney(used)}/${formatMoney(limit)}`);
|
|
@@ -1383,18 +1380,18 @@ function formatOpenRouterLine(label, data) {
|
|
|
1383
1380
|
parts.push(`used ${formatMoney(used)}`);
|
|
1384
1381
|
}
|
|
1385
1382
|
if (reset) parts.push(`Reset ${reset}`);
|
|
1386
|
-
if (parts.length ===
|
|
1383
|
+
if (parts.length === 0) throw new Error("OpenRouter payload has no usage fields");
|
|
1387
1384
|
return parts.join(" | ");
|
|
1388
1385
|
}
|
|
1389
1386
|
function formatOneApiBillingLine(limit, used) {
|
|
1390
|
-
return `
|
|
1387
|
+
return `balance ${formatMoney(Math.max(0, limit - used))} | used ${formatMoney(used)}/${formatMoney(limit)}`;
|
|
1391
1388
|
}
|
|
1392
|
-
function formatQuotaLimitedLine(
|
|
1389
|
+
function formatQuotaLimitedLine(root) {
|
|
1393
1390
|
const quota = root?.quota || {};
|
|
1394
1391
|
const limit = pickNumber(quota, ["limit", "quota"]);
|
|
1395
1392
|
const used = pickNumber(quota, ["used", "quota_used"]);
|
|
1396
1393
|
const remaining = pickNumber(quota, ["remaining"]) ?? pickNumber(root, ["remaining"]);
|
|
1397
|
-
const parts =
|
|
1394
|
+
const parts = [];
|
|
1398
1395
|
if (limit !== void 0 && used !== void 0) {
|
|
1399
1396
|
parts.push(`Q ${formatMoney(used)}/${formatMoney(limit)}`);
|
|
1400
1397
|
} else if (remaining !== void 0) {
|
|
@@ -1411,11 +1408,11 @@ function formatQuotaLimitedLine(label, root) {
|
|
|
1411
1408
|
}
|
|
1412
1409
|
return parts.join(" | ");
|
|
1413
1410
|
}
|
|
1414
|
-
async function formatWalletLine(
|
|
1411
|
+
async function formatWalletLine(root) {
|
|
1415
1412
|
const balance = pickNumber(root, ["balance", "remaining", "remain", "available"]);
|
|
1416
1413
|
const todayCost = pickNumber(root?.usage?.today, ["actual_cost", "cost"]);
|
|
1417
1414
|
const recentUsage = Array.isArray(root?.daily_usage) ? root.daily_usage.reduce((sum, day) => sum + (pickNumber(day, ["actual_cost", "cost"]) || 0), 0) : void 0;
|
|
1418
|
-
const parts =
|
|
1415
|
+
const parts = [];
|
|
1419
1416
|
if (balance !== void 0) parts.push(`balance ${formatMoney(balance)}`);
|
|
1420
1417
|
if (todayCost !== void 0) parts.push(`today ${formatMoney(todayCost)}`);
|
|
1421
1418
|
if (recentUsage !== void 0 && root.daily_usage.length > 0) {
|
|
@@ -1423,7 +1420,7 @@ async function formatWalletLine(label, root) {
|
|
|
1423
1420
|
}
|
|
1424
1421
|
return parts.join(" | ");
|
|
1425
1422
|
}
|
|
1426
|
-
function formatUsageLine(
|
|
1423
|
+
function formatUsageLine(root) {
|
|
1427
1424
|
const sub = root?.subscription || {};
|
|
1428
1425
|
const dailyLimit = pickNumber(sub, ["daily_limit_usd"]);
|
|
1429
1426
|
const dailyUsage = pickNumber(sub, ["daily_usage_usd"]);
|
|
@@ -1432,7 +1429,7 @@ function formatUsageLine(label, root) {
|
|
|
1432
1429
|
const monthlyLimit = pickNumber(sub, ["monthly_limit_usd"]);
|
|
1433
1430
|
const monthlyUsage = pickNumber(sub, ["monthly_usage_usd"]);
|
|
1434
1431
|
const expires = shortDate(sub.expires_at);
|
|
1435
|
-
const parts =
|
|
1432
|
+
const parts = [];
|
|
1436
1433
|
if (dailyLimit > 0 && dailyUsage !== void 0) parts.push(`D ${formatMoney(dailyUsage)}/${formatMoney(dailyLimit)}`);
|
|
1437
1434
|
if (weeklyLimit > 0 && weeklyUsage !== void 0) parts.push(`W ${formatMoney(weeklyUsage)}/${formatMoney(weeklyLimit)}`);
|
|
1438
1435
|
if (monthlyLimit > 0 && monthlyUsage !== void 0) parts.push(`M ${formatMoney(monthlyUsage)}/${formatMoney(monthlyLimit)}`);
|
|
@@ -1462,7 +1459,7 @@ async function fetchV1Usage(context) {
|
|
|
1462
1459
|
name: "v1 usage"
|
|
1463
1460
|
});
|
|
1464
1461
|
if (!hasV1UsageFields(usageRoot(json))) throw new Error("v1 usage payload has no usage fields");
|
|
1465
|
-
return usageResult(context, "v1-usage", await formatQuota(
|
|
1462
|
+
return usageResult(context, "v1-usage", await formatQuota(json), json);
|
|
1466
1463
|
}
|
|
1467
1464
|
async function fetchNewApiTokenUsage(context) {
|
|
1468
1465
|
const json = await requestJson(joinUrl(serviceRoot(context.baseUrl), "/api/usage/token/"), {
|
|
@@ -1491,7 +1488,7 @@ async function fetchNewApiTokenUsage(context) {
|
|
|
1491
1488
|
source: "newapi-token",
|
|
1492
1489
|
raw: json
|
|
1493
1490
|
};
|
|
1494
|
-
return usageResult(context, "newapi-token", await formatNewApiTokenLine(
|
|
1491
|
+
return usageResult(context, "newapi-token", await formatNewApiTokenLine(json), normalized);
|
|
1495
1492
|
}
|
|
1496
1493
|
async function fetchOneApiBillingUsage(context) {
|
|
1497
1494
|
const base = serviceRoot(context.baseUrl);
|
|
@@ -1537,7 +1534,7 @@ async function fetchOpenRouterUsage(context) {
|
|
|
1537
1534
|
for (const endpoint of endpoints) {
|
|
1538
1535
|
try {
|
|
1539
1536
|
const json = await requestJson(endpoint.url, { key: context.key, name: endpoint.source });
|
|
1540
|
-
return usageResult(context, endpoint.source, formatOpenRouterLine(
|
|
1537
|
+
return usageResult(context, endpoint.source, formatOpenRouterLine(json), json);
|
|
1541
1538
|
} catch (error) {
|
|
1542
1539
|
lastError = error;
|
|
1543
1540
|
await debugLog({ source: endpoint.source, error: error.message });
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// integrations/usage/opencode-plugin.mjs
|
|
2
2
|
import { queryProviderUsage } from "./core.mjs";
|
|
3
3
|
var DEFAULT_REFRESH_MS = 6e4;
|
|
4
|
-
function toastMessage(text) {
|
|
5
|
-
return text.replace(/^API \| /, "");
|
|
6
|
-
}
|
|
7
4
|
function firstString(value, keys) {
|
|
8
5
|
for (const key of keys) {
|
|
9
6
|
if (typeof value?.[key] === "string" && value[key].trim()) return value[key].trim();
|
|
@@ -38,7 +35,7 @@ var AgentToolsUsage = async ({ client }, options = {}) => {
|
|
|
38
35
|
await client.tui.showToast({
|
|
39
36
|
body: {
|
|
40
37
|
title: "Provider usage",
|
|
41
|
-
message:
|
|
38
|
+
message: text,
|
|
42
39
|
variant: "info",
|
|
43
40
|
duration: 8e3
|
|
44
41
|
}
|
|
@@ -4,9 +4,6 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
var AGENT_TOOLS_HOME = process.env.AGENT_TOOLS_HOME || join(homedir(), ".agent-tools");
|
|
6
6
|
var SNAPSHOT_PATH = join(AGENT_TOOLS_HOME, "cache", "usage-snapshot.json");
|
|
7
|
-
function toastMessage(text) {
|
|
8
|
-
return text.replace(/^API \| /, "");
|
|
9
|
-
}
|
|
10
7
|
function latestUsage() {
|
|
11
8
|
try {
|
|
12
9
|
const data = JSON.parse(readFileSync(SNAPSHOT_PATH, "utf8"));
|
|
@@ -28,7 +25,7 @@ var tui = async (api) => {
|
|
|
28
25
|
const message = latestUsage();
|
|
29
26
|
api.ui.toast({
|
|
30
27
|
title: "Provider usage",
|
|
31
|
-
message: message
|
|
28
|
+
message: message || "Provider usage is not available yet",
|
|
32
29
|
variant: message ? "info" : "warning",
|
|
33
30
|
duration: 8e3
|
|
34
31
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Turns gateway payloads into the compact one-line usage message.
|
|
2
|
+
// Lines carry no branding prefix; each display surface adds its own context
|
|
3
|
+
// (hook name, statusline position, toast title) if it needs any.
|
|
2
4
|
|
|
3
5
|
import { newApiQuotaScale, providerUsageDays } from "./config.mjs";
|
|
4
6
|
|
|
@@ -20,10 +22,6 @@ function formatMaybeMoney(value, unit = "USD") {
|
|
|
20
22
|
return `${value.toLocaleString("en-US", { maximumFractionDigits: 1 })} ${unit}`;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
function usageParts() {
|
|
24
|
-
return ["API"];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
25
|
async function formatNewApiQuota(value) {
|
|
28
26
|
const scale = await newApiQuotaScale();
|
|
29
27
|
if (Number.isFinite(scale) && scale > 0) return formatMoney(value / scale);
|
|
@@ -92,7 +90,7 @@ export function hasV1UsageFields(root) {
|
|
|
92
90
|
);
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
export async function formatQuota(
|
|
93
|
+
export async function formatQuota(data) {
|
|
96
94
|
const root = usageRoot(data);
|
|
97
95
|
const unit = root?.unit || "USD";
|
|
98
96
|
const remaining = pickNumber(root, ["remaining"]);
|
|
@@ -100,21 +98,21 @@ export async function formatQuota(label, data) {
|
|
|
100
98
|
const used = pickNumber(root, ["total_usage", "used", "usage"]);
|
|
101
99
|
const balance = pickNumber(root, ["balance", "remaining", "remain", "available"]);
|
|
102
100
|
|
|
103
|
-
if (isQuotaLimitedUsage(root)) return formatQuotaLimitedLine(
|
|
104
|
-
if (isSubscriptionUsage(root)) return formatUsageLine(
|
|
105
|
-
if (isWalletUsage(root)) return await formatWalletLine(
|
|
106
|
-
if (remaining !== undefined) return formatUsageLine(
|
|
107
|
-
if (balance !== undefined) return `
|
|
101
|
+
if (isQuotaLimitedUsage(root)) return formatQuotaLimitedLine(root);
|
|
102
|
+
if (isSubscriptionUsage(root)) return formatUsageLine(root);
|
|
103
|
+
if (isWalletUsage(root)) return await formatWalletLine(root);
|
|
104
|
+
if (remaining !== undefined) return formatUsageLine(root);
|
|
105
|
+
if (balance !== undefined) return `balance ${formatMaybeMoney(balance, unit)}`;
|
|
108
106
|
if (hardLimit !== undefined && used !== undefined) {
|
|
109
|
-
return `
|
|
107
|
+
return `remaining ${formatMaybeMoney(Math.max(0, hardLimit - used), unit)}`;
|
|
110
108
|
}
|
|
111
|
-
if (hardLimit !== undefined) return `
|
|
109
|
+
if (hardLimit !== undefined) return `total ${formatMaybeMoney(hardLimit, unit)}`;
|
|
112
110
|
|
|
113
111
|
const keys = Object.keys(root || {}).slice(0, 4).join(", ");
|
|
114
|
-
return keys ? `
|
|
112
|
+
return keys ? `received (${keys})` : `checked ${unit}`;
|
|
115
113
|
}
|
|
116
114
|
|
|
117
|
-
export async function formatNewApiTokenLine(
|
|
115
|
+
export async function formatNewApiTokenLine(data) {
|
|
118
116
|
const root = usageRoot(data);
|
|
119
117
|
const unlimited = root?.unlimited_quota === true || root?.unlimitedQuota === true;
|
|
120
118
|
const quota = pickNumber(root, ["quota", "limit", "total_quota", "totalQuota"]);
|
|
@@ -128,7 +126,7 @@ export async function formatNewApiTokenLine(label, data) {
|
|
|
128
126
|
throw new Error("NewAPI token usage payload has no quota fields");
|
|
129
127
|
}
|
|
130
128
|
|
|
131
|
-
const parts =
|
|
129
|
+
const parts = [];
|
|
132
130
|
if (unlimited) parts.push("unlimited");
|
|
133
131
|
if (remaining !== undefined) parts.push(`balance ${await formatNewApiQuota(remaining)}`);
|
|
134
132
|
if (used !== undefined && quota !== undefined) {
|
|
@@ -139,13 +137,13 @@ export async function formatNewApiTokenLine(label, data) {
|
|
|
139
137
|
return parts.join(" | ");
|
|
140
138
|
}
|
|
141
139
|
|
|
142
|
-
export function formatOpenRouterLine(
|
|
140
|
+
export function formatOpenRouterLine(data) {
|
|
143
141
|
const root = usageRoot(data);
|
|
144
142
|
const limit = pickNumber(root, ["limit", "limit_remaining", "total_credits"]);
|
|
145
143
|
const remaining = pickNumber(root, ["limit_remaining", "remaining_credits"]);
|
|
146
144
|
const used = pickNumber(root, ["usage", "total_usage", "spend"]);
|
|
147
145
|
const reset = root?.limit_reset || root?.reset_at ? shortDate(root.limit_reset || root.reset_at) : "";
|
|
148
|
-
const parts =
|
|
146
|
+
const parts = [];
|
|
149
147
|
|
|
150
148
|
if (remaining !== undefined) parts.push(`balance ${formatMoney(remaining)}`);
|
|
151
149
|
if (used !== undefined && limit !== undefined && limit !== remaining) {
|
|
@@ -155,20 +153,20 @@ export function formatOpenRouterLine(label, data) {
|
|
|
155
153
|
}
|
|
156
154
|
if (reset) parts.push(`Reset ${reset}`);
|
|
157
155
|
|
|
158
|
-
if (parts.length ===
|
|
156
|
+
if (parts.length === 0) throw new Error("OpenRouter payload has no usage fields");
|
|
159
157
|
return parts.join(" | ");
|
|
160
158
|
}
|
|
161
159
|
|
|
162
160
|
export function formatOneApiBillingLine(limit, used) {
|
|
163
|
-
return `
|
|
161
|
+
return `balance ${formatMoney(Math.max(0, limit - used))} | used ${formatMoney(used)}/${formatMoney(limit)}`;
|
|
164
162
|
}
|
|
165
163
|
|
|
166
|
-
function formatQuotaLimitedLine(
|
|
164
|
+
function formatQuotaLimitedLine(root) {
|
|
167
165
|
const quota = root?.quota || {};
|
|
168
166
|
const limit = pickNumber(quota, ["limit", "quota"]);
|
|
169
167
|
const used = pickNumber(quota, ["used", "quota_used"]);
|
|
170
168
|
const remaining = pickNumber(quota, ["remaining"]) ?? pickNumber(root, ["remaining"]);
|
|
171
|
-
const parts =
|
|
169
|
+
const parts = [];
|
|
172
170
|
|
|
173
171
|
if (limit !== undefined && used !== undefined) {
|
|
174
172
|
parts.push(`Q ${formatMoney(used)}/${formatMoney(limit)}`);
|
|
@@ -193,14 +191,14 @@ function formatQuotaLimitedLine(label, root) {
|
|
|
193
191
|
return parts.join(" | ");
|
|
194
192
|
}
|
|
195
193
|
|
|
196
|
-
async function formatWalletLine(
|
|
194
|
+
async function formatWalletLine(root) {
|
|
197
195
|
const balance = pickNumber(root, ["balance", "remaining", "remain", "available"]);
|
|
198
196
|
const todayCost = pickNumber(root?.usage?.today, ["actual_cost", "cost"]);
|
|
199
197
|
const recentUsage = Array.isArray(root?.daily_usage)
|
|
200
198
|
? root.daily_usage.reduce((sum, day) => sum + (pickNumber(day, ["actual_cost", "cost"]) || 0), 0)
|
|
201
199
|
: undefined;
|
|
202
200
|
|
|
203
|
-
const parts =
|
|
201
|
+
const parts = [];
|
|
204
202
|
if (balance !== undefined) parts.push(`balance ${formatMoney(balance)}`);
|
|
205
203
|
if (todayCost !== undefined) parts.push(`today ${formatMoney(todayCost)}`);
|
|
206
204
|
if (recentUsage !== undefined && root.daily_usage.length > 0) {
|
|
@@ -209,7 +207,7 @@ async function formatWalletLine(label, root) {
|
|
|
209
207
|
return parts.join(" | ");
|
|
210
208
|
}
|
|
211
209
|
|
|
212
|
-
function formatUsageLine(
|
|
210
|
+
function formatUsageLine(root) {
|
|
213
211
|
const sub = root?.subscription || {};
|
|
214
212
|
const dailyLimit = pickNumber(sub, ["daily_limit_usd"]);
|
|
215
213
|
const dailyUsage = pickNumber(sub, ["daily_usage_usd"]);
|
|
@@ -219,7 +217,7 @@ function formatUsageLine(label, root) {
|
|
|
219
217
|
const monthlyUsage = pickNumber(sub, ["monthly_usage_usd"]);
|
|
220
218
|
const expires = shortDate(sub.expires_at);
|
|
221
219
|
|
|
222
|
-
const parts =
|
|
220
|
+
const parts = [];
|
|
223
221
|
if (dailyLimit > 0 && dailyUsage !== undefined) parts.push(`D ${formatMoney(dailyUsage)}/${formatMoney(dailyLimit)}`);
|
|
224
222
|
if (weeklyLimit > 0 && weeklyUsage !== undefined) parts.push(`W ${formatMoney(weeklyUsage)}/${formatMoney(weeklyLimit)}`);
|
|
225
223
|
if (monthlyLimit > 0 && monthlyUsage !== undefined) parts.push(`M ${formatMoney(monthlyUsage)}/${formatMoney(monthlyLimit)}`);
|
|
@@ -56,7 +56,7 @@ async function fetchV1Usage(context) {
|
|
|
56
56
|
name: "v1 usage",
|
|
57
57
|
});
|
|
58
58
|
if (!hasV1UsageFields(usageRoot(json))) throw new Error("v1 usage payload has no usage fields");
|
|
59
|
-
return usageResult(context, "v1-usage", await formatQuota(
|
|
59
|
+
return usageResult(context, "v1-usage", await formatQuota(json), json);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// New API exposes a read-only usage endpoint authenticated by the same relay
|
|
@@ -88,7 +88,7 @@ async function fetchNewApiTokenUsage(context) {
|
|
|
88
88
|
source: "newapi-token",
|
|
89
89
|
raw: json,
|
|
90
90
|
};
|
|
91
|
-
return usageResult(context, "newapi-token", await formatNewApiTokenLine(
|
|
91
|
+
return usageResult(context, "newapi-token", await formatNewApiTokenLine(json), normalized);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// One API's legacy OpenAI billing endpoints use the same relay API key as
|
|
@@ -143,7 +143,7 @@ async function fetchOpenRouterUsage(context) {
|
|
|
143
143
|
for (const endpoint of endpoints) {
|
|
144
144
|
try {
|
|
145
145
|
const json = await requestJson(endpoint.url, { key: context.key, name: endpoint.source });
|
|
146
|
-
return usageResult(context, endpoint.source, formatOpenRouterLine(
|
|
146
|
+
return usageResult(context, endpoint.source, formatOpenRouterLine(json), json);
|
|
147
147
|
} catch (error) {
|
|
148
148
|
lastError = error;
|
|
149
149
|
await debugLog({ source: endpoint.source, error: error.message });
|
|
@@ -2,10 +2,6 @@ import { queryProviderUsage } from "./core.mjs";
|
|
|
2
2
|
|
|
3
3
|
const DEFAULT_REFRESH_MS = 60_000;
|
|
4
4
|
|
|
5
|
-
function toastMessage(text) {
|
|
6
|
-
return text.replace(/^API \| /, "");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
5
|
function firstString(value, keys) {
|
|
10
6
|
for (const key of keys) {
|
|
11
7
|
if (typeof value?.[key] === "string" && value[key].trim()) return value[key].trim();
|
|
@@ -49,7 +45,7 @@ export const AgentToolsUsage = async ({ client }, options = {}) => {
|
|
|
49
45
|
await client.tui.showToast({
|
|
50
46
|
body: {
|
|
51
47
|
title: "Provider usage",
|
|
52
|
-
message:
|
|
48
|
+
message: text,
|
|
53
49
|
variant: "info",
|
|
54
50
|
duration: 8000,
|
|
55
51
|
},
|
|
@@ -5,10 +5,6 @@ import { join } from "node:path";
|
|
|
5
5
|
const AGENT_TOOLS_HOME = process.env.AGENT_TOOLS_HOME || join(homedir(), ".agent-tools");
|
|
6
6
|
const SNAPSHOT_PATH = join(AGENT_TOOLS_HOME, "cache", "usage-snapshot.json");
|
|
7
7
|
|
|
8
|
-
function toastMessage(text) {
|
|
9
|
-
return text.replace(/^API \| /, "");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
8
|
function latestUsage() {
|
|
13
9
|
try {
|
|
14
10
|
const data = JSON.parse(readFileSync(SNAPSHOT_PATH, "utf8"));
|
|
@@ -33,7 +29,7 @@ const tui = async (api) => {
|
|
|
33
29
|
const message = latestUsage();
|
|
34
30
|
api.ui.toast({
|
|
35
31
|
title: "Provider usage",
|
|
36
|
-
message: message
|
|
32
|
+
message: message || "Provider usage is not available yet",
|
|
37
33
|
variant: message ? "info" : "warning",
|
|
38
34
|
duration: 8000,
|
|
39
35
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kairyou/agent-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Reusable Agent Skills and installable integrations (statusline, provider usage, vision) for Codex, Claude Code, and opencode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"build": "node scripts/build.mjs",
|
|
33
33
|
"prepare": "npm run build",
|
|
34
34
|
"test": "node --test tests/*.test.mjs",
|
|
35
|
-
"release": "node scripts/release.mjs"
|
|
35
|
+
"release": "node scripts/release.mjs --target=registry",
|
|
36
|
+
"release:github": "node scripts/release.mjs --target=github"
|
|
36
37
|
},
|
|
37
38
|
"bin": {
|
|
38
39
|
"agent-tools": "./scripts/install.mjs"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Internal registry publisher used by release.mjs.
|
|
3
|
+
// Publishes to npmjs or an npm-compatible private registry without changing the version.
|
|
4
|
+
// Credentials persist in ~/.agent-tools/publish.npmrc, leaving ~/.npmrc untouched.
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
13
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8"));
|
|
14
|
+
const PACKAGE_NAME = packageJson.name;
|
|
15
|
+
const PUBLISH_NPMRC = path.join(
|
|
16
|
+
process.env.AGENT_TOOLS_HOME || path.join(os.homedir(), ".agent-tools"),
|
|
17
|
+
"publish.npmrc"
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
let registryOverride;
|
|
21
|
+
let authType;
|
|
22
|
+
let dryRun = false;
|
|
23
|
+
let authOnly = false;
|
|
24
|
+
for (const arg of process.argv.slice(2)) {
|
|
25
|
+
if (arg === "--dry-run") dryRun = true;
|
|
26
|
+
else if (arg === "--auth-only") authOnly = true;
|
|
27
|
+
else if (arg.startsWith("--registry=")) registryOverride = arg.slice("--registry=".length);
|
|
28
|
+
else if (arg.startsWith("--auth-type=")) authType = arg.slice("--auth-type=".length);
|
|
29
|
+
else {
|
|
30
|
+
console.error(
|
|
31
|
+
`Unknown argument: ${arg} ` +
|
|
32
|
+
`(expected --registry=<url>, --auth-type=<type>, --auth-only, or --dry-run)`
|
|
33
|
+
);
|
|
34
|
+
process.exit(2);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof PACKAGE_NAME !== "string" || PACKAGE_NAME.trim() === "") {
|
|
39
|
+
console.error("package.json must define a non-empty name before publishing.");
|
|
40
|
+
process.exit(2);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function runNpm(args, { capture = false, allowFailure = false } = {}) {
|
|
44
|
+
const npmExecPath = process.env.npm_execpath;
|
|
45
|
+
const command = npmExecPath ? process.execPath : process.platform === "win32" ? "npm.cmd" : "npm";
|
|
46
|
+
const commandArgs = npmExecPath ? [npmExecPath, ...args] : args;
|
|
47
|
+
const result = spawnSync(command, commandArgs, {
|
|
48
|
+
cwd: ROOT,
|
|
49
|
+
encoding: capture ? "utf8" : undefined,
|
|
50
|
+
stdio: capture ? "pipe" : "inherit",
|
|
51
|
+
});
|
|
52
|
+
if (result.error) throw result.error;
|
|
53
|
+
if (!allowFailure && result.status !== 0) process.exit(result.status ?? 1);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readNpmConfig(key) {
|
|
58
|
+
const result = runNpm(["config", "get", key], { capture: true });
|
|
59
|
+
const value = result.stdout.trim();
|
|
60
|
+
return value && value !== "undefined" && value !== "null" ? value : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const packageScope = PACKAGE_NAME.match(/^(@[^/]+)\//)?.[1];
|
|
64
|
+
const registry =
|
|
65
|
+
registryOverride ||
|
|
66
|
+
packageJson.publishConfig?.registry ||
|
|
67
|
+
(packageScope && readNpmConfig(`${packageScope}:registry`)) ||
|
|
68
|
+
readNpmConfig("registry");
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const url = new URL(registry);
|
|
72
|
+
if (!["http:", "https:"].includes(url.protocol)) throw new Error("unsupported protocol");
|
|
73
|
+
} catch {
|
|
74
|
+
console.error("The resolved npm registry must be a valid HTTP(S) URL.");
|
|
75
|
+
process.exit(2);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(`Package: ${PACKAGE_NAME}@${packageJson.version}`);
|
|
79
|
+
console.log(`Registry: ${registry}`);
|
|
80
|
+
if (new URL(registry).hostname === "registry.npmjs.org") {
|
|
81
|
+
console.log("Note: npmjs releases normally go through `npm run release:github` (OIDC).");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (dryRun) {
|
|
85
|
+
runNpm(["pack", "--dry-run"]);
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fs.mkdirSync(path.dirname(PUBLISH_NPMRC), { recursive: true, mode: 0o700 });
|
|
90
|
+
if (!fs.existsSync(PUBLISH_NPMRC)) fs.writeFileSync(PUBLISH_NPMRC, "", { mode: 0o600 });
|
|
91
|
+
|
|
92
|
+
const npmConfigArgs = [`--registry=${registry}`, `--userconfig=${PUBLISH_NPMRC}`];
|
|
93
|
+
const whoami = runNpm(["whoami", ...npmConfigArgs], { capture: true, allowFailure: true });
|
|
94
|
+
if (whoami.status !== 0) {
|
|
95
|
+
console.log(`Authentication required. Credentials will be saved to ${PUBLISH_NPMRC}`);
|
|
96
|
+
const loginArgs = ["login", ...npmConfigArgs];
|
|
97
|
+
if (authType) loginArgs.push(`--auth-type=${authType}`);
|
|
98
|
+
runNpm(loginArgs);
|
|
99
|
+
runNpm(["whoami", ...npmConfigArgs]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (authOnly) {
|
|
103
|
+
console.log(`Authenticated for ${registry}`);
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
runNpm(["publish", ...npmConfigArgs]);
|
|
108
|
+
console.log(`\nPublished ${PACKAGE_NAME}@${packageJson.version} to ${registry}`);
|
package/scripts/release.mjs
CHANGED
|
@@ -2,60 +2,153 @@
|
|
|
2
2
|
// Release helper for this package.
|
|
3
3
|
//
|
|
4
4
|
// Usage:
|
|
5
|
-
// npm run release [-- minor|major]
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// Release when scripts/ integrations/ config.default.jsonc or
|
|
14
|
-
// package.json deps change — npx users only get these from the published package.
|
|
15
|
-
// (skills/ ships from GitHub via `npx skills add`; no release needed.)
|
|
5
|
+
// npm run release [-- patch|minor|major]
|
|
6
|
+
// Test, bump, publish to the configured npm registry, then push the commit and v* tag.
|
|
7
|
+
// npm run release:github [-- patch|minor|major]
|
|
8
|
+
// Test, bump, and push the commit and v* tag; GitHub Actions publishes the package.
|
|
9
|
+
// npm run release -- --publish-only [--registry=<url>] [--auth-type=legacy]
|
|
10
|
+
// Publish the current version only; do not test, bump, commit, tag, or push.
|
|
11
|
+
// npm run release -- --dry-run
|
|
12
|
+
// Test and preview the package; do not authenticate, bump, publish, or push.
|
|
16
13
|
|
|
17
14
|
import { spawnSync } from "node:child_process";
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
|
+
const ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
20
|
+
const PUBLISH_SCRIPT = path.join(ROOT, "scripts", "publish.mjs");
|
|
19
21
|
const BUMPS = ["patch", "minor", "major"];
|
|
20
22
|
|
|
23
|
+
let target = "registry";
|
|
21
24
|
let bump = "patch";
|
|
25
|
+
let bumpSpecified = false;
|
|
22
26
|
let dryRun = false;
|
|
27
|
+
let publishOnly = false;
|
|
28
|
+
const publishArgs = [];
|
|
23
29
|
for (const arg of process.argv.slice(2)) {
|
|
24
30
|
if (arg === "--dry-run") dryRun = true;
|
|
25
|
-
else if (
|
|
26
|
-
else
|
|
27
|
-
|
|
31
|
+
else if (arg === "--publish-only") publishOnly = true;
|
|
32
|
+
else if (arg.startsWith("--target=")) target = arg.slice("--target=".length);
|
|
33
|
+
else if (arg.startsWith("--registry=") || arg.startsWith("--auth-type=")) {
|
|
34
|
+
publishArgs.push(arg);
|
|
35
|
+
} else if (BUMPS.includes(arg)) {
|
|
36
|
+
bump = arg;
|
|
37
|
+
bumpSpecified = true;
|
|
38
|
+
} else {
|
|
39
|
+
console.error(
|
|
40
|
+
`Unknown argument: ${arg} ` +
|
|
41
|
+
`(expected ${BUMPS.join("|")}, --publish-only, --dry-run, ` +
|
|
42
|
+
`--registry=<url>, or --auth-type=<type>)`
|
|
43
|
+
);
|
|
28
44
|
process.exit(2);
|
|
29
45
|
}
|
|
30
46
|
}
|
|
31
47
|
|
|
32
|
-
|
|
33
|
-
console.
|
|
34
|
-
|
|
48
|
+
if (!["registry", "github"].includes(target)) {
|
|
49
|
+
console.error(`Unknown release target: ${target} (expected registry or github)`);
|
|
50
|
+
process.exit(2);
|
|
51
|
+
}
|
|
52
|
+
if (target === "github" && publishArgs.length > 0) {
|
|
53
|
+
console.error("--registry and --auth-type only apply to registry releases.");
|
|
54
|
+
process.exit(2);
|
|
55
|
+
}
|
|
56
|
+
if (target === "github" && publishOnly) {
|
|
57
|
+
console.error("--publish-only only applies to registry releases.");
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
if (publishOnly && (dryRun || bumpSpecified)) {
|
|
61
|
+
console.error("--publish-only cannot be combined with a version bump or --dry-run.");
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function run(command, args, { label, onFailure } = {}) {
|
|
66
|
+
console.log(`\n> ${label || [command, ...args].join(" ")}`);
|
|
67
|
+
const result = spawnSync(command, args, { cwd: ROOT, stdio: "inherit" });
|
|
68
|
+
if (result.error) {
|
|
69
|
+
console.error(`\nAborted: ${result.error.message}`);
|
|
70
|
+
onFailure?.();
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
35
73
|
if (result.status !== 0) {
|
|
36
|
-
console.error(`\nAborted:
|
|
74
|
+
console.error(`\nAborted: command exited with ${result.status}.`);
|
|
75
|
+
onFailure?.();
|
|
37
76
|
process.exit(result.status ?? 1);
|
|
38
77
|
}
|
|
39
78
|
}
|
|
40
79
|
|
|
41
|
-
|
|
80
|
+
function runNpm(args) {
|
|
81
|
+
const npmExecPath = process.env.npm_execpath;
|
|
82
|
+
if (npmExecPath) {
|
|
83
|
+
run(process.execPath, [npmExecPath, ...args], { label: `npm ${args.join(" ")}` });
|
|
84
|
+
} else {
|
|
85
|
+
run(process.platform === "win32" ? "npm.cmd" : "npm", args);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function printRecovery(commands) {
|
|
90
|
+
console.error("\nResume without creating another version:");
|
|
91
|
+
for (const command of commands) console.error(` ${command}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const status = spawnSync("git", ["status", "--porcelain"], { cwd: ROOT, encoding: "utf8" });
|
|
95
|
+
if (status.error || status.status !== 0) {
|
|
96
|
+
console.error(status.error?.message || status.stderr || "Unable to read git status.");
|
|
97
|
+
process.exit(status.status ?? 1);
|
|
98
|
+
}
|
|
42
99
|
if (!dryRun && status.stdout.trim() !== "") {
|
|
43
|
-
console.error("Working tree is not clean
|
|
100
|
+
console.error("Working tree is not clean - commit or stash first:\n" + status.stdout);
|
|
44
101
|
process.exit(1);
|
|
45
102
|
}
|
|
46
103
|
|
|
47
|
-
|
|
104
|
+
if (publishOnly) {
|
|
105
|
+
run(process.execPath, [PUBLISH_SCRIPT, ...publishArgs], {
|
|
106
|
+
label: `node scripts/publish.mjs ${publishArgs.join(" ")}`.trimEnd(),
|
|
107
|
+
});
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!dryRun && target === "registry") {
|
|
112
|
+
run(process.execPath, [PUBLISH_SCRIPT, "--auth-only", ...publishArgs], {
|
|
113
|
+
label: `node scripts/publish.mjs --auth-only ${publishArgs.join(" ")}`.trimEnd(),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
runNpm(["test"]);
|
|
48
117
|
|
|
49
118
|
if (dryRun) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
console.log("\nDry run complete. No version bump, nothing pushed.");
|
|
119
|
+
runNpm(["pack", "--dry-run"]);
|
|
120
|
+
console.log("\nDry run complete. No authentication, version bump, publish, or push.");
|
|
53
121
|
process.exit(0);
|
|
54
122
|
}
|
|
55
123
|
|
|
56
|
-
|
|
57
|
-
|
|
124
|
+
const versionArgs = ["version", bump, "--tag-version-prefix=v"];
|
|
125
|
+
if (target === "registry") versionArgs.push("--message=%s [skip ci]");
|
|
126
|
+
runNpm(versionArgs);
|
|
127
|
+
const version = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8")).version;
|
|
128
|
+
const tag = `v${version}`;
|
|
58
129
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
130
|
+
if (target === "registry") {
|
|
131
|
+
run(process.execPath, [PUBLISH_SCRIPT, ...publishArgs], {
|
|
132
|
+
label: `node scripts/publish.mjs ${publishArgs.join(" ")}`.trimEnd(),
|
|
133
|
+
onFailure() {
|
|
134
|
+
const suffix = publishArgs.length > 0 ? ` ${publishArgs.join(" ")}` : "";
|
|
135
|
+
printRecovery([
|
|
136
|
+
`npm run release -- --publish-only${suffix}`,
|
|
137
|
+
"git push --follow-tags",
|
|
138
|
+
]);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
run("git", ["push", "--follow-tags"], {
|
|
142
|
+
onFailure() {
|
|
143
|
+
printRecovery(["git push --follow-tags"]);
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
console.log(`\nReleased ${tag} to the configured npm registry.`);
|
|
147
|
+
} else {
|
|
148
|
+
run("git", ["push", "--follow-tags"], {
|
|
149
|
+
onFailure() {
|
|
150
|
+
printRecovery(["git push --follow-tags"]);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
console.log(`\nPushed ${tag}. GitHub Actions will publish it.`);
|
|
154
|
+
}
|