@leo000001/opencode-quota-sidebar 2.0.1 → 2.0.4
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 +3 -0
- package/CONTRIBUTING.md +6 -1
- package/README.md +50 -33
- package/SECURITY.md +1 -1
- package/dist/format.js +118 -30
- package/dist/index.js +55 -4
- package/dist/persistence.js +15 -1
- package/dist/providers/core/kimi_for_coding.d.ts +2 -0
- package/dist/providers/core/kimi_for_coding.js +219 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +3 -1
- package/dist/quota.js +1 -1
- package/dist/quota_render.js +1 -0
- package/dist/quota_service.js +153 -13
- package/dist/storage_parse.js +1 -0
- package/dist/title_apply.d.ts +21 -5
- package/dist/title_apply.js +77 -21
- package/dist/title_refresh.d.ts +3 -1
- package/dist/title_refresh.js +25 -2
- package/dist/tools.d.ts +19 -3
- package/dist/tools.js +58 -16
- package/dist/types.d.ts +2 -0
- package/dist/usage.d.ts +2 -0
- package/dist/usage.js +15 -0
- package/dist/usage_service.js +38 -7
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { isRecord, swallow } from '../../helpers.js';
|
|
2
|
+
import { asNumber, configuredProviderEnabled, fetchWithTimeout, sanitizeBaseURL, toIso, } from '../common.js';
|
|
3
|
+
const KIMI_FOR_CODING_BASE_URL = 'https://api.kimi.com/coding/v1';
|
|
4
|
+
function resolveApiKey(auth, providerOptions) {
|
|
5
|
+
const optionKey = providerOptions?.apiKey;
|
|
6
|
+
if (typeof optionKey === 'string' && optionKey)
|
|
7
|
+
return optionKey;
|
|
8
|
+
if (!auth)
|
|
9
|
+
return undefined;
|
|
10
|
+
if (auth.type === 'api' && typeof auth.key === 'string' && auth.key) {
|
|
11
|
+
return auth.key;
|
|
12
|
+
}
|
|
13
|
+
if (auth.type === 'wellknown') {
|
|
14
|
+
if (typeof auth.key === 'string' && auth.key)
|
|
15
|
+
return auth.key;
|
|
16
|
+
if (typeof auth.token === 'string' && auth.token)
|
|
17
|
+
return auth.token;
|
|
18
|
+
}
|
|
19
|
+
if (auth.type === 'oauth' && typeof auth.access === 'string' && auth.access) {
|
|
20
|
+
return auth.access;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
function isKimiCodingBaseURL(value) {
|
|
25
|
+
const normalized = sanitizeBaseURL(value);
|
|
26
|
+
if (!normalized)
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = new URL(normalized);
|
|
30
|
+
if (parsed.protocol !== 'https:')
|
|
31
|
+
return false;
|
|
32
|
+
const pathname = parsed.pathname.replace(/\/+$/, '');
|
|
33
|
+
return parsed.host === 'api.kimi.com' && pathname === '/coding/v1';
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function usagesUrl(baseURL) {
|
|
40
|
+
const normalized = sanitizeBaseURL(baseURL);
|
|
41
|
+
if (isKimiCodingBaseURL(normalized)) {
|
|
42
|
+
return `${normalized}/usages`;
|
|
43
|
+
}
|
|
44
|
+
return `${KIMI_FOR_CODING_BASE_URL}/usages`;
|
|
45
|
+
}
|
|
46
|
+
function percentFromQuota(limit, remaining) {
|
|
47
|
+
const total = asNumber(limit) ??
|
|
48
|
+
(typeof limit === 'string' && limit.trim() ? Number(limit) : undefined);
|
|
49
|
+
const left = asNumber(remaining) ??
|
|
50
|
+
(typeof remaining === 'string' && remaining.trim()
|
|
51
|
+
? Number(remaining)
|
|
52
|
+
: undefined);
|
|
53
|
+
if (total === undefined || left === undefined || total <= 0)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (!Number.isFinite(total) || !Number.isFinite(left))
|
|
56
|
+
return undefined;
|
|
57
|
+
return Math.max(0, Math.min(100, (left / total) * 100));
|
|
58
|
+
}
|
|
59
|
+
function windowLabel(duration, timeUnit) {
|
|
60
|
+
if (timeUnit === 'TIME_UNIT_MINUTE' && duration === 300)
|
|
61
|
+
return '5h';
|
|
62
|
+
if (timeUnit === 'TIME_UNIT_DAY' && duration === 7)
|
|
63
|
+
return 'Weekly';
|
|
64
|
+
if (timeUnit === 'TIME_UNIT_MINUTE' && duration && duration > 0) {
|
|
65
|
+
const hours = duration / 60;
|
|
66
|
+
if (hours <= 24)
|
|
67
|
+
return `${Math.round(hours)}h`;
|
|
68
|
+
}
|
|
69
|
+
if (timeUnit === 'TIME_UNIT_HOUR' && duration && duration > 0) {
|
|
70
|
+
if (duration <= 24)
|
|
71
|
+
return `${Math.round(duration)}h`;
|
|
72
|
+
const days = duration / 24;
|
|
73
|
+
if (days <= 6)
|
|
74
|
+
return `${Math.round(days)}d`;
|
|
75
|
+
}
|
|
76
|
+
if (timeUnit === 'TIME_UNIT_DAY' && duration && duration > 0) {
|
|
77
|
+
if (duration <= 6)
|
|
78
|
+
return `${Math.round(duration)}d`;
|
|
79
|
+
if (duration === 7)
|
|
80
|
+
return 'Weekly';
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
function parseWindow(value) {
|
|
85
|
+
if (!isRecord(value))
|
|
86
|
+
return undefined;
|
|
87
|
+
const window = isRecord(value.window) ? value.window : undefined;
|
|
88
|
+
const detail = isRecord(value.detail) ? value.detail : undefined;
|
|
89
|
+
if (!window || !detail)
|
|
90
|
+
return undefined;
|
|
91
|
+
const duration = asNumber(window.duration);
|
|
92
|
+
const timeUnit = typeof window.timeUnit === 'string' ? window.timeUnit : undefined;
|
|
93
|
+
const label = windowLabel(duration, timeUnit);
|
|
94
|
+
const remainingPercent = percentFromQuota(detail.limit, detail.remaining);
|
|
95
|
+
if (!label || remainingPercent === undefined)
|
|
96
|
+
return undefined;
|
|
97
|
+
return {
|
|
98
|
+
label,
|
|
99
|
+
remainingPercent,
|
|
100
|
+
resetAt: toIso(detail.resetTime),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function dedupeWindows(windows) {
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
const deduped = [];
|
|
106
|
+
for (const window of windows) {
|
|
107
|
+
const key = `${window.label}|${window.resetAt || ''}|${window.remainingPercent ?? ''}`;
|
|
108
|
+
if (seen.has(key))
|
|
109
|
+
continue;
|
|
110
|
+
seen.add(key);
|
|
111
|
+
deduped.push(window);
|
|
112
|
+
}
|
|
113
|
+
return deduped;
|
|
114
|
+
}
|
|
115
|
+
async function fetchKimiForCodingQuota({ providerID, providerOptions, auth, config, }) {
|
|
116
|
+
const checkedAt = Date.now();
|
|
117
|
+
const base = {
|
|
118
|
+
providerID,
|
|
119
|
+
adapterID: 'kimi-for-coding',
|
|
120
|
+
label: 'Kimi For Coding',
|
|
121
|
+
shortLabel: 'Kimi',
|
|
122
|
+
sortOrder: 15,
|
|
123
|
+
};
|
|
124
|
+
const apiKey = resolveApiKey(auth, providerOptions);
|
|
125
|
+
if (!apiKey) {
|
|
126
|
+
return {
|
|
127
|
+
...base,
|
|
128
|
+
status: 'unavailable',
|
|
129
|
+
checkedAt,
|
|
130
|
+
note: 'missing api key',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const response = await fetchWithTimeout(usagesUrl(providerOptions?.baseURL), {
|
|
134
|
+
method: 'GET',
|
|
135
|
+
headers: {
|
|
136
|
+
Accept: 'application/json',
|
|
137
|
+
Authorization: `Bearer ${apiKey}`,
|
|
138
|
+
'User-Agent': 'opencode-quota-sidebar',
|
|
139
|
+
},
|
|
140
|
+
}, config.quota.requestTimeoutMs).catch(swallow('fetchKimiForCodingQuota:usage'));
|
|
141
|
+
if (!response) {
|
|
142
|
+
return {
|
|
143
|
+
...base,
|
|
144
|
+
status: 'error',
|
|
145
|
+
checkedAt,
|
|
146
|
+
note: 'network request failed',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
status: 'error',
|
|
153
|
+
checkedAt,
|
|
154
|
+
note: `http ${response.status}`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const payload = await response
|
|
158
|
+
.json()
|
|
159
|
+
.catch(swallow('fetchKimiForCodingQuota:json'));
|
|
160
|
+
if (!isRecord(payload)) {
|
|
161
|
+
return {
|
|
162
|
+
...base,
|
|
163
|
+
status: 'error',
|
|
164
|
+
checkedAt,
|
|
165
|
+
note: 'invalid response',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const windows = Array.isArray(payload.limits)
|
|
169
|
+
? payload.limits.map((item) => parseWindow(item)).filter(Boolean)
|
|
170
|
+
: [];
|
|
171
|
+
const usage = isRecord(payload.usage) ? payload.usage : undefined;
|
|
172
|
+
const topLevelRemainingPercent = usage
|
|
173
|
+
? percentFromQuota(usage.limit, usage.remaining)
|
|
174
|
+
: undefined;
|
|
175
|
+
const topLevelResetAt = usage ? toIso(usage.resetTime) : undefined;
|
|
176
|
+
const allWindows = dedupeWindows([
|
|
177
|
+
...windows,
|
|
178
|
+
topLevelRemainingPercent !== undefined
|
|
179
|
+
? {
|
|
180
|
+
label: 'Weekly',
|
|
181
|
+
remainingPercent: topLevelRemainingPercent,
|
|
182
|
+
resetAt: topLevelResetAt,
|
|
183
|
+
}
|
|
184
|
+
: undefined,
|
|
185
|
+
].filter((value) => Boolean(value))).sort((left, right) => {
|
|
186
|
+
const order = (label) => {
|
|
187
|
+
if (label === '5h')
|
|
188
|
+
return 0;
|
|
189
|
+
if (label === 'Weekly')
|
|
190
|
+
return 1;
|
|
191
|
+
return 2;
|
|
192
|
+
};
|
|
193
|
+
return order(left.label) - order(right.label);
|
|
194
|
+
});
|
|
195
|
+
const primary = allWindows[0];
|
|
196
|
+
return {
|
|
197
|
+
...base,
|
|
198
|
+
status: primary ? 'ok' : 'error',
|
|
199
|
+
checkedAt,
|
|
200
|
+
remainingPercent: primary?.remainingPercent,
|
|
201
|
+
resetAt: primary?.resetAt,
|
|
202
|
+
note: primary ? undefined : 'missing quota fields',
|
|
203
|
+
windows: allWindows.length > 0 ? allWindows : undefined,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
export const kimiForCodingAdapter = {
|
|
207
|
+
id: 'kimi-for-coding',
|
|
208
|
+
label: 'Kimi For Coding',
|
|
209
|
+
shortLabel: 'Kimi',
|
|
210
|
+
sortOrder: 15,
|
|
211
|
+
normalizeID: (providerID) => providerID === 'kimi-for-coding' ? 'kimi-for-coding' : undefined,
|
|
212
|
+
matchScore: ({ providerID, providerOptions }) => {
|
|
213
|
+
if (providerID === 'kimi-for-coding')
|
|
214
|
+
return 100;
|
|
215
|
+
return isKimiCodingBaseURL(providerOptions?.baseURL) ? 95 : 0;
|
|
216
|
+
},
|
|
217
|
+
isEnabled: (config) => configuredProviderEnabled(config.quota, 'kimi-for-coding', true),
|
|
218
|
+
fetch: fetchKimiForCodingQuota,
|
|
219
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { anthropicAdapter } from './core/anthropic.js';
|
|
2
2
|
import { buzzAdapter } from './third_party/buzz.js';
|
|
3
3
|
import { copilotAdapter } from './core/copilot.js';
|
|
4
|
+
import { kimiForCodingAdapter } from './core/kimi_for_coding.js';
|
|
4
5
|
import { openaiAdapter } from './core/openai.js';
|
|
5
6
|
import { QuotaProviderRegistry } from './registry.js';
|
|
6
7
|
import { rightCodeAdapter } from './third_party/rightcode.js';
|
|
7
8
|
export declare function createDefaultProviderRegistry(): QuotaProviderRegistry;
|
|
8
|
-
export { anthropicAdapter, buzzAdapter, copilotAdapter, openaiAdapter, rightCodeAdapter, QuotaProviderRegistry, };
|
|
9
|
+
export { anthropicAdapter, buzzAdapter, copilotAdapter, kimiForCodingAdapter, openaiAdapter, rightCodeAdapter, QuotaProviderRegistry, };
|
|
9
10
|
export type { AuthUpdate, AuthValue, ProviderResolveContext, QuotaFetchContext, QuotaProviderAdapter, RefreshedOAuthAuth, } from './types.js';
|
package/dist/providers/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { anthropicAdapter } from './core/anthropic.js';
|
|
2
2
|
import { buzzAdapter } from './third_party/buzz.js';
|
|
3
3
|
import { copilotAdapter } from './core/copilot.js';
|
|
4
|
+
import { kimiForCodingAdapter } from './core/kimi_for_coding.js';
|
|
4
5
|
import { openaiAdapter } from './core/openai.js';
|
|
5
6
|
import { QuotaProviderRegistry } from './registry.js';
|
|
6
7
|
import { rightCodeAdapter } from './third_party/rightcode.js';
|
|
@@ -8,9 +9,10 @@ export function createDefaultProviderRegistry() {
|
|
|
8
9
|
const registry = new QuotaProviderRegistry();
|
|
9
10
|
registry.register(rightCodeAdapter);
|
|
10
11
|
registry.register(buzzAdapter);
|
|
12
|
+
registry.register(kimiForCodingAdapter);
|
|
11
13
|
registry.register(openaiAdapter);
|
|
12
14
|
registry.register(copilotAdapter);
|
|
13
15
|
registry.register(anthropicAdapter);
|
|
14
16
|
return registry;
|
|
15
17
|
}
|
|
16
|
-
export { anthropicAdapter, buzzAdapter, copilotAdapter, openaiAdapter, rightCodeAdapter, QuotaProviderRegistry, };
|
|
18
|
+
export { anthropicAdapter, buzzAdapter, copilotAdapter, kimiForCodingAdapter, openaiAdapter, rightCodeAdapter, QuotaProviderRegistry, };
|
package/dist/quota.js
CHANGED
|
@@ -33,7 +33,7 @@ export function quotaSort(left, right) {
|
|
|
33
33
|
}
|
|
34
34
|
export function listDefaultQuotaProviderIDs() {
|
|
35
35
|
// Keep default report behavior stable for built-in subscription providers.
|
|
36
|
-
return ['openai', 'github-copilot', 'anthropic'];
|
|
36
|
+
return ['openai', 'kimi-for-coding', 'github-copilot', 'anthropic'];
|
|
37
37
|
}
|
|
38
38
|
export function createQuotaRuntime() {
|
|
39
39
|
const providerRegistry = createDefaultProviderRegistry();
|
package/dist/quota_render.js
CHANGED
package/dist/quota_service.js
CHANGED
|
@@ -11,6 +11,7 @@ export function createQuotaService(deps) {
|
|
|
11
11
|
const authCache = new TtlValueCache();
|
|
12
12
|
const providerOptionsCache = new TtlValueCache();
|
|
13
13
|
const inFlight = new Map();
|
|
14
|
+
let lastSuccessfulProviderOptionsMap = {};
|
|
14
15
|
const authFingerprint = (auth) => {
|
|
15
16
|
if (!auth || typeof auth !== 'object')
|
|
16
17
|
return undefined;
|
|
@@ -46,7 +47,7 @@ export function createQuotaService(deps) {
|
|
|
46
47
|
if (cached)
|
|
47
48
|
return cached;
|
|
48
49
|
const value = await loadAuthMap(deps.authPath);
|
|
49
|
-
return authCache.set(value,
|
|
50
|
+
return authCache.set(value, 5_000);
|
|
50
51
|
};
|
|
51
52
|
const getProviderOptionsMap = async () => {
|
|
52
53
|
const cached = providerOptionsCache.get();
|
|
@@ -58,23 +59,142 @@ export function createQuotaService(deps) {
|
|
|
58
59
|
}
|
|
59
60
|
// Newer runtimes expose config.providers; older clients may only expose
|
|
60
61
|
// provider.list with a slightly different response shape.
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
let response;
|
|
63
|
+
let fromConfigProviders = false;
|
|
64
|
+
if (client.config?.providers) {
|
|
65
|
+
fromConfigProviders = true;
|
|
66
|
+
response = await client.config
|
|
67
|
+
.providers({
|
|
63
68
|
query: { directory: deps.directory },
|
|
64
69
|
throwOnError: true,
|
|
65
70
|
})
|
|
66
|
-
|
|
71
|
+
.catch(swallow('getProviderOptionsMap:configProviders'));
|
|
72
|
+
}
|
|
73
|
+
if (!response && client.provider?.list) {
|
|
74
|
+
response = await client.provider
|
|
75
|
+
.list({
|
|
67
76
|
query: { directory: deps.directory },
|
|
68
77
|
throwOnError: true,
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
})
|
|
79
|
+
.catch(swallow('getProviderOptionsMap:providerList'));
|
|
80
|
+
}
|
|
81
|
+
const data = isRecord(response) && Object.prototype.hasOwnProperty.call(response, 'data')
|
|
82
|
+
? response.data
|
|
83
|
+
: undefined;
|
|
84
|
+
if (!response || data === undefined) {
|
|
85
|
+
if (client.provider?.list && fromConfigProviders) {
|
|
86
|
+
response = await client.provider
|
|
87
|
+
.list({
|
|
88
|
+
query: { directory: deps.directory },
|
|
89
|
+
throwOnError: true,
|
|
90
|
+
})
|
|
91
|
+
.catch(swallow('getProviderOptionsMap:providerListNoDataFallback'));
|
|
92
|
+
const fallbackData = isRecord(response) && Object.prototype.hasOwnProperty.call(response, 'data')
|
|
93
|
+
? response.data
|
|
94
|
+
: undefined;
|
|
95
|
+
const fallbackRecord = isRecord(fallbackData) ? fallbackData : undefined;
|
|
96
|
+
const fallbackList = Array.isArray(fallbackRecord?.providers)
|
|
97
|
+
? fallbackRecord.providers
|
|
98
|
+
: Array.isArray(fallbackRecord?.all)
|
|
99
|
+
? fallbackRecord.all
|
|
100
|
+
: Array.isArray(fallbackData)
|
|
101
|
+
? fallbackData
|
|
102
|
+
: undefined;
|
|
103
|
+
const map = Array.isArray(fallbackList)
|
|
104
|
+
? fallbackList.reduce((acc, item) => {
|
|
105
|
+
if (!item || typeof item !== 'object')
|
|
106
|
+
return acc;
|
|
107
|
+
const record = item;
|
|
108
|
+
const id = record.id;
|
|
109
|
+
const options = record.options;
|
|
110
|
+
const key = record.key;
|
|
111
|
+
if (typeof id !== 'string')
|
|
112
|
+
return acc;
|
|
113
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
114
|
+
acc[id] =
|
|
115
|
+
typeof key === 'string' && key ? { apiKey: key } : {};
|
|
116
|
+
return acc;
|
|
117
|
+
}
|
|
118
|
+
const optionsRecord = options;
|
|
119
|
+
acc[id] = {
|
|
120
|
+
...optionsRecord,
|
|
121
|
+
...(typeof key === 'string' && key && optionsRecord.apiKey === undefined
|
|
122
|
+
? { apiKey: key }
|
|
123
|
+
: {}),
|
|
124
|
+
};
|
|
125
|
+
return acc;
|
|
126
|
+
}, {})
|
|
127
|
+
: {};
|
|
128
|
+
if (Object.keys(map).length > 0) {
|
|
129
|
+
lastSuccessfulProviderOptionsMap = map;
|
|
130
|
+
return providerOptionsCache.set(map, 5_000);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return Object.keys(lastSuccessfulProviderOptionsMap).length > 0
|
|
134
|
+
? lastSuccessfulProviderOptionsMap
|
|
135
|
+
: {};
|
|
136
|
+
}
|
|
137
|
+
const dataRecord = isRecord(data) ? data : undefined;
|
|
138
|
+
const list = Array.isArray(dataRecord?.providers)
|
|
139
|
+
? dataRecord.providers
|
|
140
|
+
: Array.isArray(dataRecord?.all)
|
|
141
|
+
? dataRecord.all
|
|
75
142
|
: Array.isArray(data)
|
|
76
143
|
? data
|
|
77
144
|
: undefined;
|
|
145
|
+
if (!list && fromConfigProviders && client.provider?.list) {
|
|
146
|
+
response = await client.provider
|
|
147
|
+
.list({
|
|
148
|
+
query: { directory: deps.directory },
|
|
149
|
+
throwOnError: true,
|
|
150
|
+
})
|
|
151
|
+
.catch(swallow('getProviderOptionsMap:providerListFallback'));
|
|
152
|
+
const fallbackData = isRecord(response) && Object.prototype.hasOwnProperty.call(response, 'data')
|
|
153
|
+
? response.data
|
|
154
|
+
: undefined;
|
|
155
|
+
const fallbackRecord = isRecord(fallbackData) ? fallbackData : undefined;
|
|
156
|
+
const fallbackList = Array.isArray(fallbackRecord?.providers)
|
|
157
|
+
? fallbackRecord.providers
|
|
158
|
+
: Array.isArray(fallbackRecord?.all)
|
|
159
|
+
? fallbackRecord.all
|
|
160
|
+
: Array.isArray(fallbackData)
|
|
161
|
+
? fallbackData
|
|
162
|
+
: undefined;
|
|
163
|
+
const map = Array.isArray(fallbackList)
|
|
164
|
+
? fallbackList.reduce((acc, item) => {
|
|
165
|
+
if (!item || typeof item !== 'object')
|
|
166
|
+
return acc;
|
|
167
|
+
const record = item;
|
|
168
|
+
const id = record.id;
|
|
169
|
+
const options = record.options;
|
|
170
|
+
const key = record.key;
|
|
171
|
+
if (typeof id !== 'string')
|
|
172
|
+
return acc;
|
|
173
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
174
|
+
acc[id] = typeof key === 'string' && key ? { apiKey: key } : {};
|
|
175
|
+
return acc;
|
|
176
|
+
}
|
|
177
|
+
const optionsRecord = options;
|
|
178
|
+
acc[id] = {
|
|
179
|
+
...optionsRecord,
|
|
180
|
+
...(typeof key === 'string' && key && optionsRecord.apiKey === undefined
|
|
181
|
+
? { apiKey: key }
|
|
182
|
+
: {}),
|
|
183
|
+
};
|
|
184
|
+
return acc;
|
|
185
|
+
}, {})
|
|
186
|
+
: {};
|
|
187
|
+
if (Object.keys(map).length > 0) {
|
|
188
|
+
lastSuccessfulProviderOptionsMap = map;
|
|
189
|
+
return providerOptionsCache.set(map, 5_000);
|
|
190
|
+
}
|
|
191
|
+
if (!Array.isArray(fallbackList)) {
|
|
192
|
+
return Object.keys(lastSuccessfulProviderOptionsMap).length > 0
|
|
193
|
+
? lastSuccessfulProviderOptionsMap
|
|
194
|
+
: {};
|
|
195
|
+
}
|
|
196
|
+
return providerOptionsCache.set(map, 5_000);
|
|
197
|
+
}
|
|
78
198
|
const map = Array.isArray(list)
|
|
79
199
|
? list.reduce((acc, item) => {
|
|
80
200
|
if (!item || typeof item !== 'object')
|
|
@@ -82,18 +202,34 @@ export function createQuotaService(deps) {
|
|
|
82
202
|
const record = item;
|
|
83
203
|
const id = record.id;
|
|
84
204
|
const options = record.options;
|
|
205
|
+
const key = record.key;
|
|
85
206
|
if (typeof id !== 'string')
|
|
86
207
|
return acc;
|
|
87
208
|
if (!options ||
|
|
88
209
|
typeof options !== 'object' ||
|
|
89
210
|
Array.isArray(options)) {
|
|
90
|
-
acc[id] = {};
|
|
211
|
+
acc[id] = typeof key === 'string' && key ? { apiKey: key } : {};
|
|
91
212
|
return acc;
|
|
92
213
|
}
|
|
93
|
-
|
|
214
|
+
const optionsRecord = options;
|
|
215
|
+
acc[id] = {
|
|
216
|
+
...optionsRecord,
|
|
217
|
+
...(typeof key === 'string' && key && optionsRecord.apiKey === undefined
|
|
218
|
+
? { apiKey: key }
|
|
219
|
+
: {}),
|
|
220
|
+
};
|
|
94
221
|
return acc;
|
|
95
222
|
}, {})
|
|
96
223
|
: {};
|
|
224
|
+
if (Object.keys(map).length > 0) {
|
|
225
|
+
lastSuccessfulProviderOptionsMap = map;
|
|
226
|
+
return providerOptionsCache.set(map, 5_000);
|
|
227
|
+
}
|
|
228
|
+
if (!Array.isArray(list)) {
|
|
229
|
+
return Object.keys(lastSuccessfulProviderOptionsMap).length > 0
|
|
230
|
+
? lastSuccessfulProviderOptionsMap
|
|
231
|
+
: providerOptionsCache.set(map, 5_000);
|
|
232
|
+
}
|
|
97
233
|
return providerOptionsCache.set(map, 5_000);
|
|
98
234
|
};
|
|
99
235
|
const isValidQuotaCache = (snapshot) => {
|
|
@@ -276,7 +412,11 @@ export function createQuotaService(deps) {
|
|
|
276
412
|
body: next,
|
|
277
413
|
throwOnError: true,
|
|
278
414
|
})
|
|
279
|
-
.catch(
|
|
415
|
+
.catch((error) => {
|
|
416
|
+
swallow('getQuotaSnapshots:authSet')(error);
|
|
417
|
+
throw error;
|
|
418
|
+
});
|
|
419
|
+
authCache.clear();
|
|
280
420
|
}, providerOptions)
|
|
281
421
|
.then((latest) => {
|
|
282
422
|
if (!latest)
|
package/dist/storage_parse.js
CHANGED
|
@@ -27,6 +27,7 @@ function parseProviderUsage(value) {
|
|
|
27
27
|
cost: asNumber(value.cost, 0),
|
|
28
28
|
apiCost: asNumber(value.apiCost, 0),
|
|
29
29
|
assistantMessages: asNumber(value.assistantMessages, 0),
|
|
30
|
+
cacheBuckets: parseCacheUsageBuckets(value.cacheBuckets),
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
function parseCacheUsageBucket(value) {
|
package/dist/title_apply.d.ts
CHANGED
|
@@ -17,16 +17,32 @@ export declare function createTitleApplicator(deps: {
|
|
|
17
17
|
scheduleParentRefreshIfSafe: (sessionID: string, parentID?: string) => void;
|
|
18
18
|
restoreConcurrency: number;
|
|
19
19
|
}): {
|
|
20
|
-
applyTitle: (sessionID: string) => Promise<
|
|
20
|
+
applyTitle: (sessionID: string) => Promise<boolean>;
|
|
21
21
|
handleSessionUpdatedTitle: (args: {
|
|
22
22
|
sessionID: string;
|
|
23
23
|
incomingTitle: string;
|
|
24
24
|
sessionState: SessionState;
|
|
25
25
|
scheduleRefresh: (sessionID: string, delay?: number) => void;
|
|
26
26
|
}) => Promise<void>;
|
|
27
|
-
restoreSessionTitle: (sessionID: string
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
restoreSessionTitle: (sessionID: string, options?: {
|
|
28
|
+
abortIfEnabled?: boolean;
|
|
29
|
+
}) => Promise<boolean>;
|
|
30
|
+
restoreAllVisibleTitles: (options?: {
|
|
31
|
+
abortIfEnabled?: boolean;
|
|
32
|
+
}) => Promise<{
|
|
33
|
+
attempted: number;
|
|
34
|
+
restored: number;
|
|
35
|
+
listFailed: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
refreshAllTouchedTitles: () => Promise<{
|
|
38
|
+
attempted: number;
|
|
39
|
+
refreshed: number;
|
|
40
|
+
listFailed: boolean;
|
|
41
|
+
}>;
|
|
42
|
+
refreshAllVisibleTitles: () => Promise<{
|
|
43
|
+
attempted: number;
|
|
44
|
+
refreshed: number;
|
|
45
|
+
listFailed: boolean;
|
|
46
|
+
}>;
|
|
31
47
|
forgetSession: (sessionID: string) => void;
|
|
32
48
|
};
|