@kaitranntt/ccs 7.14.0 → 7.15.0-dev.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.
Files changed (62) hide show
  1. package/dist/cliproxy/account-manager.d.ts +46 -1
  2. package/dist/cliproxy/account-manager.d.ts.map +1 -1
  3. package/dist/cliproxy/account-manager.js +175 -10
  4. package/dist/cliproxy/account-manager.js.map +1 -1
  5. package/dist/cliproxy/auth/oauth-handler.d.ts.map +1 -1
  6. package/dist/cliproxy/auth/oauth-handler.js +65 -3
  7. package/dist/cliproxy/auth/oauth-handler.js.map +1 -1
  8. package/dist/cliproxy/cliproxy-executor.d.ts.map +1 -1
  9. package/dist/cliproxy/cliproxy-executor.js +18 -23
  10. package/dist/cliproxy/cliproxy-executor.js.map +1 -1
  11. package/dist/cliproxy/quota-fetcher.d.ts +3 -1
  12. package/dist/cliproxy/quota-fetcher.d.ts.map +1 -1
  13. package/dist/cliproxy/quota-fetcher.js +98 -24
  14. package/dist/cliproxy/quota-fetcher.js.map +1 -1
  15. package/dist/cliproxy/quota-manager.d.ts +88 -0
  16. package/dist/cliproxy/quota-manager.d.ts.map +1 -0
  17. package/dist/cliproxy/quota-manager.js +278 -0
  18. package/dist/cliproxy/quota-manager.js.map +1 -0
  19. package/dist/commands/cliproxy-command.d.ts.map +1 -1
  20. package/dist/commands/cliproxy-command.js +191 -0
  21. package/dist/commands/cliproxy-command.js.map +1 -1
  22. package/dist/commands/help-command.d.ts.map +1 -1
  23. package/dist/commands/help-command.js +4 -0
  24. package/dist/commands/help-command.js.map +1 -1
  25. package/dist/config/unified-config-loader.d.ts.map +1 -1
  26. package/dist/config/unified-config-loader.js +22 -0
  27. package/dist/config/unified-config-loader.js.map +1 -1
  28. package/dist/config/unified-config-types.d.ts +62 -2
  29. package/dist/config/unified-config-types.d.ts.map +1 -1
  30. package/dist/config/unified-config-types.js +29 -2
  31. package/dist/config/unified-config-types.js.map +1 -1
  32. package/dist/ui/assets/{accounts-wkvC_Z1E.js → accounts-D31DF_DO.js} +1 -1
  33. package/dist/ui/assets/{alert-dialog--nlDi-Dj.js → alert-dialog-LCg4HaeZ.js} +1 -1
  34. package/dist/ui/assets/{api-DeyefOLW.js → api-Bmxcet5w.js} +1 -1
  35. package/dist/ui/assets/{auth-section-BQXfw1cb.js → auth-section-CRTokeZ8.js} +1 -1
  36. package/dist/ui/assets/{card-P5vPGo2D.js → card-BmnReIPp.js} +1 -1
  37. package/dist/ui/assets/cliproxy-BrJaxqVj.js +3 -0
  38. package/dist/ui/assets/{cliproxy-control-panel-Dbs8NAQa.js → cliproxy-control-panel-D7FGe5HL.js} +1 -1
  39. package/dist/ui/assets/{confirm-dialog-BM75Bkww.js → confirm-dialog-CzpGAyFB.js} +1 -1
  40. package/dist/ui/assets/{copilot-BwylIlO5.js → copilot-C2v-0P0m.js} +3 -3
  41. package/dist/ui/assets/{globalenv-section-Cgpz_NQ0.js → globalenv-section-Cx0L1Zte.js} +1 -1
  42. package/dist/ui/assets/{health-DWUjYWDI.js → health-BvZrzSJ3.js} +1 -1
  43. package/dist/ui/assets/icons-CZDQWWm2.js +1 -0
  44. package/dist/ui/assets/index-BX0IVNt5.css +1 -0
  45. package/dist/ui/assets/{index-CIWe-99u.js → index-CJrvk2Qf.js} +1 -1
  46. package/dist/ui/assets/{index-D8MFq-UJ.js → index-D8K1YCe7.js} +14 -14
  47. package/dist/ui/assets/{index-4Wd5fx72.js → index-fED9JOL0.js} +1 -1
  48. package/dist/ui/assets/{index-f_AuSs01.js → index-qAWtHsB9.js} +1 -1
  49. package/dist/ui/assets/{shared-I-pAdvVQ.js → shared-Do9VzQ5g.js} +1 -1
  50. package/dist/ui/assets/{switch-DHwS-NMF.js → switch-CmGBdPfU.js} +1 -1
  51. package/dist/ui/index.html +3 -3
  52. package/dist/web-server/index.d.ts.map +1 -1
  53. package/dist/web-server/index.js +8 -1
  54. package/dist/web-server/index.js.map +1 -1
  55. package/dist/web-server/routes/cliproxy-auth-routes.d.ts.map +1 -1
  56. package/dist/web-server/routes/cliproxy-auth-routes.js +92 -1
  57. package/dist/web-server/routes/cliproxy-auth-routes.js.map +1 -1
  58. package/package.json +1 -1
  59. package/scripts/code-reviewer.ts +308 -0
  60. package/dist/ui/assets/cliproxy-CcMLaKpF.js +0 -3
  61. package/dist/ui/assets/icons-U9n5DGj9.js +0 -1
  62. package/dist/ui/assets/index-1g-Hy9VA.css +0 -1
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ /**
3
+ * Quota Manager for Hybrid Auto+Manual Account Selection
4
+ *
5
+ * Provides pre-flight quota checking with caching, tier-based failover,
6
+ * and cooldown tracking for exhausted accounts.
7
+ *
8
+ * Key features:
9
+ * - 30-second in-memory cache for quota results
10
+ * - Tier-priority failover (ultra > pro by default)
11
+ * - Cooldown tracking for exhausted accounts
12
+ * - Respects paused accounts from manual config
13
+ * - Graceful degradation on API failures
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.getQuotaStatus = exports.preflightCheck = exports.findHealthyAccount = exports.clearCooldown = exports.applyCooldown = exports.isOnCooldown = exports.clearQuotaCache = exports.setCachedQuota = exports.getCachedQuota = void 0;
17
+ const quota_fetcher_1 = require("./quota-fetcher");
18
+ const account_manager_1 = require("./account-manager");
19
+ const unified_config_loader_1 = require("../config/unified-config-loader");
20
+ const CACHE_TTL_MS = 30000; // 30 seconds
21
+ const quotaCache = new Map();
22
+ // Request deduplication: track in-flight fetch promises to avoid parallel duplicate requests
23
+ const pendingFetches = new Map();
24
+ function getCacheKey(provider, accountId) {
25
+ return `${provider}:${accountId}`;
26
+ }
27
+ /**
28
+ * Get cached quota result if still valid
29
+ */
30
+ function getCachedQuota(provider, accountId) {
31
+ const key = getCacheKey(provider, accountId);
32
+ const entry = quotaCache.get(key);
33
+ if (!entry)
34
+ return null;
35
+ if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
36
+ quotaCache.delete(key);
37
+ return null;
38
+ }
39
+ return entry.result;
40
+ }
41
+ exports.getCachedQuota = getCachedQuota;
42
+ /**
43
+ * Cache quota result
44
+ */
45
+ function setCachedQuota(provider, accountId, result) {
46
+ const key = getCacheKey(provider, accountId);
47
+ quotaCache.set(key, { result, timestamp: Date.now() });
48
+ }
49
+ exports.setCachedQuota = setCachedQuota;
50
+ /**
51
+ * Clear all cached quota results
52
+ */
53
+ function clearQuotaCache() {
54
+ quotaCache.clear();
55
+ }
56
+ exports.clearQuotaCache = clearQuotaCache;
57
+ /**
58
+ * Fetch quota with request deduplication
59
+ * If a fetch for this account is already in progress, return the existing promise
60
+ */
61
+ async function fetchQuotaWithDedup(provider, accountId) {
62
+ const key = getCacheKey(provider, accountId);
63
+ // Check if fetch already in progress
64
+ const pending = pendingFetches.get(key);
65
+ if (pending) {
66
+ return pending;
67
+ }
68
+ // Start new fetch and track it
69
+ const fetchPromise = (0, quota_fetcher_1.fetchAccountQuota)(provider, accountId)
70
+ .then((result) => {
71
+ setCachedQuota(provider, accountId, result);
72
+ return result;
73
+ })
74
+ .catch(() => {
75
+ return { success: false, models: [], lastUpdated: Date.now() };
76
+ })
77
+ .finally(() => {
78
+ pendingFetches.delete(key);
79
+ });
80
+ pendingFetches.set(key, fetchPromise);
81
+ return fetchPromise;
82
+ }
83
+ const cooldownMap = new Map();
84
+ /**
85
+ * Check if account is on cooldown
86
+ */
87
+ function isOnCooldown(provider, accountId) {
88
+ const key = getCacheKey(provider, accountId);
89
+ const entry = cooldownMap.get(key);
90
+ if (!entry)
91
+ return false;
92
+ if (Date.now() > entry.until) {
93
+ cooldownMap.delete(key);
94
+ return false;
95
+ }
96
+ return true;
97
+ }
98
+ exports.isOnCooldown = isOnCooldown;
99
+ /**
100
+ * Apply cooldown to an exhausted account
101
+ */
102
+ function applyCooldown(provider, accountId, minutes) {
103
+ const key = getCacheKey(provider, accountId);
104
+ cooldownMap.set(key, { until: Date.now() + minutes * 60 * 1000 });
105
+ }
106
+ exports.applyCooldown = applyCooldown;
107
+ /**
108
+ * Clear cooldown for an account
109
+ */
110
+ function clearCooldown(provider, accountId) {
111
+ const key = getCacheKey(provider, accountId);
112
+ cooldownMap.delete(key);
113
+ }
114
+ exports.clearCooldown = clearCooldown;
115
+ /**
116
+ * Calculate average quota percentage from models
117
+ */
118
+ function calculateAverageQuota(quota) {
119
+ if (!quota.success || quota.models.length === 0) {
120
+ return 100; // Assume OK if no data
121
+ }
122
+ const total = quota.models.reduce((sum, m) => sum + m.percentage, 0);
123
+ return total / quota.models.length;
124
+ }
125
+ /**
126
+ * Find healthy account with remaining quota
127
+ * Respects tier priority and skips paused/cooldown accounts
128
+ */
129
+ async function findHealthyAccount(provider, exclude) {
130
+ const config = (0, unified_config_loader_1.loadOrCreateUnifiedConfig)();
131
+ const tierPriority = config.quota_management?.auto?.tier_priority ?? ['paid'];
132
+ const threshold = config.quota_management?.auto?.exhaustion_threshold ?? 5;
133
+ const accounts = (0, account_manager_1.getProviderAccounts)(provider);
134
+ // Filter available accounts
135
+ const available = accounts.filter((a) => !exclude.includes(a.id) && !(0, account_manager_1.isAccountPaused)(provider, a.id) && !isOnCooldown(provider, a.id));
136
+ if (available.length === 0)
137
+ return null;
138
+ // Fetch quota for each available account (with caching and deduplication)
139
+ const withQuotas = await Promise.all(available.map(async (account) => {
140
+ let quota = getCachedQuota(provider, account.id);
141
+ if (!quota) {
142
+ quota = await fetchQuotaWithDedup(provider, account.id);
143
+ }
144
+ const avgQuota = calculateAverageQuota(quota);
145
+ return {
146
+ id: account.id,
147
+ tier: account.tier || 'paid',
148
+ lastQuota: avgQuota,
149
+ };
150
+ }));
151
+ // Filter by threshold
152
+ const healthy = withQuotas.filter((a) => a.lastQuota >= threshold);
153
+ if (healthy.length === 0)
154
+ return null;
155
+ // Sort by tier priority then quota descending
156
+ healthy.sort((a, b) => {
157
+ const tierA = tierPriority.indexOf(a.tier);
158
+ const tierB = tierPriority.indexOf(b.tier);
159
+ const tierOrderA = tierA === -1 ? 999 : tierA;
160
+ const tierOrderB = tierB === -1 ? 999 : tierB;
161
+ if (tierOrderA !== tierOrderB)
162
+ return tierOrderA - tierOrderB;
163
+ return b.lastQuota - a.lastQuota;
164
+ });
165
+ return healthy[0];
166
+ }
167
+ exports.findHealthyAccount = findHealthyAccount;
168
+ /**
169
+ * Find and switch to a healthy account
170
+ */
171
+ async function findAndSwitch(provider, excludeAccountId, reason) {
172
+ const alternative = await findHealthyAccount(provider, [excludeAccountId]);
173
+ if (!alternative) {
174
+ // No alternatives: use original anyway (graceful degradation)
175
+ return {
176
+ proceed: true,
177
+ accountId: excludeAccountId,
178
+ reason: `${reason}, no alternatives available`,
179
+ };
180
+ }
181
+ // Switch default
182
+ (0, account_manager_1.setDefaultAccount)(provider, alternative.id);
183
+ (0, account_manager_1.touchAccount)(provider, alternative.id);
184
+ return {
185
+ proceed: true,
186
+ accountId: alternative.id,
187
+ switchedFrom: excludeAccountId,
188
+ reason,
189
+ quotaPercent: alternative.lastQuota,
190
+ };
191
+ }
192
+ /**
193
+ * Perform pre-flight quota check before session start
194
+ *
195
+ * Checks if default account has sufficient quota, auto-switches if needed.
196
+ * Respects paused accounts, tier priority, and cooldown settings.
197
+ *
198
+ * @param provider - CLIProxy provider (only 'agy' supports quota)
199
+ * @returns PreflightResult with account to use and any switch info
200
+ */
201
+ async function preflightCheck(provider) {
202
+ // Only Antigravity supports quota checking
203
+ if (provider !== 'agy') {
204
+ const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
205
+ return { proceed: true, accountId: defaultAccount?.id || '' };
206
+ }
207
+ const config = (0, unified_config_loader_1.loadOrCreateUnifiedConfig)();
208
+ const quotaConfig = config.quota_management;
209
+ // Skip if preflight disabled or mode is manual
210
+ if (!quotaConfig?.auto?.preflight_check || quotaConfig?.mode === 'manual') {
211
+ const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
212
+ return { proceed: true, accountId: defaultAccount?.id || '' };
213
+ }
214
+ const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
215
+ if (!defaultAccount) {
216
+ return { proceed: false, accountId: '', reason: 'No accounts configured' };
217
+ }
218
+ // Check forced_default override (manual mode)
219
+ const forcedDefault = quotaConfig.manual?.forced_default;
220
+ if (forcedDefault) {
221
+ const forcedAccount = (0, account_manager_1.getProviderAccounts)(provider).find((a) => a.id === forcedDefault);
222
+ if (forcedAccount) {
223
+ return { proceed: true, accountId: forcedAccount.id, reason: 'Forced default override' };
224
+ }
225
+ }
226
+ // Check if default is paused
227
+ if ((0, account_manager_1.isAccountPaused)(provider, defaultAccount.id)) {
228
+ return await findAndSwitch(provider, defaultAccount.id, 'Default account is paused');
229
+ }
230
+ // Check cooldown
231
+ if (isOnCooldown(provider, defaultAccount.id)) {
232
+ return await findAndSwitch(provider, defaultAccount.id, 'Default account on cooldown');
233
+ }
234
+ // Check quota (with cache and deduplication)
235
+ let quota = getCachedQuota(provider, defaultAccount.id);
236
+ if (!quota) {
237
+ quota = await fetchQuotaWithDedup(provider, defaultAccount.id);
238
+ }
239
+ // Calculate average quota
240
+ const avgQuota = calculateAverageQuota(quota);
241
+ const threshold = quotaConfig.auto?.exhaustion_threshold ?? 5;
242
+ if (avgQuota < threshold) {
243
+ // Apply cooldown to exhausted account
244
+ applyCooldown(provider, defaultAccount.id, quotaConfig.auto?.cooldown_minutes ?? 5);
245
+ return await findAndSwitch(provider, defaultAccount.id, `Quota exhausted (${avgQuota.toFixed(1)}%)`);
246
+ }
247
+ return {
248
+ proceed: true,
249
+ accountId: defaultAccount.id,
250
+ quotaPercent: avgQuota,
251
+ };
252
+ }
253
+ exports.preflightCheck = preflightCheck;
254
+ /**
255
+ * Get quota status for all accounts of a provider
256
+ * Used by CLI status command
257
+ */
258
+ async function getQuotaStatus(provider) {
259
+ const accounts = (0, account_manager_1.getProviderAccounts)(provider);
260
+ const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
261
+ const results = await Promise.all(accounts.map(async (account) => {
262
+ let quota = getCachedQuota(provider, account.id);
263
+ if (!quota && provider === 'agy') {
264
+ quota = await fetchQuotaWithDedup(provider, account.id);
265
+ }
266
+ const avgQuota = quota ? calculateAverageQuota(quota) : 100;
267
+ return {
268
+ account,
269
+ quota: avgQuota,
270
+ paused: (0, account_manager_1.isAccountPaused)(provider, account.id),
271
+ onCooldown: isOnCooldown(provider, account.id),
272
+ isDefault: defaultAccount?.id === account.id,
273
+ };
274
+ }));
275
+ return { accounts: results };
276
+ }
277
+ exports.getQuotaStatus = getQuotaStatus;
278
+ //# sourceMappingURL=quota-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-manager.js","sourceRoot":"","sources":["../../src/cliproxy/quota-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAGH,mDAAiE;AACjE,uDAO2B;AAC3B,2EAA4E;AAW5E,MAAM,YAAY,GAAG,KAAM,CAAC,CAAC,aAAa;AAC1C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;AAEjD,6FAA6F;AAC7F,MAAM,cAAc,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE/D,SAAS,WAAW,CAAC,QAA0B,EAAE,SAAiB;IAChE,OAAO,GAAG,QAAQ,IAAI,SAAS,EAAE,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,QAA0B,EAAE,SAAiB;IAC1E,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAElC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;QAChD,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAZD,wCAYC;AAED;;GAEG;AACH,SAAgB,cAAc,CAC5B,QAA0B,EAC1B,SAAiB,EACjB,MAAmB;IAEnB,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC7C,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAPD,wCAOC;AAED;;GAEG;AACH,SAAgB,eAAe;IAC7B,UAAU,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC;AAFD,0CAEC;AAED;;;GAGG;AACH,KAAK,UAAU,mBAAmB,CAChC,QAA0B,EAC1B,SAAiB;IAEjB,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAE7C,qCAAqC;IACrC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,+BAA+B;IAC/B,MAAM,YAAY,GAAG,IAAA,iCAAiB,EAAC,QAAQ,EAAE,SAAS,CAAC;SACxD,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QACf,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAC5C,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;SACD,KAAK,CAAC,GAAgB,EAAE;QACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACjE,CAAC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE;QACZ,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEL,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACtC,OAAO,YAAY,CAAC;AACtB,CAAC;AAUD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAC;AAErD;;GAEG;AACH,SAAgB,YAAY,CAAC,QAA0B,EAAE,SAAiB;IACxE,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAEnC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAC7B,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAZD,oCAYC;AAED;;GAEG;AACH,SAAgB,aAAa,CAC3B,QAA0B,EAC1B,SAAiB,EACjB,OAAe;IAEf,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC7C,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AACpE,CAAC;AAPD,sCAOC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,QAA0B,EAAE,SAAiB;IACzE,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC7C,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAHD,sCAGC;AAsBD;;GAEG;AACH,SAAS,qBAAqB,CAAC,KAAkB;IAC/C,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,GAAG,CAAC,CAAC,uBAAuB;IACrC,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;AACrC,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CACtC,QAA0B,EAC1B,OAAiB;IAEjB,MAAM,MAAM,GAAG,IAAA,iDAAyB,GAAE,CAAC;IAC3C,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB,EAAE,IAAI,EAAE,oBAAoB,IAAI,CAAC,CAAC;IAE3E,MAAM,QAAQ,GAAG,IAAA,qCAAmB,EAAC,QAAQ,CAAC,CAAC;IAE/C,4BAA4B;IAC5B,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAC/B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAA,iCAAe,EAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAC/F,CAAC;IAEF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,0EAA0E;IAC1E,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9B,IAAI,KAAK,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAE9C,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM;YAC5B,SAAS,EAAE,QAAQ;SACpB,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEF,sBAAsB;IACtB,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,8CAA8C;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9C,MAAM,UAAU,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QAE9C,IAAI,UAAU,KAAK,UAAU;YAAE,OAAO,UAAU,GAAG,UAAU,CAAC;QAC9D,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AApDD,gDAoDC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAC1B,QAA0B,EAC1B,gBAAwB,EACxB,MAAc;IAEd,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAE3E,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,8DAA8D;QAC9D,OAAO;YACL,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,gBAAgB;YAC3B,MAAM,EAAE,GAAG,MAAM,6BAA6B;SAC/C,CAAC;IACJ,CAAC;IAED,iBAAiB;IACjB,IAAA,mCAAiB,EAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAA,8BAAY,EAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAEvC,OAAO;QACL,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,WAAW,CAAC,EAAE;QACzB,YAAY,EAAE,gBAAgB;QAC9B,MAAM;QACN,YAAY,EAAE,WAAW,CAAC,SAAS;KACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,cAAc,CAAC,QAA0B;IAC7D,2CAA2C;IAC3C,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACvB,MAAM,cAAc,GAAG,IAAA,mCAAiB,EAAC,QAAQ,CAAC,CAAC;QACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,iDAAyB,GAAE,CAAC;IAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAE5C,+CAA+C;IAC/C,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,eAAe,IAAI,WAAW,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1E,MAAM,cAAc,GAAG,IAAA,mCAAiB,EAAC,QAAQ,CAAC,CAAC;QACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,cAAc,GAAG,IAAA,mCAAiB,EAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;IAC7E,CAAC;IAED,8CAA8C;IAC9C,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC;IACzD,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,aAAa,GAAG,IAAA,qCAAmB,EAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,aAAa,CAAC,CAAC;QACxF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,IAAI,IAAA,iCAAe,EAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC,EAAE,CAAC;QACjD,OAAO,MAAM,aAAa,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,2BAA2B,CAAC,CAAC;IACvF,CAAC;IAED,iBAAiB;IACjB,IAAI,YAAY,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9C,OAAO,MAAM,aAAa,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,6BAA6B,CAAC,CAAC;IACzF,CAAC;IAED,6CAA6C;IAC7C,IAAI,KAAK,GAAG,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,EAAE,oBAAoB,IAAI,CAAC,CAAC;IAE9D,IAAI,QAAQ,GAAG,SAAS,EAAE,CAAC;QACzB,sCAAsC;QACtC,aAAa,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,EAAE,gBAAgB,IAAI,CAAC,CAAC,CAAC;QACpF,OAAO,MAAM,aAAa,CACxB,QAAQ,EACR,cAAc,CAAC,EAAE,EACjB,oBAAoB,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC5C,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,cAAc,CAAC,EAAE;QAC5B,YAAY,EAAE,QAAQ;KACvB,CAAC;AACJ,CAAC;AAjED,wCAiEC;AAED;;;GAGG;AACI,KAAK,UAAU,cAAc,CAAC,QAA0B;IAS7D,MAAM,QAAQ,GAAG,IAAA,qCAAmB,EAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,IAAA,mCAAiB,EAAC,QAAQ,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC7B,IAAI,KAAK,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACjC,KAAK,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAE5D,OAAO;YACL,OAAO;YACP,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,IAAA,iCAAe,EAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;YAC7C,UAAU,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;YAC9C,SAAS,EAAE,cAAc,EAAE,EAAE,KAAK,OAAO,CAAC,EAAE;SAC7C,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAhCD,wCAgCC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cliproxy-command.d.ts","sourceRoot":"","sources":["../../src/commands/cliproxy-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAuqBH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DzE"}
1
+ {"version":3,"file":"cliproxy-command.d.ts","sourceRoot":"","sources":["../../src/commands/cliproxy-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AA23BH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFzE"}
@@ -47,6 +47,7 @@ const path = __importStar(require("path"));
47
47
  const auth_handler_1 = require("../cliproxy/auth-handler");
48
48
  const account_manager_1 = require("../cliproxy/account-manager");
49
49
  const quota_fetcher_1 = require("../cliproxy/quota-fetcher");
50
+ const quota_manager_1 = require("../cliproxy/quota-manager");
50
51
  const platform_detector_1 = require("../cliproxy/platform-detector");
51
52
  const profile_detector_1 = require("../auth/profile-detector");
52
53
  const model_catalog_1 = require("../cliproxy/model-catalog");
@@ -478,6 +479,15 @@ async function showHelp() {
478
479
  ['remove <name>', 'Remove a CLIProxy variant profile'],
479
480
  ],
480
481
  ],
482
+ [
483
+ 'Quota Management:',
484
+ [
485
+ ['default <account>', 'Set default account for rotation'],
486
+ ['pause <account>', 'Pause account (skip in rotation)'],
487
+ ['resume <account>', 'Resume paused account'],
488
+ ['quota', 'Show quota status for all accounts'],
489
+ ],
490
+ ],
481
491
  [
482
492
  'Proxy Lifecycle:',
483
493
  [
@@ -598,6 +608,170 @@ function formatQuotaBar(percentage) {
598
608
  return `[${filledChar.repeat(filled)}${' '.repeat(empty)}]`;
599
609
  }
600
610
  // ============================================================================
611
+ // QUOTA MANAGEMENT COMMANDS
612
+ // ============================================================================
613
+ async function handleSetDefault(args) {
614
+ await (0, ui_1.initUI)();
615
+ const parsed = parseProfileArgs(args);
616
+ if (!parsed.name) {
617
+ console.log((0, ui_1.fail)('Usage: ccs cliproxy default <account> [--provider <provider>]'));
618
+ console.log('');
619
+ console.log('Examples:');
620
+ console.log(' ccs cliproxy default ultra@gmail.com');
621
+ console.log(' ccs cliproxy default john --provider agy');
622
+ process.exit(1);
623
+ }
624
+ const provider = (parsed.provider || 'agy');
625
+ const account = (0, account_manager_1.findAccountByQuery)(provider, parsed.name);
626
+ if (!account) {
627
+ console.log((0, ui_1.fail)(`Account not found: ${parsed.name}`));
628
+ console.log('');
629
+ const accounts = (0, account_manager_1.getProviderAccounts)(provider);
630
+ if (accounts.length > 0) {
631
+ console.log('Available accounts:');
632
+ for (const acc of accounts) {
633
+ const badge = acc.isDefault ? (0, ui_1.color)(' (current default)', 'info') : '';
634
+ console.log(` - ${acc.email || acc.id}${badge}`);
635
+ }
636
+ }
637
+ else {
638
+ console.log(`No accounts found for provider: ${provider}`);
639
+ console.log(`Run: ccs ${provider} --auth`);
640
+ }
641
+ process.exit(1);
642
+ }
643
+ const success = (0, account_manager_1.setDefaultAccount)(provider, account.id);
644
+ if (success) {
645
+ console.log((0, ui_1.ok)(`Default account set to: ${account.email || account.id}`));
646
+ console.log((0, ui_1.info)(`Provider: ${provider}`));
647
+ }
648
+ else {
649
+ console.log((0, ui_1.fail)('Failed to set default account'));
650
+ process.exit(1);
651
+ }
652
+ }
653
+ async function handlePauseAccount(args) {
654
+ await (0, ui_1.initUI)();
655
+ const parsed = parseProfileArgs(args);
656
+ if (!parsed.name) {
657
+ console.log((0, ui_1.fail)('Usage: ccs cliproxy pause <account> [--provider <provider>]'));
658
+ console.log('');
659
+ console.log('Pauses an account so it will be skipped in quota rotation.');
660
+ process.exit(1);
661
+ }
662
+ const provider = (parsed.provider || 'agy');
663
+ const account = (0, account_manager_1.findAccountByQuery)(provider, parsed.name);
664
+ if (!account) {
665
+ console.log((0, ui_1.fail)(`Account not found: ${parsed.name}`));
666
+ process.exit(1);
667
+ }
668
+ if (account.paused) {
669
+ console.log((0, ui_1.warn)(`Account already paused: ${account.email || account.id}`));
670
+ console.log((0, ui_1.info)(`Paused at: ${account.pausedAt || 'unknown'}`));
671
+ return;
672
+ }
673
+ const success = (0, account_manager_1.pauseAccount)(provider, account.id);
674
+ if (success) {
675
+ console.log((0, ui_1.ok)(`Account paused: ${account.email || account.id}`));
676
+ console.log((0, ui_1.info)('Account will be skipped in quota rotation'));
677
+ }
678
+ else {
679
+ console.log((0, ui_1.fail)('Failed to pause account'));
680
+ process.exit(1);
681
+ }
682
+ }
683
+ async function handleResumeAccount(args) {
684
+ await (0, ui_1.initUI)();
685
+ const parsed = parseProfileArgs(args);
686
+ if (!parsed.name) {
687
+ console.log((0, ui_1.fail)('Usage: ccs cliproxy resume <account> [--provider <provider>]'));
688
+ console.log('');
689
+ console.log('Resumes a paused account for quota rotation.');
690
+ process.exit(1);
691
+ }
692
+ const provider = (parsed.provider || 'agy');
693
+ const account = (0, account_manager_1.findAccountByQuery)(provider, parsed.name);
694
+ if (!account) {
695
+ console.log((0, ui_1.fail)(`Account not found: ${parsed.name}`));
696
+ process.exit(1);
697
+ }
698
+ if (!account.paused) {
699
+ console.log((0, ui_1.warn)(`Account is not paused: ${account.email || account.id}`));
700
+ return;
701
+ }
702
+ const success = (0, account_manager_1.resumeAccount)(provider, account.id);
703
+ if (success) {
704
+ console.log((0, ui_1.ok)(`Account resumed: ${account.email || account.id}`));
705
+ console.log((0, ui_1.info)('Account is now active in quota rotation'));
706
+ }
707
+ else {
708
+ console.log((0, ui_1.fail)('Failed to resume account'));
709
+ process.exit(1);
710
+ }
711
+ }
712
+ async function handleQuotaStatus() {
713
+ await (0, ui_1.initUI)();
714
+ console.log((0, ui_1.header)('Quota Status'));
715
+ console.log('');
716
+ const provider = 'agy';
717
+ const accounts = (0, account_manager_1.getProviderAccounts)(provider);
718
+ if (accounts.length === 0) {
719
+ console.log((0, ui_1.info)('No Antigravity accounts configured'));
720
+ console.log(` Run: ${(0, ui_1.color)('ccs agy --auth', 'command')} to authenticate`);
721
+ return;
722
+ }
723
+ console.log((0, ui_1.dim)('Fetching quotas...'));
724
+ const quotaResult = await (0, quota_fetcher_1.fetchAllProviderQuotas)(provider);
725
+ // Build table rows
726
+ const rows = [];
727
+ for (const account of accounts) {
728
+ const quotaData = quotaResult.accounts.find((q) => q.account.id === account.id);
729
+ const quota = quotaData?.quota;
730
+ // Calculate average quota
731
+ let avgQuota = 'N/A';
732
+ if (quota?.success && quota.models.length > 0) {
733
+ const avg = Math.round(quota.models.reduce((sum, m) => sum + m.percentage, 0) / quota.models.length);
734
+ avgQuota = `${avg}%`;
735
+ }
736
+ // Build status badges
737
+ const statusParts = [];
738
+ if (account.paused)
739
+ statusParts.push((0, ui_1.color)('PAUSED', 'warning'));
740
+ if ((0, quota_manager_1.isOnCooldown)(provider, account.id))
741
+ statusParts.push((0, ui_1.color)('COOLDOWN', 'warning'));
742
+ const defaultMark = account.isDefault ? (0, ui_1.color)('*', 'success') : ' ';
743
+ const tier = account.tier || 'unknown';
744
+ const status = statusParts.join(', ');
745
+ rows.push([
746
+ defaultMark,
747
+ account.nickname || account.email || account.id,
748
+ tier,
749
+ avgQuota,
750
+ status,
751
+ ]);
752
+ }
753
+ console.log('');
754
+ console.log((0, ui_1.table)(rows, {
755
+ head: ['', 'Account', 'Tier', 'Quota', 'Status'],
756
+ colWidths: [3, 30, 10, 10, 20],
757
+ }));
758
+ console.log('');
759
+ console.log((0, ui_1.info)(`Default account marked with ${(0, ui_1.color)('*', 'success')}`));
760
+ console.log('');
761
+ // Show summary of paused/cooldown accounts
762
+ const pausedCount = accounts.filter((a) => a.paused).length;
763
+ const cooldownCount = accounts.filter((a) => (0, quota_manager_1.isOnCooldown)(provider, a.id)).length;
764
+ if (pausedCount > 0) {
765
+ console.log((0, ui_1.warn)(`${pausedCount} account(s) paused - use 'ccs cliproxy resume <account>' to re-enable`));
766
+ }
767
+ if (cooldownCount > 0) {
768
+ console.log((0, ui_1.info)(`${cooldownCount} account(s) on cooldown (exhausted recently)`));
769
+ }
770
+ if (pausedCount > 0 || cooldownCount > 0) {
771
+ console.log('');
772
+ }
773
+ }
774
+ // ============================================================================
601
775
  // MAIN ROUTER
602
776
  // ============================================================================
603
777
  async function handleCliproxyCommand(args) {
@@ -631,6 +805,23 @@ async function handleCliproxyCommand(args) {
631
805
  await handleDoctor();
632
806
  return;
633
807
  }
808
+ // Quota management commands
809
+ if (command === 'default') {
810
+ await handleSetDefault(args.slice(1));
811
+ return;
812
+ }
813
+ if (command === 'pause') {
814
+ await handlePauseAccount(args.slice(1));
815
+ return;
816
+ }
817
+ if (command === 'resume') {
818
+ await handleResumeAccount(args.slice(1));
819
+ return;
820
+ }
821
+ if (command === 'quota') {
822
+ await handleQuotaStatus();
823
+ return;
824
+ }
634
825
  const installIdx = args.indexOf('--install');
635
826
  if (installIdx !== -1) {
636
827
  let version = args[installIdx + 1];