@narumitw/pi-usage 0.59.0 → 0.60.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 +3 -6
- package/dist/index.ts +136 -39
- package/dist/index.ts.map +2 -2
- package/package.json +1 -1
- package/src/format.ts +15 -8
- package/src/index.ts +1 -1
- package/src/providers/zai.ts +109 -5
- package/src/query.ts +62 -26
- package/src/types.ts +11 -0
- package/src/usage.ts +2 -0
package/package.json
CHANGED
package/src/format.ts
CHANGED
|
@@ -269,6 +269,10 @@ function formatOpenRouterReport(lines: string[], report: UsageReport): void {
|
|
|
269
269
|
|
|
270
270
|
function formatOpenCodeZenReport(lines: string[], report: UsageReport): void {
|
|
271
271
|
for (const bucket of report.buckets) {
|
|
272
|
+
if (bucket.unit === "percent" && bucket.used !== undefined) {
|
|
273
|
+
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${formatPercentBucket(bucket)}`);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
272
276
|
const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : "";
|
|
273
277
|
const used = bucket.used ?? "unavailable";
|
|
274
278
|
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${used}% used${reset}`);
|
|
@@ -469,8 +473,7 @@ function formatXaiReport(lines: string[], report: UsageReport): void {
|
|
|
469
473
|
if (included) {
|
|
470
474
|
let value = "unavailable";
|
|
471
475
|
if (included.unit === "percent" && included.used !== undefined) {
|
|
472
|
-
value =
|
|
473
|
-
if (included.remaining !== undefined) value += ` · ${included.remaining}% left`;
|
|
476
|
+
value = formatPercentBar(included);
|
|
474
477
|
} else if (included.used !== undefined) {
|
|
475
478
|
value = `${formatUsd(included.used)} used`;
|
|
476
479
|
if (included.limit !== undefined) value += ` of ${formatUsd(included.limit)}`;
|
|
@@ -497,12 +500,13 @@ function formatXaiReport(lines: string[], report: UsageReport): void {
|
|
|
497
500
|
|
|
498
501
|
function formatZaiReport(lines: string[], report: UsageReport): void {
|
|
499
502
|
for (const bucket of report.buckets) {
|
|
503
|
+
if (bucket.unit === "percent" && bucket.used !== undefined) {
|
|
504
|
+
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${formatPercentBucket(bucket)}`);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
500
507
|
const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : "";
|
|
501
508
|
let value = "unavailable";
|
|
502
|
-
if (bucket.
|
|
503
|
-
value = `${bucket.used}% used`;
|
|
504
|
-
if (bucket.remaining !== undefined) value += ` · ${bucket.remaining}% left`;
|
|
505
|
-
} else if (bucket.used !== undefined && bucket.limit !== undefined) {
|
|
509
|
+
if (bucket.used !== undefined && bucket.limit !== undefined) {
|
|
506
510
|
value = `${bucket.used} of ${bucket.limit} used`;
|
|
507
511
|
if (bucket.remaining !== undefined) value += ` · ${bucket.remaining} left`;
|
|
508
512
|
} else if (bucket.used !== undefined) {
|
|
@@ -637,10 +641,13 @@ function compactLimitLabel(label: string): string {
|
|
|
637
641
|
}
|
|
638
642
|
|
|
639
643
|
function formatPercentBucket(bucket: UsageBucket): string {
|
|
644
|
+
return `${formatPercentBar(bucket)}${bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : ""}`;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function formatPercentBar(bucket: UsageBucket): string {
|
|
640
648
|
const remaining = clampPercent(bucket.remaining ?? 0);
|
|
641
649
|
const filled = Math.round((remaining / 100) * BAR_SEGMENTS);
|
|
642
|
-
|
|
643
|
-
return `[${"█".repeat(filled)}${"░".repeat(BAR_SEGMENTS - filled)}] ${remaining.toFixed(0)}% left${reset}`;
|
|
650
|
+
return `[${"█".repeat(filled)}${"░".repeat(BAR_SEGMENTS - filled)}] ${remaining.toFixed(0)}% left`;
|
|
644
651
|
}
|
|
645
652
|
|
|
646
653
|
function formatWindowLabel(
|
package/src/index.ts
CHANGED
|
@@ -52,7 +52,7 @@ export { normalizeOpenCodeZenPayload } from "./providers/opencode-zen.js";
|
|
|
52
52
|
export { normalizeOpenRouterKeyPayload } from "./providers/openrouter.js";
|
|
53
53
|
export { normalizeVercelAIGatewayCreditsPayload } from "./providers/vercel-ai-gateway.js";
|
|
54
54
|
export { normalizeXaiBillingPayload } from "./providers/xai.js";
|
|
55
|
-
export { normalizeZaiQuotaPayload } from "./providers/zai.js";
|
|
55
|
+
export { normalizeZaiQuotaPayload, normalizeZaiSubscriptionPayload } from "./providers/zai.js";
|
|
56
56
|
export {
|
|
57
57
|
adapterForProvider,
|
|
58
58
|
isStaleExtensionContextError,
|
package/src/providers/zai.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { sanitizeDisplayText } from "../core.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
UsageBucket,
|
|
4
|
+
UsageMetric,
|
|
5
|
+
UsageReport,
|
|
6
|
+
ZaiPlanInfo,
|
|
7
|
+
ZaiQuotaPayload,
|
|
8
|
+
ZaiSubscriptionPayload,
|
|
9
|
+
} from "../types.js";
|
|
3
10
|
|
|
4
11
|
const FIVE_HOUR_WINDOW_MINUTES = 300;
|
|
5
12
|
const WEEKLY_WINDOW_MINUTES = 10_080;
|
|
@@ -9,6 +16,7 @@ export function normalizeZaiQuotaPayload(
|
|
|
9
16
|
providerName: string,
|
|
10
17
|
payload: ZaiQuotaPayload,
|
|
11
18
|
capturedAt: number,
|
|
19
|
+
plan?: ZaiPlanInfo,
|
|
12
20
|
): UsageReport {
|
|
13
21
|
const data = asObject(payload.data);
|
|
14
22
|
if (!data) throw new Error("Z.AI quota response data was not an object.");
|
|
@@ -26,14 +34,20 @@ export function normalizeZaiQuotaPayload(
|
|
|
26
34
|
addCountBucket(buckets, limit, "mcp-monthly", "MCP monthly allowance");
|
|
27
35
|
addUsageDetailMetrics(metrics, limit.usageDetails);
|
|
28
36
|
} else if (isPlanUsage && unit === 3) {
|
|
29
|
-
addPercentBucket(
|
|
37
|
+
addPercentBucket(
|
|
38
|
+
buckets,
|
|
39
|
+
limit,
|
|
40
|
+
"five-hour",
|
|
41
|
+
sessionWindowLabel(limit),
|
|
42
|
+
sessionWindowMinutes(limit),
|
|
43
|
+
);
|
|
30
44
|
} else if (isPlanUsage && unit === 6) {
|
|
31
45
|
const used = asNonnegativeNumber(limit.currentValue);
|
|
32
46
|
const quota = asNonnegativeNumber(limit.usage);
|
|
33
47
|
if (used !== undefined && quota !== undefined) {
|
|
34
|
-
addCountBucket(buckets, limit, "weekly", "Weekly window",
|
|
48
|
+
addCountBucket(buckets, limit, "weekly", "Weekly window", weeklyWindowMinutes(limit));
|
|
35
49
|
} else {
|
|
36
|
-
addPercentBucket(buckets, limit, "weekly", "Weekly window",
|
|
50
|
+
addPercentBucket(buckets, limit, "weekly", "Weekly window", weeklyWindowMinutes(limit));
|
|
37
51
|
}
|
|
38
52
|
}
|
|
39
53
|
}
|
|
@@ -43,7 +57,12 @@ export function normalizeZaiQuotaPayload(
|
|
|
43
57
|
|
|
44
58
|
const notes: string[] = [];
|
|
45
59
|
const level = asString(data.level);
|
|
46
|
-
|
|
60
|
+
const planLabel = plan?.name ?? level;
|
|
61
|
+
if (planLabel) {
|
|
62
|
+
notes.push(
|
|
63
|
+
plan?.renewsAt ? `Plan: ${planLabel} · renews ${plan.renewsAt}` : `Plan: ${planLabel}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
47
66
|
|
|
48
67
|
return {
|
|
49
68
|
providerId,
|
|
@@ -57,6 +76,79 @@ export function normalizeZaiQuotaPayload(
|
|
|
57
76
|
};
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
// The undocumented subscription endpoint can include historical products. Prefer the current,
|
|
80
|
+
// valid product and fail closed to the quota plan level when only explicitly inactive products exist.
|
|
81
|
+
export function normalizeZaiSubscriptionPayload(
|
|
82
|
+
payload: ZaiSubscriptionPayload,
|
|
83
|
+
): ZaiPlanInfo | undefined {
|
|
84
|
+
if (payload.success === false) return undefined;
|
|
85
|
+
if (typeof payload.code === "number" && payload.code !== 0 && payload.code !== 200) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
if (!Array.isArray(payload.data)) return undefined;
|
|
89
|
+
|
|
90
|
+
const candidates: Array<{
|
|
91
|
+
plan: ZaiPlanInfo;
|
|
92
|
+
status?: string;
|
|
93
|
+
inCurrentPeriod?: boolean;
|
|
94
|
+
}> = [];
|
|
95
|
+
for (const raw of payload.data) {
|
|
96
|
+
const entry = asObject(raw);
|
|
97
|
+
if (!entry) continue;
|
|
98
|
+
const name = asString(entry.productName);
|
|
99
|
+
if (!name) continue;
|
|
100
|
+
const renewsAt = planRenewalDate(entry.nextRenewTime);
|
|
101
|
+
const status = asString(entry.status)?.toUpperCase();
|
|
102
|
+
const inCurrentPeriod = asBoolean(entry.inCurrentPeriod);
|
|
103
|
+
candidates.push({
|
|
104
|
+
plan: { name, ...(renewsAt !== undefined ? { renewsAt } : {}) },
|
|
105
|
+
...(status !== undefined ? { status } : {}),
|
|
106
|
+
...(inCurrentPeriod !== undefined ? { inCurrentPeriod } : {}),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const hasStateMetadata = candidates.some(
|
|
111
|
+
(candidate) => candidate.status !== undefined || candidate.inCurrentPeriod !== undefined,
|
|
112
|
+
);
|
|
113
|
+
if (!hasStateMetadata) return candidates[0]?.plan;
|
|
114
|
+
return (
|
|
115
|
+
candidates.find(
|
|
116
|
+
(candidate) => candidate.inCurrentPeriod === true && candidate.status === "VALID",
|
|
117
|
+
)?.plan ??
|
|
118
|
+
candidates.find(
|
|
119
|
+
(candidate) => candidate.inCurrentPeriod === true && candidate.status === undefined,
|
|
120
|
+
)?.plan ??
|
|
121
|
+
candidates.find(
|
|
122
|
+
(candidate) => candidate.status === "VALID" && candidate.inCurrentPeriod === undefined,
|
|
123
|
+
)?.plan
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function planRenewalDate(value: unknown): string | undefined {
|
|
128
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}/u.test(value)) return value.slice(0, 10);
|
|
129
|
+
const millis = asNonnegativeNumber(value);
|
|
130
|
+
if (millis === undefined || millis === 0) return undefined;
|
|
131
|
+
return new Date(millis).toISOString().slice(0, 10);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Z.AI encodes each window as a (unit, number) pair — unit 3 counts hours and unit 6 counts
|
|
135
|
+
// weeks — so the payload's number drives the window length and session label; the established
|
|
136
|
+
// 5-hour and weekly constants remain the fallback when the payload omits it.
|
|
137
|
+
function sessionWindowMinutes(limit: Record<string, unknown>): number {
|
|
138
|
+
const hours = asPositiveNumber(limit.number);
|
|
139
|
+
return hours === undefined ? FIVE_HOUR_WINDOW_MINUTES : Math.round(hours * 60);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function sessionWindowLabel(limit: Record<string, unknown>): string {
|
|
143
|
+
const minutes = sessionWindowMinutes(limit);
|
|
144
|
+
return minutes === FIVE_HOUR_WINDOW_MINUTES ? "5h window" : `${Math.round(minutes / 60)}h window`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function weeklyWindowMinutes(limit: Record<string, unknown>): number {
|
|
148
|
+
const weeks = asPositiveNumber(limit.number);
|
|
149
|
+
return weeks === undefined ? WEEKLY_WINDOW_MINUTES : Math.round(weeks * WEEKLY_WINDOW_MINUTES);
|
|
150
|
+
}
|
|
151
|
+
|
|
60
152
|
function addPercentBucket(
|
|
61
153
|
buckets: UsageBucket[],
|
|
62
154
|
limit: Record<string, unknown>,
|
|
@@ -130,6 +222,18 @@ function asNonnegativeNumber(value: unknown): number | undefined {
|
|
|
130
222
|
return value;
|
|
131
223
|
}
|
|
132
224
|
|
|
225
|
+
function asPositiveNumber(value: unknown): number | undefined {
|
|
226
|
+
const number = asNonnegativeNumber(value);
|
|
227
|
+
return number !== undefined && number > 0 ? number : undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function asBoolean(value: unknown): boolean | undefined {
|
|
231
|
+
if (typeof value === "boolean") return value;
|
|
232
|
+
if (value === 1) return true;
|
|
233
|
+
if (value === 0) return false;
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
133
237
|
function asEpochSeconds(value: unknown): number | undefined {
|
|
134
238
|
const millis = asNonnegativeNumber(value);
|
|
135
239
|
if (millis === undefined) return undefined;
|
package/src/query.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { normalizeOpenCodeZenPayload } from "./providers/opencode-zen.js";
|
|
|
25
25
|
import { normalizeOpenRouterKeyPayload } from "./providers/openrouter.js";
|
|
26
26
|
import { normalizeVercelAIGatewayCreditsPayload } from "./providers/vercel-ai-gateway.js";
|
|
27
27
|
import { normalizeXaiBillingPayload } from "./providers/xai.js";
|
|
28
|
-
import { normalizeZaiQuotaPayload } from "./providers/zai.js";
|
|
28
|
+
import { normalizeZaiQuotaPayload, normalizeZaiSubscriptionPayload } from "./providers/zai.js";
|
|
29
29
|
import type {
|
|
30
30
|
BasetenBillingUsagePayload,
|
|
31
31
|
CodexBackendPayload,
|
|
@@ -46,7 +46,9 @@ import type {
|
|
|
46
46
|
VercelAIGatewayCreditsPayload,
|
|
47
47
|
XaiBillingPayload,
|
|
48
48
|
XaiUserPayload,
|
|
49
|
+
ZaiPlanInfo,
|
|
49
50
|
ZaiQuotaPayload,
|
|
51
|
+
ZaiSubscriptionPayload,
|
|
50
52
|
} from "./types.js";
|
|
51
53
|
|
|
52
54
|
const BASETEN_BILLING_USAGE_URL = "https://api.baseten.co/v1/billing/usage_summary";
|
|
@@ -295,35 +297,16 @@ export const SUPPORTED_ADAPTERS: readonly UsageProviderAdapter[] = [
|
|
|
295
297
|
id: "zai",
|
|
296
298
|
displayName: "Z.AI",
|
|
297
299
|
semantics: { kind: "consumer-subscription", label: "GLM Coding Plan usage" },
|
|
298
|
-
async query(auth, signal, timeoutMs) {
|
|
299
|
-
|
|
300
|
-
zaiMonitorUrl(auth.model.baseUrl),
|
|
301
|
-
zaiMonitorAuth(auth),
|
|
302
|
-
signal,
|
|
303
|
-
timeoutMs,
|
|
304
|
-
"Z.AI quota endpoint",
|
|
305
|
-
);
|
|
306
|
-
return normalizeZaiQuotaPayload("zai", "Z.AI", payload as ZaiQuotaPayload, Date.now());
|
|
300
|
+
async query(auth, signal, timeoutMs, guard) {
|
|
301
|
+
return queryZaiUsage("zai", "Z.AI", auth, signal, timeoutMs, guard);
|
|
307
302
|
},
|
|
308
303
|
},
|
|
309
304
|
{
|
|
310
305
|
id: "zai-coding-cn",
|
|
311
306
|
displayName: "Z.AI Coding CN",
|
|
312
307
|
semantics: { kind: "consumer-subscription", label: "GLM Coding Plan usage" },
|
|
313
|
-
async query(auth, signal, timeoutMs) {
|
|
314
|
-
|
|
315
|
-
zaiMonitorUrl(auth.model.baseUrl),
|
|
316
|
-
zaiMonitorAuth(auth),
|
|
317
|
-
signal,
|
|
318
|
-
timeoutMs,
|
|
319
|
-
"Z.AI Coding CN quota endpoint",
|
|
320
|
-
);
|
|
321
|
-
return normalizeZaiQuotaPayload(
|
|
322
|
-
"zai-coding-cn",
|
|
323
|
-
"Z.AI Coding CN",
|
|
324
|
-
payload as ZaiQuotaPayload,
|
|
325
|
-
Date.now(),
|
|
326
|
-
);
|
|
308
|
+
async query(auth, signal, timeoutMs, guard) {
|
|
309
|
+
return queryZaiUsage("zai-coding-cn", "Z.AI Coding CN", auth, signal, timeoutMs, guard);
|
|
327
310
|
},
|
|
328
311
|
},
|
|
329
312
|
];
|
|
@@ -1123,10 +1106,14 @@ function fireworksBillingSummaryUrl(accountId: string, startedAt: number): strin
|
|
|
1123
1106
|
return url.toString();
|
|
1124
1107
|
}
|
|
1125
1108
|
|
|
1126
|
-
function
|
|
1109
|
+
function zaiOrigin(baseUrl: string | undefined): string {
|
|
1127
1110
|
const base = baseUrl?.trim();
|
|
1128
1111
|
if (!base) throw new Error("Z.AI model base URL is unavailable.");
|
|
1129
|
-
return
|
|
1112
|
+
return new URL(base).origin;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function zaiMonitorUrl(baseUrl: string | undefined): string {
|
|
1116
|
+
return `${zaiOrigin(baseUrl)}/api/monitor/usage/quota/limit`;
|
|
1130
1117
|
}
|
|
1131
1118
|
|
|
1132
1119
|
function zaiMonitorAuth(auth: ResolvedUsageAuth): ResolvedUsageAuth {
|
|
@@ -1137,6 +1124,55 @@ function zaiMonitorAuth(auth: ResolvedUsageAuth): ResolvedUsageAuth {
|
|
|
1137
1124
|
return { ...auth, headers: { ...auth.headers, Authorization: token } };
|
|
1138
1125
|
}
|
|
1139
1126
|
|
|
1127
|
+
async function queryZaiUsage(
|
|
1128
|
+
providerId: "zai" | "zai-coding-cn",
|
|
1129
|
+
providerName: string,
|
|
1130
|
+
auth: ResolvedUsageAuth,
|
|
1131
|
+
signal: AbortSignal,
|
|
1132
|
+
timeoutMs: number,
|
|
1133
|
+
guard: UsageRequestGuard | undefined,
|
|
1134
|
+
): Promise<UsageReport> {
|
|
1135
|
+
if (!guard) throw new Error("Z.AI usage requires request-boundary revalidation.");
|
|
1136
|
+
const startedAt = Date.now();
|
|
1137
|
+
await guard();
|
|
1138
|
+
const payload = (await fetchProviderJson(
|
|
1139
|
+
zaiMonitorUrl(auth.model.baseUrl),
|
|
1140
|
+
zaiMonitorAuth(auth),
|
|
1141
|
+
signal,
|
|
1142
|
+
remainingTimeout(timeoutMs, startedAt, `fetching ${providerName} quota`),
|
|
1143
|
+
`${providerName} quota endpoint`,
|
|
1144
|
+
)) as ZaiQuotaPayload;
|
|
1145
|
+
await guard();
|
|
1146
|
+
const planTimeoutMs = timeoutMs - (Date.now() - startedAt);
|
|
1147
|
+
const plan = await fetchZaiPlan(providerName, auth, signal, planTimeoutMs);
|
|
1148
|
+
return normalizeZaiQuotaPayload(providerId, providerName, payload, Date.now(), plan);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// The subscription endpoint is undocumented and may not exist on every official origin. It only
|
|
1152
|
+
// contributes the plan name and renewal date, so any non-abort failure is swallowed instead of
|
|
1153
|
+
// blanking the required quota report.
|
|
1154
|
+
async function fetchZaiPlan(
|
|
1155
|
+
providerName: string,
|
|
1156
|
+
auth: ResolvedUsageAuth,
|
|
1157
|
+
signal: AbortSignal,
|
|
1158
|
+
timeoutMs: number,
|
|
1159
|
+
): Promise<ZaiPlanInfo | undefined> {
|
|
1160
|
+
if (timeoutMs <= 0 || signal.aborted) return undefined;
|
|
1161
|
+
try {
|
|
1162
|
+
const payload = (await fetchProviderJson(
|
|
1163
|
+
`${zaiOrigin(auth.model.baseUrl)}/api/biz/subscription/list`,
|
|
1164
|
+
zaiMonitorAuth(auth),
|
|
1165
|
+
signal,
|
|
1166
|
+
timeoutMs,
|
|
1167
|
+
`${providerName} plan endpoint`,
|
|
1168
|
+
)) as ZaiSubscriptionPayload;
|
|
1169
|
+
return normalizeZaiSubscriptionPayload(payload);
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
if (isAbortError(error)) throw error;
|
|
1172
|
+
return undefined;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1140
1176
|
function isAbortError(error: unknown): boolean {
|
|
1141
1177
|
return error instanceof Error && error.name === "AbortError";
|
|
1142
1178
|
}
|
package/src/types.ts
CHANGED
|
@@ -157,6 +157,17 @@ export type ZaiQuotaPayload = {
|
|
|
157
157
|
data?: unknown;
|
|
158
158
|
};
|
|
159
159
|
|
|
160
|
+
export type ZaiSubscriptionPayload = {
|
|
161
|
+
code?: unknown;
|
|
162
|
+
success?: unknown;
|
|
163
|
+
data?: unknown;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export interface ZaiPlanInfo {
|
|
167
|
+
name: string;
|
|
168
|
+
renewsAt?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
160
171
|
export type KimiCodingUsagePayload = {
|
|
161
172
|
usage?: unknown;
|
|
162
173
|
limits?: unknown;
|
package/src/usage.ts
CHANGED