@leo000001/opencode-quota-sidebar 3.0.9 → 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.
Files changed (53) hide show
  1. package/CHANGELOG.md +0 -1
  2. package/README.md +157 -42
  3. package/README.zh-CN.md +157 -42
  4. package/SECURITY.md +1 -1
  5. package/dist/cli.d.ts +18 -0
  6. package/dist/cli.js +354 -0
  7. package/dist/cli_render.d.ts +17 -0
  8. package/dist/cli_render.js +292 -0
  9. package/dist/events.d.ts +1 -1
  10. package/dist/events.js +2 -2
  11. package/dist/format.d.ts +4 -0
  12. package/dist/format.js +302 -41
  13. package/dist/history_messages.d.ts +8 -0
  14. package/dist/history_messages.js +157 -0
  15. package/dist/history_usage.d.ts +93 -0
  16. package/dist/history_usage.js +251 -0
  17. package/dist/index.js +29 -4
  18. package/dist/period.d.ts +29 -1
  19. package/dist/period.js +187 -9
  20. package/dist/provider_catalog.d.ts +8 -0
  21. package/dist/provider_catalog.js +68 -0
  22. package/dist/providers/core/anthropic.d.ts +1 -1
  23. package/dist/providers/core/anthropic.js +69 -45
  24. package/dist/providers/core/openai.js +101 -8
  25. package/dist/providers/index.d.ts +1 -2
  26. package/dist/providers/index.js +1 -3
  27. package/dist/quota.d.ts +4 -2
  28. package/dist/quota.js +18 -21
  29. package/dist/quota_render.d.ts +1 -1
  30. package/dist/quota_render.js +23 -24
  31. package/dist/quota_service.d.ts +1 -0
  32. package/dist/quota_service.js +151 -19
  33. package/dist/storage.d.ts +1 -1
  34. package/dist/storage.js +4 -4
  35. package/dist/storage_dates.d.ts +1 -1
  36. package/dist/storage_dates.js +8 -5
  37. package/dist/storage_parse.js +23 -1
  38. package/dist/supported_quota.d.ts +4 -0
  39. package/dist/supported_quota.js +36 -0
  40. package/dist/title.js +18 -8
  41. package/dist/tools.d.ts +14 -3
  42. package/dist/tools.js +54 -2
  43. package/dist/tui.tsx +17 -6
  44. package/dist/tui_helpers.js +11 -6
  45. package/dist/types.d.ts +8 -0
  46. package/dist/usage.d.ts +18 -0
  47. package/dist/usage.js +93 -9
  48. package/dist/usage_service.d.ts +4 -1
  49. package/dist/usage_service.js +193 -189
  50. package/package.json +4 -1
  51. package/quota-sidebar.config.example.json +36 -45
  52. package/dist/providers/third_party/xyai.d.ts +0 -2
  53. package/dist/providers/third_party/xyai.js +0 -348
@@ -3,7 +3,9 @@ import { API_COST_ENABLED_PROVIDERS, cacheCoverageModeFromRates, calcEquivalentA
3
3
  import { deleteSessionFromDayChunk, dateKeyFromTimestamp, scanAllSessions, updateSessionsInDayChunks, } from './storage.js';
4
4
  import { periodStart } from './period.js';
5
5
  import { debug, debugError, isRecord, mapConcurrent, swallow, } from './helpers.js';
6
- import { emptyUsageSummary, fromCachedSessionUsage, mergeUsage, summarizeMessagesInCompletedRange, summarizeMessagesIncremental, toCachedSessionUsage, USAGE_BILLING_CACHE_VERSION, } from './usage.js';
6
+ import { accumulateMessagesInCompletedRange, emptyUsageSummary, fromCachedSessionUsage, mergeCursorFromEntries, mergeUsage, summarizeMessagesIncremental, toCachedSessionUsage, USAGE_BILLING_CACHE_VERSION, } from './usage.js';
7
+ import { decodeMessageEntries, isMissingSessionError, nextCursorFromResponse, } from './history_messages.js';
8
+ import { computeHistoryUsage } from './history_usage.js';
7
9
  const READ_ONLY_CACHE_PROVIDERS = new Set([
8
10
  'openai',
9
11
  'github-copilot',
@@ -135,164 +137,50 @@ export function createUsageService(deps) {
135
137
  // treat it as read-only (the safer default — avoids overstating cached ratio).
136
138
  return 'read-only';
137
139
  };
138
- const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
139
- const decodeTokens = (value) => {
140
- if (!isRecord(value))
141
- return undefined;
142
- if (!isFiniteNumber(value.input))
143
- return undefined;
144
- if (!isFiniteNumber(value.output))
145
- return undefined;
146
- const reasoning = isFiniteNumber(value.reasoning) ? value.reasoning : 0;
147
- const cacheRaw = isRecord(value.cache) ? value.cache : {};
148
- const read = isFiniteNumber(cacheRaw.read) ? cacheRaw.read : 0;
149
- const write = isFiniteNumber(cacheRaw.write) ? cacheRaw.write : 0;
150
- return {
151
- input: value.input,
152
- output: value.output,
153
- reasoning,
154
- cache: { read, write },
155
- };
156
- };
157
- const decodeMessageInfo = (value) => {
158
- if (!isRecord(value))
159
- return undefined;
160
- if (typeof value.id !== 'string')
161
- return undefined;
162
- if (typeof value.sessionID !== 'string')
163
- return undefined;
164
- if (typeof value.role !== 'string')
165
- return undefined;
166
- if (!isRecord(value.time))
167
- return undefined;
168
- if (!isFiniteNumber(value.time.created))
169
- return undefined;
170
- if (value.time.completed !== undefined &&
171
- !isFiniteNumber(value.time.completed)) {
172
- return undefined;
140
+ const loadSessionEntries = async (sessionID) => {
141
+ try {
142
+ const response = await deps.client.session.messages({
143
+ path: { id: sessionID },
144
+ query: { directory: deps.directory },
145
+ throwOnError: true,
146
+ });
147
+ const data = response.data;
148
+ const entries = decodeMessageEntries(data);
149
+ if (!entries)
150
+ return { status: 'error' };
151
+ return { status: 'ok', entries };
173
152
  }
174
- if (value.role !== 'assistant') {
153
+ catch (error) {
154
+ debugError(`loadSessionEntries ${sessionID}`, error);
175
155
  return {
176
- ...value,
177
- time: {
178
- created: value.time.created,
179
- completed: value.time.completed,
180
- },
156
+ status: isMissingSessionError(error) ? 'missing' : 'error',
181
157
  };
182
158
  }
183
- if (typeof value.providerID !== 'string')
184
- return undefined;
185
- if (typeof value.modelID !== 'string')
186
- return undefined;
187
- const tokens = decodeTokens(value.tokens);
188
- if (!tokens)
189
- return undefined;
190
- // Normalize token fields to a stable shape (some providers/SDK versions may
191
- // omit reasoning/cache.write; treat them as 0).
192
- return {
193
- ...value,
194
- time: {
195
- created: value.time.created,
196
- completed: value.time.completed,
197
- },
198
- tokens,
199
- };
200
- };
201
- const decodeMessageEntry = (value) => {
202
- if (!isRecord(value))
203
- return undefined;
204
- const decoded = decodeMessageInfo(value.info);
205
- if (!decoded)
206
- return undefined;
207
- return { info: decoded };
208
- };
209
- const decodeMessageEntries = (value) => {
210
- if (!Array.isArray(value))
211
- return undefined;
212
- const decoded = value
213
- .map((item) => decodeMessageEntry(item))
214
- .filter((item) => Boolean(item));
215
- if (decoded.length > 0 && decoded.length < value.length) {
216
- debug(`message entries partially decoded: kept ${decoded.length}/${value.length}`);
217
- return undefined;
218
- }
219
- // If the API returned entries but none match the expected shape,
220
- // treat it as a load failure so we don't silently undercount.
221
- if (decoded.length === 0 && value.length > 0)
222
- return undefined;
223
- return decoded;
224
- };
225
- const errorStatusCode = (value, seen = new Set()) => {
226
- if (!isRecord(value) || seen.has(value))
227
- return undefined;
228
- seen.add(value);
229
- const status = value.status;
230
- if (typeof status === 'number' && Number.isFinite(status))
231
- return status;
232
- const statusCode = value.statusCode;
233
- if (typeof statusCode === 'number' && Number.isFinite(statusCode)) {
234
- return statusCode;
235
- }
236
- return (errorStatusCode(value.response, seen) ||
237
- errorStatusCode(value.cause, seen) ||
238
- errorStatusCode(value.error, seen));
239
- };
240
- const errorText = (value, seen = new Set()) => {
241
- if (!value || seen.has(value))
242
- return '';
243
- if (typeof value === 'string')
244
- return value;
245
- if (typeof value === 'number' || typeof value === 'boolean')
246
- return `${value}`;
247
- if (value instanceof Error) {
248
- seen.add(value);
249
- return [
250
- value.message,
251
- errorText(value.cause, seen),
252
- ]
253
- .filter(Boolean)
254
- .join('\n');
255
- }
256
- if (!isRecord(value))
257
- return '';
258
- seen.add(value);
259
- return [
260
- typeof value.message === 'string' ? value.message : '',
261
- typeof value.error === 'string' ? value.error : '',
262
- typeof value.detail === 'string' ? value.detail : '',
263
- typeof value.title === 'string' ? value.title : '',
264
- errorText(value.response, seen),
265
- errorText(value.data, seen),
266
- errorText(value.cause, seen),
267
- ]
268
- .filter(Boolean)
269
- .join('\n');
270
159
  };
271
- const isMissingSessionError = (error) => {
272
- const status = errorStatusCode(error);
273
- if (status === 404 || status === 410)
274
- return true;
275
- const text = errorText(error).toLowerCase();
276
- if (!text)
277
- return false;
278
- return (/\b(session|conversation)\b.*\b(not found|missing|deleted|does not exist)\b/.test(text) ||
279
- /\b(not found|missing|deleted|does not exist)\b.*\b(session|conversation)\b/.test(text));
280
- };
281
- const loadSessionEntries = async (sessionID) => {
160
+ const MESSAGE_PAGE_LIMIT = 200;
161
+ const loadSessionEntriesPage = async (sessionID, before) => {
282
162
  try {
283
163
  const response = await deps.client.session.messages({
284
164
  path: { id: sessionID },
285
- query: { directory: deps.directory },
165
+ query: {
166
+ directory: deps.directory,
167
+ limit: MESSAGE_PAGE_LIMIT,
168
+ ...(before ? { before } : {}),
169
+ },
286
170
  throwOnError: true,
287
171
  });
288
172
  const data = response.data;
289
173
  const entries = decodeMessageEntries(data);
290
174
  if (!entries)
291
175
  return { status: 'error' };
292
- return { status: 'ok', entries };
176
+ return {
177
+ status: 'ok',
178
+ entries,
179
+ nextBefore: nextCursorFromResponse(response),
180
+ };
293
181
  }
294
182
  catch (error) {
295
- debugError(`loadSessionEntries ${sessionID}`, error);
183
+ debugError(`loadSessionEntriesPage ${sessionID}`, error);
296
184
  return {
297
185
  status: isMissingSessionError(error) ? 'missing' : 'error',
298
186
  };
@@ -349,6 +237,21 @@ export function createUsageService(deps) {
349
237
  return modelCostLookupKeys(info.providerID, info.modelID).some((key) => Boolean(modelCostMap[key]));
350
238
  });
351
239
  };
240
+ const shouldTrackFullUsageForRange = (cached, hasPricing) => {
241
+ if (!cached)
242
+ return true;
243
+ if (!isUsageBillingCurrent(cached))
244
+ return true;
245
+ if (!hasPricing)
246
+ return false;
247
+ if (cached.assistantMessages <= 0)
248
+ return false;
249
+ if (cached.apiCost > 0)
250
+ return false;
251
+ if (cached.total <= 0)
252
+ return false;
253
+ return hasAnySubscriptionProvider(cached);
254
+ };
352
255
  const summarizeSessionUsage = async (sessionID, generationAtStart, options) => {
353
256
  const load = await loadSessionEntries(sessionID);
354
257
  const entries = load.status === 'ok' ? load.entries : undefined;
@@ -487,69 +390,88 @@ export function createUsageService(deps) {
487
390
  return merged;
488
391
  };
489
392
  const RANGE_USAGE_CONCURRENCY = 5;
393
+ const filterRangeSessions = (sessions, startAt, endAt) => {
394
+ return sessions.filter((session) => {
395
+ if (session.state.createdAt > endAt)
396
+ return false;
397
+ if (session.state.dirty === true)
398
+ return true;
399
+ const lastMessageTime = session.state.cursor?.lastMessageTime;
400
+ if (typeof lastMessageTime === 'number' &&
401
+ Number.isFinite(lastMessageTime) &&
402
+ lastMessageTime < startAt) {
403
+ return false;
404
+ }
405
+ return true;
406
+ });
407
+ };
490
408
  const summarizeRangeUsage = async (period) => {
491
- const startAt = periodStart(period);
492
- const endAt = Date.now();
409
+ const now = Date.now();
410
+ const startAt = periodStart(period, now);
411
+ const endAt = now;
493
412
  await deps.persistence.flushSave();
494
- const sessions = await scanAllSessions(deps.statePath, deps.state);
413
+ const sessions = filterRangeSessions(await scanAllSessions(deps.statePath, deps.state), startAt, endAt);
495
414
  const usage = emptyUsageSummary();
496
415
  const modelCostMap = await getModelCostMap();
497
416
  const hasPricing = Object.keys(modelCostMap).length > 0;
498
417
  if (sessions.length > 0) {
499
418
  const fetched = await mapConcurrent(sessions, RANGE_USAGE_CONCURRENCY, async (session) => {
500
- const load = await loadSessionEntries(session.sessionID);
501
- if (load.status !== 'ok') {
502
- return {
503
- sessionID: session.sessionID,
504
- dateKey: session.dateKey,
505
- createdAt: session.state.createdAt,
506
- lastMessageTime: session.state.cursor?.lastMessageTime,
507
- dirty: session.state.dirty === true,
508
- computed: emptyUsageSummary(),
509
- fullUsage: undefined,
510
- loadFailed: load.status === 'error',
511
- missing: load.status === 'missing',
512
- persist: false,
513
- cursor: undefined,
514
- };
515
- }
516
- const entries = load.entries;
517
- const computed = summarizeMessagesInCompletedRange(entries, startAt, endAt, 0, {
419
+ const usageOptions = {
518
420
  calcApiCost: (message) => calcEquivalentApiCost(message, modelCostMap),
519
421
  classifyCacheMode: (message) => classifyCacheMode(message, modelCostMap),
520
- });
521
- const shouldPersistFullUsage = !session.state.usage ||
522
- shouldRecomputeUsageCache(session.state.usage, hasPricing, hasResolvableApiCostMessages(entries, modelCostMap));
523
- if (!shouldPersistFullUsage) {
524
- return {
525
- sessionID: session.sessionID,
526
- dateKey: session.dateKey,
527
- createdAt: session.state.createdAt,
528
- lastMessageTime: session.state.cursor?.lastMessageTime,
529
- dirty: session.state.dirty === true,
530
- computed,
531
- fullUsage: undefined,
532
- loadFailed: false,
533
- missing: false,
534
- persist: false,
535
- cursor: session.state.cursor,
536
- };
422
+ };
423
+ const computed = emptyUsageSummary();
424
+ const trackFullUsage = shouldTrackFullUsageForRange(session.state.usage, hasPricing);
425
+ const fullUsage = trackFullUsage ? emptyUsageSummary() : undefined;
426
+ let cursor;
427
+ let hasResolvableApiCostMessage = false;
428
+ let before;
429
+ while (true) {
430
+ const load = await loadSessionEntriesPage(session.sessionID, before);
431
+ if (load.status !== 'ok') {
432
+ return {
433
+ sessionID: session.sessionID,
434
+ dateKey: session.dateKey,
435
+ createdAt: session.state.createdAt,
436
+ lastMessageTime: session.state.cursor?.lastMessageTime,
437
+ dirty: session.state.dirty === true,
438
+ computed: emptyUsageSummary(),
439
+ fullUsage: undefined,
440
+ loadFailed: load.status === 'error',
441
+ missing: load.status === 'missing',
442
+ persist: false,
443
+ cursor: undefined,
444
+ };
445
+ }
446
+ const entries = load.entries;
447
+ if (entries.length === 0)
448
+ break;
449
+ accumulateMessagesInCompletedRange(computed, entries, startAt, endAt, usageOptions);
450
+ if (fullUsage) {
451
+ accumulateMessagesInCompletedRange(fullUsage, entries, 0, Number.POSITIVE_INFINITY, usageOptions);
452
+ cursor = mergeCursorFromEntries(cursor, entries);
453
+ }
454
+ if (!hasResolvableApiCostMessage) {
455
+ hasResolvableApiCostMessage = hasResolvableApiCostMessages(entries, modelCostMap);
456
+ }
457
+ if (!load.nextBefore)
458
+ break;
459
+ before = load.nextBefore;
537
460
  }
538
- const { usage: fullUsage, cursor } = summarizeMessagesIncremental(entries, undefined, undefined, true, {
539
- calcApiCost: (message) => calcEquivalentApiCost(message, modelCostMap),
540
- classifyCacheMode: (message) => classifyCacheMode(message, modelCostMap),
541
- });
461
+ const shouldPersistFullUsage = !!fullUsage &&
462
+ (!session.state.usage ||
463
+ shouldRecomputeUsageCache(session.state.usage, hasPricing, hasResolvableApiCostMessage));
542
464
  return {
543
465
  sessionID: session.sessionID,
544
466
  dateKey: session.dateKey,
545
467
  createdAt: session.state.createdAt,
546
- lastMessageTime: cursor.lastMessageTime,
547
- dirty: false,
468
+ lastMessageTime: cursor?.lastMessageTime,
469
+ dirty: session.state.dirty === true,
548
470
  computed,
549
- fullUsage,
471
+ fullUsage: shouldPersistFullUsage ? fullUsage : undefined,
550
472
  loadFailed: false,
551
473
  missing: false,
552
- persist: true,
474
+ persist: shouldPersistFullUsage,
553
475
  cursor,
554
476
  };
555
477
  });
@@ -632,6 +554,87 @@ export function createUsageService(deps) {
632
554
  }
633
555
  return usage;
634
556
  };
557
+ const summarizeHistoryUsage = async (period, rawSince) => {
558
+ await deps.persistence.flushSave();
559
+ const sessions = await scanAllSessions(deps.statePath, deps.state);
560
+ const result = await computeHistoryUsage({
561
+ sessions,
562
+ loadMessagesPage: loadSessionEntriesPage,
563
+ getModelCostMap: getModelCostMap,
564
+ calcApiCost: (message, costMap) => calcEquivalentApiCost(message, costMap),
565
+ classifyCacheMode: (message, costMap) => classifyCacheMode(message, costMap),
566
+ hasResolvableApiCostMessages: (entries, costMap) => hasResolvableApiCostMessages(entries, costMap),
567
+ shouldTrackFullUsage: shouldTrackFullUsageForRange,
568
+ shouldRecomputeUsageCache,
569
+ throwOnLoadFailure: true,
570
+ }, period, rawSince);
571
+ // Server-side persistence: persist recomputed full-session usage back to
572
+ // memory state and day chunks so future queries are faster.
573
+ const hints = result.persistenceHints;
574
+ if (hints && hints.length > 0) {
575
+ const missingSessions = hints.filter((item) => item.missing);
576
+ if (missingSessions.length > 0) {
577
+ let stateChanged = false;
578
+ for (const missing of missingSessions) {
579
+ deps.state.deletedSessionDateMap[missing.sessionID] = missing.dateKey;
580
+ delete deps.state.sessions[missing.sessionID];
581
+ delete deps.state.sessionDateMap[missing.sessionID];
582
+ deps.persistence.markDirty(missing.dateKey);
583
+ forgetSession(missing.sessionID);
584
+ stateChanged = true;
585
+ }
586
+ await Promise.all(missingSessions.map(async (missing) => {
587
+ const deletedFromChunk = await deleteSessionFromDayChunk(deps.statePath, missing.sessionID, missing.dateKey).catch((error) => {
588
+ swallow('deleteSessionFromDayChunk')(error);
589
+ return false;
590
+ });
591
+ if (!deletedFromChunk)
592
+ return;
593
+ delete deps.state.deletedSessionDateMap[missing.sessionID];
594
+ stateChanged = true;
595
+ }));
596
+ if (stateChanged)
597
+ deps.persistence.scheduleSave();
598
+ }
599
+ let dirty = false;
600
+ const diskOnlyUpdates = [];
601
+ for (const item of hints) {
602
+ if (!item.persist || !item.fullUsage)
603
+ continue;
604
+ const memoryState = deps.state.sessions[item.sessionID];
605
+ if (memoryState) {
606
+ memoryState.usage = toCachedSessionUsage(item.fullUsage);
607
+ memoryState.cursor = item.cursor;
608
+ const resolvedDateKey = deps.state.sessionDateMap[item.sessionID] ||
609
+ dateKeyFromTimestamp(memoryState.createdAt);
610
+ deps.state.sessionDateMap[item.sessionID] = resolvedDateKey;
611
+ deps.persistence.markDirty(resolvedDateKey);
612
+ memoryState.dirty = false;
613
+ dirty = true;
614
+ }
615
+ else {
616
+ diskOnlyUpdates.push({
617
+ sessionID: item.sessionID,
618
+ dateKey: item.dateKey,
619
+ usage: toCachedSessionUsage(item.fullUsage),
620
+ cursor: item.cursor,
621
+ });
622
+ }
623
+ }
624
+ if (diskOnlyUpdates.length > 0) {
625
+ const persisted = await updateSessionsInDayChunks(deps.statePath, diskOnlyUpdates).catch((error) => {
626
+ swallow('updateSessionsInDayChunks')(error);
627
+ return false;
628
+ });
629
+ if (!persisted) {
630
+ throw new Error(`history usage unavailable: failed to persist ${diskOnlyUpdates.length} disk-only session(s)`);
631
+ }
632
+ }
633
+ if (dirty)
634
+ deps.persistence.scheduleSave();
635
+ }
636
+ return result;
637
+ };
635
638
  const summarizeForTool = async (period, sessionID, includeChildren) => {
636
639
  if (period === 'session') {
637
640
  if (!includeChildren) {
@@ -683,6 +686,7 @@ export function createUsageService(deps) {
683
686
  return {
684
687
  summarizeSessionUsageForDisplay,
685
688
  summarizeForTool,
689
+ summarizeHistoryUsage,
686
690
  markSessionDirty,
687
691
  markForceRescan,
688
692
  forgetSession,
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@leo000001/opencode-quota-sidebar",
3
- "version": "3.0.9",
3
+ "version": "4.0.0",
4
4
  "description": "OpenCode plugin that shows quota and token usage in TUI sidebar panels and compact session titles",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
+ "bin": {
8
+ "opencode-quota": "./dist/cli.js"
9
+ },
7
10
  "types": "dist/index.d.ts",
8
11
  "exports": {
9
12
  ".": {
@@ -1,45 +1,36 @@
1
- {
2
- "sidebar": {
3
- "enabled": true,
4
- "width": 36,
5
- "titleMode": "auto",
6
- "multilineTitle": true,
7
- "showCost": true,
8
- "showQuota": true,
9
- "wrapQuotaLines": true,
10
- "includeChildren": true,
11
- "childrenMaxDepth": 6,
12
- "childrenMaxSessions": 128,
13
- "childrenConcurrency": 5,
14
- "desktopCompact": {
15
- "recentRequests": 50,
16
- "recentMinutes": 60
17
- }
18
- },
19
- "quota": {
20
- "refreshMs": 300000,
21
- "includeOpenAI": true,
22
- "includeCopilot": true,
23
- "includeAnthropic": true,
24
- "providers": {
25
- "rightcode": {
26
- "enabled": true
27
- },
28
- "xyai": {
29
- "enabled": false,
30
- "baseURL": "https://new.xychatai.com",
31
- "serviceType": "codex",
32
- "login": {
33
- "username": "your-account@example.com",
34
- "password": "your-password"
35
- }
36
- }
37
- },
38
- "refreshAccessToken": false,
39
- "requestTimeoutMs": 8000
40
- },
41
- "toast": {
42
- "durationMs": 12000
43
- },
44
- "retentionDays": 730
45
- }
1
+ {
2
+ "sidebar": {
3
+ "enabled": true,
4
+ "width": 36,
5
+ "titleMode": "auto",
6
+ "multilineTitle": true,
7
+ "showCost": true,
8
+ "showQuota": true,
9
+ "wrapQuotaLines": true,
10
+ "includeChildren": true,
11
+ "childrenMaxDepth": 6,
12
+ "childrenMaxSessions": 128,
13
+ "childrenConcurrency": 5,
14
+ "desktopCompact": {
15
+ "recentRequests": 50,
16
+ "recentMinutes": 60
17
+ }
18
+ },
19
+ "quota": {
20
+ "refreshMs": 60000,
21
+ "includeOpenAI": true,
22
+ "includeCopilot": true,
23
+ "includeAnthropic": true,
24
+ "providers": {
25
+ "rightcode": {
26
+ "enabled": true
27
+ }
28
+ },
29
+ "refreshAccessToken": false,
30
+ "requestTimeoutMs": 12000
31
+ },
32
+ "toast": {
33
+ "durationMs": 12000
34
+ },
35
+ "retentionDays": 730
36
+ }
@@ -1,2 +0,0 @@
1
- import type { QuotaProviderAdapter } from "../types.js";
2
- export declare const xyaiAdapter: QuotaProviderAdapter;