@kairyou/agent-tools 0.6.0 → 0.7.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 +11 -11
- package/README.zh-CN.md +9 -10
- package/dist/usage/cli.mjs +931 -1
- package/dist/usage/core.mjs +54 -125
- package/integrations/usage/cli.mjs +5 -2
- package/integrations/usage/lib/config.mjs +24 -25
- package/integrations/usage/lib/format.mjs +3 -40
- package/integrations/usage/lib/http.mjs +2 -2
- package/integrations/usage/lib/routes.mjs +37 -96
- package/package.json +1 -1
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
AGENT_TOOLS_HOME,
|
|
9
9
|
agentConfig,
|
|
10
10
|
usagePreset,
|
|
11
|
-
panelUserHeaders,
|
|
12
11
|
newApiQuotaScale,
|
|
13
12
|
providerUsageDays,
|
|
14
13
|
debugLog,
|
|
@@ -22,15 +21,12 @@ import {
|
|
|
22
21
|
} from "./urls.mjs";
|
|
23
22
|
import {
|
|
24
23
|
pickNumber,
|
|
25
|
-
formatMoney,
|
|
26
24
|
usageRoot,
|
|
27
25
|
hasV1UsageFields,
|
|
28
26
|
formatQuota,
|
|
29
27
|
formatNewApiTokenLine,
|
|
28
|
+
formatOneApiBillingLine,
|
|
30
29
|
formatOpenRouterLine,
|
|
31
|
-
formatPanelUserSelfLine,
|
|
32
|
-
panelQuotaScale,
|
|
33
|
-
panelQuotaLooksRemaining,
|
|
34
30
|
} from "./format.mjs";
|
|
35
31
|
import { readRouteCache } from "./cache.mjs";
|
|
36
32
|
|
|
@@ -63,13 +59,12 @@ async function fetchV1Usage(context) {
|
|
|
63
59
|
return usageResult(context, "v1-usage", await formatQuota(context.label, json), json);
|
|
64
60
|
}
|
|
65
61
|
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
// path.
|
|
62
|
+
// New API exposes a read-only usage endpoint authenticated by the same relay
|
|
63
|
+
// API key used for model requests.
|
|
69
64
|
async function fetchNewApiTokenUsage(context) {
|
|
70
65
|
const json = await requestJson(joinUrl(serviceRoot(context.baseUrl), "/api/usage/token/"), {
|
|
71
66
|
key: context.key,
|
|
72
|
-
name: "
|
|
67
|
+
name: "New API token usage",
|
|
73
68
|
});
|
|
74
69
|
const root = usageRoot(json);
|
|
75
70
|
const quota = pickNumber(root, ["quota", "limit", "total_quota", "totalQuota"]);
|
|
@@ -96,84 +91,46 @@ async function fetchNewApiTokenUsage(context) {
|
|
|
96
91
|
return usageResult(context, "newapi-token", await formatNewApiTokenLine(context.label, json), normalized);
|
|
97
92
|
}
|
|
98
93
|
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
const kind = preset === "auto" ? "new-api" : preset;
|
|
105
|
-
const json = await requestJson(joinUrl(serviceRoot(context.baseUrl), "/api/user/self"), {
|
|
94
|
+
// One API's legacy OpenAI billing endpoints use the same relay API key as
|
|
95
|
+
// model requests. Subscription reports the total quota; usage reports cents.
|
|
96
|
+
async function fetchOneApiBillingUsage(context) {
|
|
97
|
+
const base = serviceRoot(context.baseUrl);
|
|
98
|
+
const subscription = await requestJson(joinUrl(base, "/v1/dashboard/billing/subscription"), {
|
|
106
99
|
key: context.key,
|
|
107
|
-
name: "
|
|
108
|
-
headers: await panelUserHeaders(),
|
|
100
|
+
name: "One API billing subscription",
|
|
109
101
|
});
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
102
|
+
const usage = await requestJson(joinUrl(base, "/v1/dashboard/billing/usage"), {
|
|
103
|
+
key: context.key,
|
|
104
|
+
name: "One API billing usage",
|
|
105
|
+
});
|
|
106
|
+
const limit = pickNumber(subscription, ["hard_limit_usd", "hardLimitUsd"]);
|
|
107
|
+
const usageCents = pickNumber(usage, ["total_usage", "totalUsage"]);
|
|
108
|
+
if (limit === undefined || usageCents === undefined) {
|
|
109
|
+
throw new Error("One API billing payload has no quota fields");
|
|
118
110
|
}
|
|
119
|
-
const
|
|
120
|
-
const quota = pickNumber(root, ["quota"]);
|
|
121
|
-
const used = pickNumber(root, ["used_quota", "usedQuota"]);
|
|
122
|
-
const remaining = panelQuotaLooksRemaining(kind)
|
|
123
|
-
? quota
|
|
124
|
-
: (quota === undefined || used === undefined ? quota : Math.max(0, quota - used));
|
|
125
|
-
const total = panelQuotaLooksRemaining(kind)
|
|
126
|
-
? (quota === undefined || used === undefined ? quota : quota + used)
|
|
127
|
-
: quota;
|
|
111
|
+
const used = usageCents / 100;
|
|
128
112
|
const normalized = {
|
|
129
113
|
mode: "quota_limited",
|
|
130
114
|
quota: {
|
|
131
|
-
limit
|
|
132
|
-
used
|
|
133
|
-
remaining:
|
|
115
|
+
limit,
|
|
116
|
+
used,
|
|
117
|
+
remaining: Math.max(0, limit - used),
|
|
134
118
|
},
|
|
135
119
|
unit: "USD",
|
|
136
|
-
source: "
|
|
137
|
-
raw:
|
|
120
|
+
source: "oneapi-billing",
|
|
121
|
+
raw: { subscription, usage },
|
|
138
122
|
};
|
|
139
123
|
return usageResult(
|
|
140
124
|
context,
|
|
141
|
-
"
|
|
142
|
-
|
|
143
|
-
normalized
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Sub2API exposes user balance as USD at /api/v1/auth/me. Newer deployments may
|
|
148
|
-
// also expose richer subscription summaries through /v1/usage, so this route is
|
|
149
|
-
// a fallback for deployments where /v1/usage is unavailable.
|
|
150
|
-
async function fetchSub2ApiAuthMeUsage(context) {
|
|
151
|
-
const json = await requestJson(joinUrl(serviceRoot(context.baseUrl), "/api/v1/auth/me"), {
|
|
152
|
-
key: context.key,
|
|
153
|
-
name: "Sub2API auth/me",
|
|
154
|
-
});
|
|
155
|
-
const root = usageRoot(json);
|
|
156
|
-
const balance = pickNumber(root, ["balance"]);
|
|
157
|
-
if (balance === undefined) throw new Error("Sub2API auth/me payload has no balance field");
|
|
158
|
-
const normalized = {
|
|
159
|
-
mode: "unrestricted",
|
|
160
|
-
planName: root?.username || root?.email || context.label || "Sub2API",
|
|
161
|
-
balance,
|
|
162
|
-
unit: "USD",
|
|
163
|
-
source: "sub2api-auth-me",
|
|
164
|
-
raw: json,
|
|
165
|
-
};
|
|
166
|
-
return usageResult(
|
|
167
|
-
context,
|
|
168
|
-
"sub2api-auth-me",
|
|
169
|
-
`API | balance ${formatMoney(balance)}`,
|
|
125
|
+
"oneapi-billing",
|
|
126
|
+
formatOneApiBillingLine(limit, used),
|
|
170
127
|
normalized
|
|
171
128
|
);
|
|
172
129
|
}
|
|
173
130
|
|
|
174
131
|
// OpenRouter exposes normal API-key usage at /api/v1/key. Some accounts also
|
|
175
132
|
// expose credits at /api/v1/credits; keep this route isolated because
|
|
176
|
-
// OpenRouter's base URL already includes /api/v1, unlike
|
|
133
|
+
// OpenRouter's base URL already includes /api/v1, unlike New API.
|
|
177
134
|
async function fetchOpenRouterUsage(context) {
|
|
178
135
|
const base = cleanBaseUrl(context.baseUrl).includes("/api/v1")
|
|
179
136
|
? cleanBaseUrl(context.baseUrl)
|
|
@@ -201,20 +158,15 @@ const USAGE_ROUTES = {
|
|
|
201
158
|
path: "/v1/usage",
|
|
202
159
|
run: fetchV1Usage,
|
|
203
160
|
},
|
|
204
|
-
"sub2api-auth-me": {
|
|
205
|
-
id: "sub2api-auth-me",
|
|
206
|
-
path: "/api/v1/auth/me",
|
|
207
|
-
run: fetchSub2ApiAuthMeUsage,
|
|
208
|
-
},
|
|
209
161
|
"newapi-token": {
|
|
210
162
|
id: "newapi-token",
|
|
211
163
|
path: "/api/usage/token/",
|
|
212
164
|
run: fetchNewApiTokenUsage,
|
|
213
165
|
},
|
|
214
|
-
"
|
|
215
|
-
id: "
|
|
216
|
-
path: "/
|
|
217
|
-
run:
|
|
166
|
+
"oneapi-billing": {
|
|
167
|
+
id: "oneapi-billing",
|
|
168
|
+
path: "/v1/dashboard/billing/subscription",
|
|
169
|
+
run: fetchOneApiBillingUsage,
|
|
218
170
|
},
|
|
219
171
|
"openrouter": {
|
|
220
172
|
id: "openrouter",
|
|
@@ -224,7 +176,7 @@ const USAGE_ROUTES = {
|
|
|
224
176
|
};
|
|
225
177
|
|
|
226
178
|
// User-authored gateway routes, declared in config.jsonc:
|
|
227
|
-
// "providerUsage": { "routes": ["custom/
|
|
179
|
+
// "providerUsage": { "routes": ["custom/my-gateway.mjs"] }
|
|
228
180
|
// Paths resolve against ~/.agent-tools. Each module exports
|
|
229
181
|
// `export async function run(context, helpers)` plus an optional
|
|
230
182
|
// `export const meta = { id }` (id defaults to the file name). Broken modules
|
|
@@ -304,25 +256,14 @@ async function routeRegistry() {
|
|
|
304
256
|
return registry;
|
|
305
257
|
}
|
|
306
258
|
|
|
307
|
-
// Presets
|
|
308
|
-
// protocols (e.g. anyrouter/agentrouter just try the NewAPI panel endpoints
|
|
309
|
-
// and /v1/usage in a different order).
|
|
310
|
-
// They do not provide panel session-cookie authentication; only endpoints that
|
|
311
|
-
// accept the configured Bearer key can succeed.
|
|
259
|
+
// Presets select API-key usage protocols, not hosted gateway brands.
|
|
312
260
|
async function usageRouteIds(context) {
|
|
313
261
|
const preset = await usagePreset();
|
|
314
262
|
const routes = {
|
|
315
|
-
"sub2api": ["v1-usage"
|
|
263
|
+
"sub2api": ["v1-usage"],
|
|
316
264
|
"openai-compatible": ["v1-usage"],
|
|
317
|
-
"new-api": ["newapi-token"
|
|
318
|
-
"one-api": ["
|
|
319
|
-
"onehub": ["newapi-token", "panel-user-self"],
|
|
320
|
-
"one-hub": ["newapi-token", "panel-user-self"],
|
|
321
|
-
"donehub": ["newapi-token", "panel-user-self"],
|
|
322
|
-
"done-hub": ["newapi-token", "panel-user-self"],
|
|
323
|
-
"veloera": ["panel-user-self", "newapi-token"],
|
|
324
|
-
"anyrouter": ["newapi-token", "panel-user-self", "v1-usage"],
|
|
325
|
-
"agentrouter": ["newapi-token", "panel-user-self", "v1-usage"],
|
|
265
|
+
"new-api": ["newapi-token"],
|
|
266
|
+
"one-api": ["oneapi-billing"],
|
|
326
267
|
"openrouter": ["openrouter"],
|
|
327
268
|
};
|
|
328
269
|
if (routes[preset]) return routes[preset];
|
|
@@ -334,7 +275,7 @@ async function usageRouteIds(context) {
|
|
|
334
275
|
const customIds = (await customRoutes()).map((route) => route.id);
|
|
335
276
|
const builtinIds = hostIncludes(context.baseUrl, "openrouter.ai")
|
|
336
277
|
? ["openrouter"]
|
|
337
|
-
: ["v1-usage", "
|
|
278
|
+
: ["v1-usage", "newapi-token", "oneapi-billing"];
|
|
338
279
|
return [...new Set([...customIds, ...builtinIds])];
|
|
339
280
|
}
|
|
340
281
|
|
package/package.json
CHANGED