@leo000001/opencode-quota-sidebar 3.0.10 → 4.0.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/CHANGELOG.md +0 -1
- package/README.md +157 -42
- package/README.zh-CN.md +157 -42
- package/SECURITY.md +1 -1
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +354 -0
- package/dist/cli_render.d.ts +17 -0
- package/dist/cli_render.js +292 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.js +2 -2
- package/dist/format.d.ts +4 -0
- package/dist/format.js +302 -41
- package/dist/history_messages.d.ts +8 -0
- package/dist/history_messages.js +157 -0
- package/dist/history_usage.d.ts +93 -0
- package/dist/history_usage.js +251 -0
- package/dist/index.js +29 -4
- package/dist/period.d.ts +29 -1
- package/dist/period.js +187 -9
- package/dist/provider_catalog.d.ts +8 -0
- package/dist/provider_catalog.js +68 -0
- package/dist/providers/core/anthropic.d.ts +1 -1
- package/dist/providers/core/anthropic.js +69 -45
- package/dist/providers/core/openai.js +38 -2
- package/dist/providers/index.d.ts +1 -2
- package/dist/providers/index.js +1 -3
- package/dist/quota.d.ts +4 -2
- package/dist/quota.js +18 -21
- package/dist/quota_render.d.ts +1 -1
- package/dist/quota_render.js +23 -24
- package/dist/quota_service.d.ts +1 -0
- package/dist/quota_service.js +151 -19
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +4 -4
- package/dist/storage_dates.d.ts +1 -1
- package/dist/storage_dates.js +8 -5
- package/dist/storage_parse.js +23 -1
- package/dist/supported_quota.d.ts +4 -0
- package/dist/supported_quota.js +36 -0
- package/dist/title.js +18 -8
- package/dist/tools.d.ts +14 -3
- package/dist/tools.js +54 -2
- package/dist/tui.tsx +17 -6
- package/dist/tui_helpers.js +11 -6
- package/dist/types.d.ts +8 -0
- package/dist/usage.d.ts +18 -0
- package/dist/usage.js +93 -9
- package/dist/usage_service.d.ts +4 -1
- package/dist/usage_service.js +193 -189
- package/package.json +4 -1
- package/quota-sidebar.config.example.json +36 -45
- package/dist/providers/third_party/xyai.d.ts +0 -2
- package/dist/providers/third_party/xyai.js +0 -348
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function providerListFromResponse(response) {
|
|
5
|
+
const data = isRecord(response) && Object.prototype.hasOwnProperty.call(response, 'data')
|
|
6
|
+
? response.data
|
|
7
|
+
: undefined;
|
|
8
|
+
const record = isRecord(data) ? data : undefined;
|
|
9
|
+
return Array.isArray(record?.providers)
|
|
10
|
+
? record.providers
|
|
11
|
+
: Array.isArray(record?.all)
|
|
12
|
+
? record.all
|
|
13
|
+
: Array.isArray(data)
|
|
14
|
+
? data
|
|
15
|
+
: undefined;
|
|
16
|
+
}
|
|
17
|
+
export async function listCurrentProviderIDs(input) {
|
|
18
|
+
const client = input.client;
|
|
19
|
+
const ids = new Set();
|
|
20
|
+
const collect = (response) => {
|
|
21
|
+
const list = providerListFromResponse(response);
|
|
22
|
+
if (!list)
|
|
23
|
+
return false;
|
|
24
|
+
for (const item of list) {
|
|
25
|
+
if (!isRecord(item))
|
|
26
|
+
continue;
|
|
27
|
+
if (typeof item.id === 'string' && item.id)
|
|
28
|
+
ids.add(item.id);
|
|
29
|
+
}
|
|
30
|
+
return ids.size > 0;
|
|
31
|
+
};
|
|
32
|
+
if (client.config?.providers) {
|
|
33
|
+
const response = await client.config.providers({
|
|
34
|
+
query: { directory: input.directory },
|
|
35
|
+
throwOnError: true,
|
|
36
|
+
});
|
|
37
|
+
if (collect(response))
|
|
38
|
+
return ids;
|
|
39
|
+
}
|
|
40
|
+
if (client.provider?.list) {
|
|
41
|
+
const response = await client.provider.list({
|
|
42
|
+
query: { directory: input.directory },
|
|
43
|
+
throwOnError: true,
|
|
44
|
+
});
|
|
45
|
+
collect(response);
|
|
46
|
+
}
|
|
47
|
+
return ids;
|
|
48
|
+
}
|
|
49
|
+
export function filterUsageProvidersForDisplay(usage, allowedProviderIDs) {
|
|
50
|
+
if (allowedProviderIDs.size === 0)
|
|
51
|
+
return usage;
|
|
52
|
+
return {
|
|
53
|
+
...usage,
|
|
54
|
+
providers: Object.fromEntries(Object.entries(usage.providers).filter(([providerID]) => allowedProviderIDs.has(providerID))),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function filterHistoryProvidersForDisplay(result, allowedProviderIDs) {
|
|
58
|
+
if (allowedProviderIDs.size === 0)
|
|
59
|
+
return result;
|
|
60
|
+
return {
|
|
61
|
+
...result,
|
|
62
|
+
rows: result.rows.map((row) => ({
|
|
63
|
+
...row,
|
|
64
|
+
usage: filterUsageProvidersForDisplay(row.usage, allowedProviderIDs),
|
|
65
|
+
})),
|
|
66
|
+
total: filterUsageProvidersForDisplay(result.total, allowedProviderIDs),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { QuotaProviderAdapter } from
|
|
1
|
+
import type { QuotaProviderAdapter } from '../types.js';
|
|
2
2
|
export declare const anthropicAdapter: QuotaProviderAdapter;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const ANTHROPIC_OAUTH_USAGE_BETA = "oauth-2025-04-20";
|
|
1
|
+
import { asRecord, configuredProviderEnabled, fetchWithTimeout, normalizePercent, toIso, } from '../common.js';
|
|
2
|
+
const ANTHROPIC_OAUTH_USAGE_BETA = 'oauth-2025-04-20';
|
|
4
3
|
const ANTHROPIC_WINDOW_FIELDS = [
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[
|
|
8
|
-
[
|
|
9
|
-
[
|
|
10
|
-
[
|
|
4
|
+
['five_hour', '5h'],
|
|
5
|
+
['seven_day', 'Weekly'],
|
|
6
|
+
['seven_day_sonnet', 'Sonnet 7d'],
|
|
7
|
+
['seven_day_opus', 'Opus 7d'],
|
|
8
|
+
['seven_day_oauth_apps', 'OAuth Apps 7d'],
|
|
9
|
+
['seven_day_cowork', 'Cowork 7d'],
|
|
11
10
|
];
|
|
12
11
|
function parseAnthropicWindow(value, label) {
|
|
13
12
|
const win = asRecord(value);
|
|
@@ -24,96 +23,121 @@ function parseAnthropicWindow(value, label) {
|
|
|
24
23
|
};
|
|
25
24
|
return parsed;
|
|
26
25
|
}
|
|
26
|
+
function anthropicFetchErrorNote(error) {
|
|
27
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
28
|
+
return 'timeout';
|
|
29
|
+
}
|
|
30
|
+
return 'network request failed';
|
|
31
|
+
}
|
|
32
|
+
function isRetryableAnthropicStatus(status) {
|
|
33
|
+
return status === 408 || status === 429 || status >= 500;
|
|
34
|
+
}
|
|
35
|
+
async function fetchAnthropicUsage(accessToken, timeoutMs) {
|
|
36
|
+
let lastErrorNote;
|
|
37
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetchWithTimeout('https://api.anthropic.com/api/oauth/usage', {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
headers: {
|
|
42
|
+
Accept: 'application/json',
|
|
43
|
+
Authorization: `Bearer ${accessToken}`,
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
'User-Agent': 'opencode-quota-sidebar',
|
|
46
|
+
'anthropic-beta': ANTHROPIC_OAUTH_USAGE_BETA,
|
|
47
|
+
},
|
|
48
|
+
}, timeoutMs);
|
|
49
|
+
if (response.ok ||
|
|
50
|
+
!isRetryableAnthropicStatus(response.status) ||
|
|
51
|
+
attempt > 0) {
|
|
52
|
+
return { response };
|
|
53
|
+
}
|
|
54
|
+
lastErrorNote = `http ${response.status}`;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
lastErrorNote = anthropicFetchErrorNote(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { errorNote: lastErrorNote || 'network request failed' };
|
|
61
|
+
}
|
|
27
62
|
async function fetchAnthropicQuota({ providerID, auth, config, }) {
|
|
28
63
|
const checkedAt = Date.now();
|
|
29
64
|
const base = {
|
|
30
65
|
providerID,
|
|
31
|
-
adapterID:
|
|
32
|
-
label:
|
|
33
|
-
shortLabel:
|
|
66
|
+
adapterID: 'anthropic',
|
|
67
|
+
label: 'Anthropic',
|
|
68
|
+
shortLabel: 'Anthropic',
|
|
34
69
|
sortOrder: 30,
|
|
35
70
|
};
|
|
36
71
|
if (!auth) {
|
|
37
72
|
return {
|
|
38
73
|
...base,
|
|
39
|
-
status:
|
|
74
|
+
status: 'unavailable',
|
|
40
75
|
checkedAt,
|
|
41
|
-
note:
|
|
76
|
+
note: 'auth not found',
|
|
42
77
|
};
|
|
43
78
|
}
|
|
44
|
-
if (auth.type !==
|
|
79
|
+
if (auth.type !== 'oauth') {
|
|
45
80
|
return {
|
|
46
81
|
...base,
|
|
47
|
-
status:
|
|
82
|
+
status: 'unsupported',
|
|
48
83
|
checkedAt,
|
|
49
|
-
note:
|
|
84
|
+
note: 'api key auth has no quota endpoint',
|
|
50
85
|
};
|
|
51
86
|
}
|
|
52
|
-
if (typeof auth.access !==
|
|
87
|
+
if (typeof auth.access !== 'string' || !auth.access) {
|
|
53
88
|
return {
|
|
54
89
|
...base,
|
|
55
|
-
status:
|
|
90
|
+
status: 'unavailable',
|
|
56
91
|
checkedAt,
|
|
57
|
-
note:
|
|
92
|
+
note: 'missing oauth access token',
|
|
58
93
|
};
|
|
59
94
|
}
|
|
60
|
-
const response = await
|
|
61
|
-
method: "GET",
|
|
62
|
-
headers: {
|
|
63
|
-
Accept: "application/json",
|
|
64
|
-
Authorization: `Bearer ${auth.access}`,
|
|
65
|
-
"Content-Type": "application/json",
|
|
66
|
-
"User-Agent": "opencode-quota-sidebar",
|
|
67
|
-
"anthropic-beta": ANTHROPIC_OAUTH_USAGE_BETA,
|
|
68
|
-
},
|
|
69
|
-
}, config.quota.requestTimeoutMs).catch(swallow("fetchAnthropicQuota:usage"));
|
|
95
|
+
const { response, errorNote } = await fetchAnthropicUsage(auth.access, config.quota.requestTimeoutMs);
|
|
70
96
|
if (!response) {
|
|
71
97
|
return {
|
|
72
98
|
...base,
|
|
73
|
-
status:
|
|
99
|
+
status: 'error',
|
|
74
100
|
checkedAt,
|
|
75
|
-
note:
|
|
101
|
+
note: errorNote || 'network request failed',
|
|
76
102
|
};
|
|
77
103
|
}
|
|
78
104
|
if (!response.ok) {
|
|
79
105
|
return {
|
|
80
106
|
...base,
|
|
81
|
-
status:
|
|
107
|
+
status: 'error',
|
|
82
108
|
checkedAt,
|
|
83
109
|
note: `http ${response.status}`,
|
|
84
110
|
};
|
|
85
111
|
}
|
|
86
|
-
const payload = await response
|
|
87
|
-
.json()
|
|
88
|
-
.catch(swallow("fetchAnthropicQuota:json"));
|
|
112
|
+
const payload = await response.json().catch(() => undefined);
|
|
89
113
|
const usage = asRecord(payload);
|
|
90
114
|
if (!usage) {
|
|
91
115
|
return {
|
|
92
116
|
...base,
|
|
93
|
-
status:
|
|
117
|
+
status: 'error',
|
|
94
118
|
checkedAt,
|
|
95
|
-
note:
|
|
119
|
+
note: 'invalid response',
|
|
96
120
|
};
|
|
97
121
|
}
|
|
98
122
|
const windows = ANTHROPIC_WINDOW_FIELDS.map(([field, label]) => parseAnthropicWindow(usage[field], label)).filter((window) => Boolean(window));
|
|
99
123
|
const primary = windows[0];
|
|
100
124
|
return {
|
|
101
125
|
...base,
|
|
102
|
-
status: primary ?
|
|
126
|
+
status: primary ? 'ok' : 'error',
|
|
103
127
|
checkedAt,
|
|
104
128
|
usedPercent: primary?.usedPercent,
|
|
105
129
|
remainingPercent: primary?.remainingPercent,
|
|
106
130
|
resetAt: primary?.resetAt,
|
|
107
|
-
note: primary ? undefined :
|
|
131
|
+
note: primary ? undefined : 'missing quota fields',
|
|
108
132
|
windows: windows.length > 0 ? windows : undefined,
|
|
109
133
|
};
|
|
110
134
|
}
|
|
111
135
|
export const anthropicAdapter = {
|
|
112
|
-
id:
|
|
113
|
-
label:
|
|
114
|
-
shortLabel:
|
|
136
|
+
id: 'anthropic',
|
|
137
|
+
label: 'Anthropic',
|
|
138
|
+
shortLabel: 'Anthropic',
|
|
115
139
|
sortOrder: 30,
|
|
116
|
-
matchScore: ({ providerID }) => (providerID ===
|
|
117
|
-
isEnabled: (config) => configuredProviderEnabled(config.quota,
|
|
140
|
+
matchScore: ({ providerID }) => (providerID === 'anthropic' ? 80 : 0),
|
|
141
|
+
isEnabled: (config) => configuredProviderEnabled(config.quota, 'anthropic', config.quota.includeAnthropic),
|
|
118
142
|
fetch: fetchAnthropicQuota,
|
|
119
143
|
};
|
|
@@ -42,19 +42,30 @@ function windowResetAt(win, fallback) {
|
|
|
42
42
|
return undefined;
|
|
43
43
|
return new Date(Date.now() + resetAfterSeconds * 1000).toISOString();
|
|
44
44
|
}
|
|
45
|
-
function parseOpenAIWindow(win, fallbackLabel) {
|
|
45
|
+
function parseOpenAIWindow(win, fallbackLabel, labelPrefix = '') {
|
|
46
46
|
const usedPercent = normalizeOpenAIQuotaPercent(win.used_percent);
|
|
47
47
|
const remainingPercent = normalizeOpenAIQuotaPercent(win.remaining_percent) ??
|
|
48
48
|
(usedPercent === undefined ? undefined : 100 - usedPercent);
|
|
49
49
|
if (remainingPercent === undefined)
|
|
50
50
|
return undefined;
|
|
51
51
|
return {
|
|
52
|
-
label: windowLabel(win, fallbackLabel),
|
|
52
|
+
label: `${labelPrefix}${windowLabel(win, fallbackLabel)}`.trim(),
|
|
53
53
|
remainingPercent,
|
|
54
54
|
usedPercent,
|
|
55
55
|
resetAt: windowResetAt(win),
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
function additionalRateLimitPrefix(limitName, meteredFeature) {
|
|
59
|
+
if (meteredFeature === 'codex_bengalfox')
|
|
60
|
+
return 'Spark ';
|
|
61
|
+
if (typeof meteredFeature === 'string' && meteredFeature)
|
|
62
|
+
return undefined;
|
|
63
|
+
if (typeof limitName !== 'string' || !limitName)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (/codex-spark/i.test(limitName))
|
|
66
|
+
return 'Spark ';
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
58
69
|
async function fetchOpenAIQuota(ctx) {
|
|
59
70
|
const checkedAt = Date.now();
|
|
60
71
|
const base = {
|
|
@@ -195,6 +206,31 @@ async function fetchOpenAIQuota(ctx) {
|
|
|
195
206
|
if (secondaryWin)
|
|
196
207
|
windows.push(secondaryWin);
|
|
197
208
|
}
|
|
209
|
+
const additionalRateLimits = Array.isArray(payload.additional_rate_limits)
|
|
210
|
+
? payload.additional_rate_limits
|
|
211
|
+
: [];
|
|
212
|
+
for (const item of additionalRateLimits) {
|
|
213
|
+
if (!isRecord(item))
|
|
214
|
+
continue;
|
|
215
|
+
const prefix = additionalRateLimitPrefix(item.limit_name, item.metered_feature);
|
|
216
|
+
if (!prefix)
|
|
217
|
+
continue;
|
|
218
|
+
const itemRateLimit = isRecord(item.rate_limit)
|
|
219
|
+
? item.rate_limit
|
|
220
|
+
: undefined;
|
|
221
|
+
if (!itemRateLimit)
|
|
222
|
+
continue;
|
|
223
|
+
if (isRecord(itemRateLimit.primary_window)) {
|
|
224
|
+
const primaryWin = parseOpenAIWindow(itemRateLimit.primary_window, '', prefix);
|
|
225
|
+
if (primaryWin)
|
|
226
|
+
windows.push(primaryWin);
|
|
227
|
+
}
|
|
228
|
+
if (isRecord(itemRateLimit.secondary_window)) {
|
|
229
|
+
const secondaryWin = parseOpenAIWindow(itemRateLimit.secondary_window, 'Weekly', prefix);
|
|
230
|
+
if (secondaryWin)
|
|
231
|
+
windows.push(secondaryWin);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
198
234
|
return {
|
|
199
235
|
...base,
|
|
200
236
|
status: remainingPercent === undefined ? 'error' : 'ok',
|
|
@@ -6,7 +6,6 @@ import { openaiAdapter } from './core/openai.js';
|
|
|
6
6
|
import { zhipuCodingPlanAdapter } from './core/zhipu_coding_plan.js';
|
|
7
7
|
import { QuotaProviderRegistry } from './registry.js';
|
|
8
8
|
import { rightCodeAdapter } from './third_party/rightcode.js';
|
|
9
|
-
import { xyaiAdapter } from './third_party/xyai.js';
|
|
10
9
|
export declare function createDefaultProviderRegistry(): QuotaProviderRegistry;
|
|
11
|
-
export { anthropicAdapter, copilotAdapter, kimiForCodingAdapter, minimaxCnCodingPlanAdapter, openaiAdapter, rightCodeAdapter,
|
|
10
|
+
export { anthropicAdapter, copilotAdapter, kimiForCodingAdapter, minimaxCnCodingPlanAdapter, openaiAdapter, rightCodeAdapter, zhipuCodingPlanAdapter, QuotaProviderRegistry, };
|
|
12
11
|
export type { AuthUpdate, AuthValue, ProviderResolveContext, QuotaFetchContext, QuotaProviderAdapter, RefreshedOAuthAuth, } from './types.js';
|
package/dist/providers/index.js
CHANGED
|
@@ -6,11 +6,9 @@ import { openaiAdapter } from './core/openai.js';
|
|
|
6
6
|
import { zhipuCodingPlanAdapter } from './core/zhipu_coding_plan.js';
|
|
7
7
|
import { QuotaProviderRegistry } from './registry.js';
|
|
8
8
|
import { rightCodeAdapter } from './third_party/rightcode.js';
|
|
9
|
-
import { xyaiAdapter } from './third_party/xyai.js';
|
|
10
9
|
export function createDefaultProviderRegistry() {
|
|
11
10
|
const registry = new QuotaProviderRegistry();
|
|
12
11
|
registry.register(rightCodeAdapter);
|
|
13
|
-
registry.register(xyaiAdapter);
|
|
14
12
|
registry.register(kimiForCodingAdapter);
|
|
15
13
|
registry.register(zhipuCodingPlanAdapter);
|
|
16
14
|
registry.register(minimaxCnCodingPlanAdapter);
|
|
@@ -19,4 +17,4 @@ export function createDefaultProviderRegistry() {
|
|
|
19
17
|
registry.register(anthropicAdapter);
|
|
20
18
|
return registry;
|
|
21
19
|
}
|
|
22
|
-
export { anthropicAdapter, copilotAdapter, kimiForCodingAdapter, minimaxCnCodingPlanAdapter, openaiAdapter, rightCodeAdapter,
|
|
20
|
+
export { anthropicAdapter, copilotAdapter, kimiForCodingAdapter, minimaxCnCodingPlanAdapter, openaiAdapter, rightCodeAdapter, zhipuCodingPlanAdapter, QuotaProviderRegistry, };
|
package/dist/quota.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthUpdate, AuthValue } from
|
|
2
|
-
import type { QuotaSidebarConfig, QuotaSnapshot } from
|
|
1
|
+
import type { AuthUpdate, AuthValue } from './providers/types.js';
|
|
2
|
+
import type { QuotaSidebarConfig, QuotaSnapshot } from './types.js';
|
|
3
3
|
export declare function quotaSort(left: QuotaSnapshot, right: QuotaSnapshot): number;
|
|
4
4
|
export declare function listDefaultQuotaProviderIDs(): string[];
|
|
5
5
|
export declare function createQuotaRuntime(): {
|
|
@@ -24,6 +24,7 @@ export declare function createQuotaRuntime(): {
|
|
|
24
24
|
};
|
|
25
25
|
note?: string;
|
|
26
26
|
windows?: import("./types.js").QuotaWindow[];
|
|
27
|
+
stale?: import("./types.js").QuotaStaleMeta;
|
|
27
28
|
} | undefined>;
|
|
28
29
|
};
|
|
29
30
|
export declare function normalizeProviderID(providerID: string): string;
|
|
@@ -48,4 +49,5 @@ export declare function fetchQuotaSnapshot(providerID: string, authMap: Record<s
|
|
|
48
49
|
};
|
|
49
50
|
note?: string;
|
|
50
51
|
windows?: import("./types.js").QuotaWindow[];
|
|
52
|
+
stale?: import("./types.js").QuotaStaleMeta;
|
|
51
53
|
} | undefined>;
|
package/dist/quota.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import { isRecord, swallow } from
|
|
3
|
-
import { createDefaultProviderRegistry } from
|
|
4
|
-
import { sanitizeBaseURL } from
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { isRecord, swallow } from './helpers.js';
|
|
3
|
+
import { createDefaultProviderRegistry } from './providers/index.js';
|
|
4
|
+
import { sanitizeBaseURL } from './providers/common.js';
|
|
5
5
|
function resolveContext(providerID, providerOptions) {
|
|
6
6
|
return { providerID, providerOptions };
|
|
7
7
|
}
|
|
8
8
|
function authCandidates(providerID, normalizedProviderID, adapterID) {
|
|
9
9
|
const candidates = new Set([providerID]);
|
|
10
|
-
if (adapterID ===
|
|
11
|
-
candidates.add(
|
|
10
|
+
if (adapterID === 'github-copilot') {
|
|
11
|
+
candidates.add('github-copilot-enterprise');
|
|
12
12
|
}
|
|
13
13
|
candidates.add(normalizedProviderID);
|
|
14
14
|
candidates.add(adapterID);
|
|
@@ -34,12 +34,12 @@ export function quotaSort(left, right) {
|
|
|
34
34
|
export function listDefaultQuotaProviderIDs() {
|
|
35
35
|
// Keep default report behavior stable for built-in subscription providers.
|
|
36
36
|
return [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
'openai',
|
|
38
|
+
'kimi-for-coding',
|
|
39
|
+
'zhipuai-coding-plan',
|
|
40
|
+
'minimax-cn-coding-plan',
|
|
41
|
+
'github-copilot',
|
|
42
|
+
'anthropic',
|
|
43
43
|
];
|
|
44
44
|
}
|
|
45
45
|
export function createQuotaRuntime() {
|
|
@@ -60,14 +60,11 @@ export function createQuotaRuntime() {
|
|
|
60
60
|
keyBase = `${adapter.id}:${providerID}`;
|
|
61
61
|
}
|
|
62
62
|
// Some third-party adapters intentionally preserve provider-specific labels
|
|
63
|
-
// (for example RC-openai
|
|
63
|
+
// (for example RC-openai) even when they share one adapter.
|
|
64
64
|
// Keep the original provider identity in cache keys so same-host aliases with
|
|
65
65
|
// different auth/config entries do not overwrite each other.
|
|
66
|
-
if (
|
|
67
|
-
normalizedProviderID.startsWith(
|
|
68
|
-
(adapter?.id === "xyai" &&
|
|
69
|
-
providerID !== "xyai" &&
|
|
70
|
-
providerID !== "xyai-vibe")) {
|
|
66
|
+
if (adapter?.id === 'rightcode' &&
|
|
67
|
+
normalizedProviderID.startsWith('rightcode-')) {
|
|
71
68
|
keyBase = normalizedProviderID;
|
|
72
69
|
}
|
|
73
70
|
return baseURL ? `${keyBase}@${baseURL}` : keyBase;
|
|
@@ -116,16 +113,16 @@ export function quotaCacheKey(providerID, providerOptions) {
|
|
|
116
113
|
}
|
|
117
114
|
export async function loadAuthMap(authPath) {
|
|
118
115
|
const parsed = await fs
|
|
119
|
-
.readFile(authPath,
|
|
116
|
+
.readFile(authPath, 'utf8')
|
|
120
117
|
.then((value) => JSON.parse(value))
|
|
121
|
-
.catch(swallow(
|
|
118
|
+
.catch(swallow('loadAuthMap'));
|
|
122
119
|
if (!isRecord(parsed))
|
|
123
120
|
return {};
|
|
124
121
|
return Object.entries(parsed).reduce((acc, [key, value]) => {
|
|
125
122
|
if (!isRecord(value))
|
|
126
123
|
return acc;
|
|
127
124
|
const type = value.type;
|
|
128
|
-
if (type !==
|
|
125
|
+
if (type !== 'oauth' && type !== 'api' && type !== 'wellknown')
|
|
129
126
|
return acc;
|
|
130
127
|
acc[key] = value;
|
|
131
128
|
return acc;
|
package/dist/quota_render.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QuotaSnapshot } from
|
|
1
|
+
import type { QuotaSnapshot } from './types.js';
|
|
2
2
|
export declare function canonicalProviderID(providerID: string): string;
|
|
3
3
|
export declare function displayShortLabel(providerID: string): string;
|
|
4
4
|
export declare function quotaDisplayLabel(quota: QuotaSnapshot): string;
|
package/dist/quota_render.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
|
+
import { isSupportedQuotaSnapshot } from './supported_quota.js';
|
|
1
2
|
const PROVIDER_SHORT_LABELS = {
|
|
2
|
-
openai:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
anthropic:
|
|
8
|
-
rightcode:
|
|
9
|
-
xyai: "XYAI",
|
|
3
|
+
openai: 'OpenAI',
|
|
4
|
+
'kimi-for-coding': 'Kimi',
|
|
5
|
+
'zhipuai-coding-plan': 'Zhipu',
|
|
6
|
+
'minimax-cn-coding-plan': 'MiniMax',
|
|
7
|
+
'github-copilot': 'Copilot',
|
|
8
|
+
anthropic: 'Anthropic',
|
|
9
|
+
rightcode: 'RC',
|
|
10
10
|
};
|
|
11
11
|
export function canonicalProviderID(providerID) {
|
|
12
|
-
if (providerID.startsWith(
|
|
13
|
-
return
|
|
14
|
-
if (providerID ===
|
|
15
|
-
return
|
|
16
|
-
if (providerID ===
|
|
17
|
-
return
|
|
18
|
-
if (providerID === "xyai-vibe")
|
|
19
|
-
return "xyai";
|
|
12
|
+
if (providerID.startsWith('github-copilot'))
|
|
13
|
+
return 'github-copilot';
|
|
14
|
+
if (providerID === 'zhipuai-coding-plan')
|
|
15
|
+
return 'zhipuai-coding-plan';
|
|
16
|
+
if (providerID === 'minimax-cn-coding-plan')
|
|
17
|
+
return 'minimax-cn-coding-plan';
|
|
20
18
|
return providerID;
|
|
21
19
|
}
|
|
22
20
|
export function displayShortLabel(providerID) {
|
|
@@ -24,8 +22,8 @@ export function displayShortLabel(providerID) {
|
|
|
24
22
|
const direct = PROVIDER_SHORT_LABELS[canonical];
|
|
25
23
|
if (direct)
|
|
26
24
|
return direct;
|
|
27
|
-
if (canonical.startsWith(
|
|
28
|
-
return `RC-${canonical.slice(
|
|
25
|
+
if (canonical.startsWith('rightcode-')) {
|
|
26
|
+
return `RC-${canonical.slice('rightcode-'.length)}`;
|
|
29
27
|
}
|
|
30
28
|
return providerID;
|
|
31
29
|
}
|
|
@@ -40,13 +38,13 @@ export function quotaDisplayLabel(quota) {
|
|
|
40
38
|
return displayShortLabel(quota.providerID);
|
|
41
39
|
}
|
|
42
40
|
function quotaKey(quota) {
|
|
43
|
-
if (quota.adapterID ===
|
|
41
|
+
if (quota.adapterID === 'rightcode')
|
|
44
42
|
return `rightcode:${quota.providerID}`;
|
|
45
43
|
return `${quota.adapterID || quota.providerID}:${quota.providerID}`;
|
|
46
44
|
}
|
|
47
45
|
function quotaScore(quota) {
|
|
48
46
|
let score = 0;
|
|
49
|
-
if (quota.status ===
|
|
47
|
+
if (quota.status === 'ok')
|
|
50
48
|
score += 10;
|
|
51
49
|
if (quota.windows && quota.windows.length > 0) {
|
|
52
50
|
score += 5 + quota.windows.length;
|
|
@@ -58,14 +56,15 @@ function quotaScore(quota) {
|
|
|
58
56
|
return score;
|
|
59
57
|
}
|
|
60
58
|
export function collapseQuotaSnapshots(quotas) {
|
|
59
|
+
const supportedQuotas = quotas.filter((quota) => isSupportedQuotaSnapshot(quota));
|
|
61
60
|
const grouped = new Map();
|
|
62
|
-
const hasRightCodeBase =
|
|
63
|
-
for (const quota of
|
|
61
|
+
const hasRightCodeBase = supportedQuotas.some((quota) => quota.adapterID === 'rightcode' && quotaDisplayLabel(quota) === 'RC');
|
|
62
|
+
for (const quota of supportedQuotas) {
|
|
64
63
|
// If both RC (balance) and RC-variant (subscription) exist,
|
|
65
64
|
// treat balance as owned by RC.
|
|
66
65
|
const normalizedQuota = hasRightCodeBase &&
|
|
67
|
-
quota.adapterID ===
|
|
68
|
-
quotaDisplayLabel(quota).startsWith(
|
|
66
|
+
quota.adapterID === 'rightcode' &&
|
|
67
|
+
quotaDisplayLabel(quota).startsWith('RC-')
|
|
69
68
|
? { ...quota, balance: undefined }
|
|
70
69
|
: quota;
|
|
71
70
|
const key = quotaKey(normalizedQuota);
|
package/dist/quota_service.d.ts
CHANGED