@kaitranntt/ccs 7.36.0-dev.6 → 7.37.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 (116) hide show
  1. package/dist/cliproxy/account-manager.d.ts +5 -207
  2. package/dist/cliproxy/account-manager.d.ts.map +1 -1
  3. package/dist/cliproxy/account-manager.js +35 -795
  4. package/dist/cliproxy/account-manager.js.map +1 -1
  5. package/dist/cliproxy/accounts/bulk-ops.d.ts +22 -0
  6. package/dist/cliproxy/accounts/bulk-ops.d.ts.map +1 -0
  7. package/dist/cliproxy/accounts/bulk-ops.js +88 -0
  8. package/dist/cliproxy/accounts/bulk-ops.js.map +1 -0
  9. package/dist/cliproxy/accounts/index.d.ts +19 -0
  10. package/dist/cliproxy/accounts/index.d.ts.map +1 -0
  11. package/dist/cliproxy/accounts/index.js +54 -0
  12. package/dist/cliproxy/accounts/index.js.map +1 -0
  13. package/dist/cliproxy/accounts/query.d.ts +36 -0
  14. package/dist/cliproxy/accounts/query.d.ts.map +1 -0
  15. package/dist/cliproxy/accounts/query.js +94 -0
  16. package/dist/cliproxy/accounts/query.js.map +1 -0
  17. package/dist/cliproxy/accounts/registry.d.ts +74 -0
  18. package/dist/cliproxy/accounts/registry.d.ts.map +1 -0
  19. package/dist/cliproxy/accounts/registry.js +510 -0
  20. package/dist/cliproxy/accounts/registry.js.map +1 -0
  21. package/dist/cliproxy/accounts/token-file-ops.d.ts +64 -0
  22. package/dist/cliproxy/accounts/token-file-ops.d.ts.map +1 -0
  23. package/dist/cliproxy/accounts/token-file-ops.js +206 -0
  24. package/dist/cliproxy/accounts/token-file-ops.js.map +1 -0
  25. package/dist/cliproxy/accounts/types.d.ts +66 -0
  26. package/dist/cliproxy/accounts/types.d.ts.map +1 -0
  27. package/dist/cliproxy/accounts/types.js +12 -0
  28. package/dist/cliproxy/accounts/types.js.map +1 -0
  29. package/dist/cliproxy/auth/token-expiry-checker.d.ts.map +1 -1
  30. package/dist/cliproxy/auth/token-expiry-checker.js +5 -1
  31. package/dist/cliproxy/auth/token-expiry-checker.js.map +1 -1
  32. package/dist/cliproxy/cliproxy-executor.d.ts +11 -28
  33. package/dist/cliproxy/cliproxy-executor.d.ts.map +1 -1
  34. package/dist/cliproxy/cliproxy-executor.js +20 -1028
  35. package/dist/cliproxy/cliproxy-executor.js.map +1 -1
  36. package/dist/cliproxy/config/env-builder.d.ts +60 -0
  37. package/dist/cliproxy/config/env-builder.d.ts.map +1 -0
  38. package/dist/cliproxy/config/env-builder.js +311 -0
  39. package/dist/cliproxy/config/env-builder.js.map +1 -0
  40. package/dist/cliproxy/config/generator.d.ts +63 -0
  41. package/dist/cliproxy/config/generator.d.ts.map +1 -0
  42. package/dist/cliproxy/config/generator.js +336 -0
  43. package/dist/cliproxy/config/generator.js.map +1 -0
  44. package/dist/cliproxy/config/index.d.ts +10 -0
  45. package/dist/cliproxy/config/index.d.ts.map +1 -0
  46. package/dist/cliproxy/config/index.js +26 -0
  47. package/dist/cliproxy/config/index.js.map +1 -0
  48. package/dist/cliproxy/config/path-resolver.d.ts +47 -0
  49. package/dist/cliproxy/config/path-resolver.d.ts.map +1 -0
  50. package/dist/cliproxy/config/path-resolver.js +104 -0
  51. package/dist/cliproxy/config/path-resolver.js.map +1 -0
  52. package/dist/cliproxy/config/port-manager.d.ts +33 -0
  53. package/dist/cliproxy/config/port-manager.d.ts.map +1 -0
  54. package/dist/cliproxy/config/port-manager.js +68 -0
  55. package/dist/cliproxy/config/port-manager.js.map +1 -0
  56. package/dist/cliproxy/config/thinking-config.d.ts +39 -0
  57. package/dist/cliproxy/config/thinking-config.d.ts.map +1 -0
  58. package/dist/cliproxy/config/thinking-config.js +143 -0
  59. package/dist/cliproxy/config/thinking-config.js.map +1 -0
  60. package/dist/cliproxy/config-generator.d.ts +9 -221
  61. package/dist/cliproxy/config-generator.d.ts.map +1 -1
  62. package/dist/cliproxy/config-generator.js +12 -856
  63. package/dist/cliproxy/config-generator.js.map +1 -1
  64. package/dist/cliproxy/executor/env-resolver.d.ts +45 -0
  65. package/dist/cliproxy/executor/env-resolver.d.ts.map +1 -0
  66. package/dist/cliproxy/executor/env-resolver.js +106 -0
  67. package/dist/cliproxy/executor/env-resolver.js.map +1 -0
  68. package/dist/cliproxy/executor/index.d.ts +24 -0
  69. package/dist/cliproxy/executor/index.d.ts.map +1 -0
  70. package/dist/cliproxy/executor/index.js +669 -0
  71. package/dist/cliproxy/executor/index.js.map +1 -0
  72. package/dist/cliproxy/executor/lifecycle-manager.d.ts +33 -0
  73. package/dist/cliproxy/executor/lifecycle-manager.d.ts.map +1 -0
  74. package/dist/cliproxy/executor/lifecycle-manager.js +161 -0
  75. package/dist/cliproxy/executor/lifecycle-manager.js.map +1 -0
  76. package/dist/cliproxy/executor/retry-handler.d.ts +27 -0
  77. package/dist/cliproxy/executor/retry-handler.d.ts.map +1 -0
  78. package/dist/cliproxy/executor/retry-handler.js +109 -0
  79. package/dist/cliproxy/executor/retry-handler.js.map +1 -0
  80. package/dist/cliproxy/executor/session-bridge.d.ts +30 -0
  81. package/dist/cliproxy/executor/session-bridge.d.ts.map +1 -0
  82. package/dist/cliproxy/executor/session-bridge.js +232 -0
  83. package/dist/cliproxy/executor/session-bridge.js.map +1 -0
  84. package/dist/commands/cliproxy/auth-subcommand.d.ts +10 -0
  85. package/dist/commands/cliproxy/auth-subcommand.d.ts.map +1 -0
  86. package/dist/commands/cliproxy/auth-subcommand.js +55 -0
  87. package/dist/commands/cliproxy/auth-subcommand.js.map +1 -0
  88. package/dist/commands/cliproxy/help-subcommand.d.ts +8 -0
  89. package/dist/commands/cliproxy/help-subcommand.d.ts.map +1 -0
  90. package/dist/commands/cliproxy/help-subcommand.js +87 -0
  91. package/dist/commands/cliproxy/help-subcommand.js.map +1 -0
  92. package/dist/commands/cliproxy/index.d.ts +11 -0
  93. package/dist/commands/cliproxy/index.d.ts.map +1 -0
  94. package/dist/commands/cliproxy/index.js +196 -0
  95. package/dist/commands/cliproxy/index.js.map +1 -0
  96. package/dist/commands/cliproxy/install-subcommand.d.ts +14 -0
  97. package/dist/commands/cliproxy/install-subcommand.d.ts.map +1 -0
  98. package/dist/commands/cliproxy/install-subcommand.js +112 -0
  99. package/dist/commands/cliproxy/install-subcommand.js.map +1 -0
  100. package/dist/commands/cliproxy/proxy-lifecycle-subcommand.d.ts +10 -0
  101. package/dist/commands/cliproxy/proxy-lifecycle-subcommand.d.ts.map +1 -0
  102. package/dist/commands/cliproxy/proxy-lifecycle-subcommand.js +54 -0
  103. package/dist/commands/cliproxy/proxy-lifecycle-subcommand.js.map +1 -0
  104. package/dist/commands/cliproxy/quota-subcommand.d.ts +16 -0
  105. package/dist/commands/cliproxy/quota-subcommand.d.ts.map +1 -0
  106. package/dist/commands/cliproxy/quota-subcommand.js +383 -0
  107. package/dist/commands/cliproxy/quota-subcommand.js.map +1 -0
  108. package/dist/commands/cliproxy/variant-subcommand.d.ts +11 -0
  109. package/dist/commands/cliproxy/variant-subcommand.d.ts.map +1 -0
  110. package/dist/commands/cliproxy/variant-subcommand.js +292 -0
  111. package/dist/commands/cliproxy/variant-subcommand.js.map +1 -0
  112. package/dist/commands/cliproxy-command.d.ts +4 -15
  113. package/dist/commands/cliproxy-command.d.ts.map +1 -1
  114. package/dist/commands/cliproxy-command.js +5 -1090
  115. package/dist/commands/cliproxy-command.js.map +1 -1
  116. package/package.json +1 -1
@@ -2,1098 +2,13 @@
2
2
  /**
3
3
  * CLIProxy Command Handler
4
4
  *
5
- * Manages CLIProxyAPI binary installation and profile variants.
5
+ * DEPRECATED: This file is a backwards-compatible re-export shim.
6
+ * The actual implementation has been modularized into src/commands/cliproxy/
6
7
  *
7
- * Binary commands:
8
- * ccs cliproxy Show current version
9
- * ccs cliproxy --install <ver> Install specific version
10
- * ccs cliproxy --latest Install latest version
11
- *
12
- * Profile commands:
13
- * ccs cliproxy create <name> Create CLIProxy variant profile
14
- * ccs cliproxy list List all CLIProxy variant profiles
15
- * ccs cliproxy remove <name> Remove CLIProxy variant profile
16
- *
17
- * Supports dual-mode configuration:
18
- * - Unified YAML format (config.yaml) when CCS_UNIFIED_CONFIG=1 or config.yaml exists
19
- * - Legacy JSON format (config.json) as fallback
8
+ * See src/commands/cliproxy/index.ts for the new dispatcher.
20
9
  */
21
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
- if (k2 === undefined) k2 = k;
23
- var desc = Object.getOwnPropertyDescriptor(m, k);
24
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
- desc = { enumerable: true, get: function() { return m[k]; } };
26
- }
27
- Object.defineProperty(o, k2, desc);
28
- }) : (function(o, m, k, k2) {
29
- if (k2 === undefined) k2 = k;
30
- o[k2] = m[k];
31
- }));
32
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
- Object.defineProperty(o, "default", { enumerable: true, value: v });
34
- }) : function(o, v) {
35
- o["default"] = v;
36
- });
37
- var __importStar = (this && this.__importStar) || function (mod) {
38
- if (mod && mod.__esModule) return mod;
39
- var result = {};
40
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
41
- __setModuleDefault(result, mod);
42
- return result;
43
- };
44
10
  Object.defineProperty(exports, "__esModule", { value: true });
45
11
  exports.handleCliproxyCommand = void 0;
46
- const path = __importStar(require("path"));
47
- const auth_handler_1 = require("../cliproxy/auth-handler");
48
- const account_manager_1 = require("../cliproxy/account-manager");
49
- const quota_fetcher_1 = require("../cliproxy/quota-fetcher");
50
- const quota_fetcher_codex_1 = require("../cliproxy/quota-fetcher-codex");
51
- const quota_fetcher_gemini_cli_1 = require("../cliproxy/quota-fetcher-gemini-cli");
52
- const quota_manager_1 = require("../cliproxy/quota-manager");
53
- const platform_detector_1 = require("../cliproxy/platform-detector");
54
- const profile_detector_1 = require("../auth/profile-detector");
55
- const model_catalog_1 = require("../cliproxy/model-catalog");
56
- const unified_config_loader_1 = require("../config/unified-config-loader");
57
- const ui_1 = require("../utils/ui");
58
- const prompt_1 = require("../utils/prompt");
59
- // Import services
60
- const services_1 = require("../cliproxy/services");
61
- // Import sync handler
62
- const cliproxy_sync_handler_1 = require("./cliproxy-sync-handler");
63
- // ============================================================================
64
- // ARGUMENT PARSING
65
- // ============================================================================
66
- /**
67
- * Parse --provider flag from args for quota command
68
- * Returns the provider filter value and remaining args
69
- * Accepts: agy, codex, gemini, gemini-cli, all
70
- */
71
- function parseProviderArg(args) {
72
- const providerIdx = args.indexOf('--provider');
73
- if (providerIdx === -1) {
74
- // Also check for --provider=value format
75
- const providerEqualsIdx = args.findIndex((a) => a.startsWith('--provider='));
76
- if (providerEqualsIdx !== -1) {
77
- const value = args[providerEqualsIdx].split('=')[1]?.toLowerCase() || '';
78
- const remainingArgs = [...args];
79
- remainingArgs.splice(providerEqualsIdx, 1);
80
- // Handle empty value
81
- if (!value) {
82
- console.error((0, ui_1.warn)('--provider requires a value. Valid options: agy, codex, gemini, gemini-cli, all'));
83
- return { provider: 'all', remainingArgs };
84
- }
85
- // Normalize gemini-cli to gemini
86
- const normalized = value === 'gemini-cli' ? 'gemini' : value;
87
- if (normalized !== 'agy' &&
88
- normalized !== 'codex' &&
89
- normalized !== 'gemini' &&
90
- normalized !== 'all') {
91
- console.error((0, ui_1.warn)(`Invalid provider '${value}'. Valid options: agy, codex, gemini, gemini-cli, all`));
92
- return { provider: 'all', remainingArgs };
93
- }
94
- return { provider: normalized, remainingArgs };
95
- }
96
- return { provider: 'all', remainingArgs: args };
97
- }
98
- const value = args[providerIdx + 1]?.toLowerCase() || 'all';
99
- const remainingArgs = [...args];
100
- remainingArgs.splice(providerIdx, 2);
101
- // Normalize gemini-cli to gemini
102
- const normalized = value === 'gemini-cli' ? 'gemini' : value;
103
- if (normalized !== 'agy' &&
104
- normalized !== 'codex' &&
105
- normalized !== 'gemini' &&
106
- normalized !== 'all') {
107
- console.error((0, ui_1.warn)(`Invalid provider '${value}'. Valid options: agy, codex, gemini, gemini-cli, all`));
108
- return { provider: 'all', remainingArgs };
109
- }
110
- return { provider: normalized, remainingArgs };
111
- }
112
- /**
113
- * Parse --backend flag from args
114
- * Returns the backend value and remaining args without --backend flag
115
- */
116
- function parseBackendArg(args) {
117
- const backendIdx = args.indexOf('--backend');
118
- if (backendIdx === -1) {
119
- // Also check for --backend=value format
120
- const backendEqualsIdx = args.findIndex((a) => a.startsWith('--backend='));
121
- if (backendEqualsIdx !== -1) {
122
- const value = args[backendEqualsIdx].split('=')[1];
123
- if (value !== 'original' && value !== 'plus') {
124
- (0, ui_1.warn)(`Invalid backend '${value}'. Valid options: original, plus`);
125
- return { backend: undefined, remainingArgs: args };
126
- }
127
- const remainingArgs = [...args];
128
- remainingArgs.splice(backendEqualsIdx, 1);
129
- return { backend: value, remainingArgs };
130
- }
131
- return { backend: undefined, remainingArgs: args };
132
- }
133
- const value = args[backendIdx + 1];
134
- if (value !== 'original' && value !== 'plus') {
135
- (0, ui_1.warn)(`Invalid backend '${value}'. Valid options: original, plus`);
136
- return { backend: undefined, remainingArgs: args };
137
- }
138
- const remainingArgs = [...args];
139
- remainingArgs.splice(backendIdx, 2);
140
- return { backend: value, remainingArgs };
141
- }
142
- /**
143
- * Get effective backend (CLI flag > config.yaml > default)
144
- */
145
- function getEffectiveBackend(cliBackend) {
146
- if (cliBackend)
147
- return cliBackend;
148
- const config = (0, unified_config_loader_1.loadOrCreateUnifiedConfig)();
149
- return config.cliproxy?.backend ?? platform_detector_1.DEFAULT_BACKEND;
150
- }
151
- /**
152
- * Get display label for backend
153
- */
154
- function getBackendLabel(backend) {
155
- return backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy';
156
- }
157
- function parseProfileArgs(args) {
158
- const result = {};
159
- for (let i = 0; i < args.length; i++) {
160
- const arg = args[i];
161
- if (arg === '--provider' && args[i + 1]) {
162
- result.provider = args[++i];
163
- }
164
- else if (arg === '--model' && args[i + 1]) {
165
- result.model = args[++i];
166
- }
167
- else if (arg === '--account' && args[i + 1]) {
168
- result.account = args[++i];
169
- }
170
- else if (arg === '--force') {
171
- result.force = true;
172
- }
173
- else if (arg === '--yes' || arg === '-y') {
174
- result.yes = true;
175
- }
176
- else if (!arg.startsWith('-') && !result.name) {
177
- result.name = arg;
178
- }
179
- }
180
- return result;
181
- }
182
- function formatModelOption(model) {
183
- const tierBadge = model.tier === 'ultra'
184
- ? (0, ui_1.color)(' [Ultra]', 'warning')
185
- : model.tier === 'pro'
186
- ? (0, ui_1.color)(' [Pro]', 'warning')
187
- : '';
188
- return `${model.name}${tierBadge}`;
189
- }
190
- // ============================================================================
191
- // COMMAND HANDLERS
192
- // ============================================================================
193
- async function handleCreate(args) {
194
- await (0, ui_1.initUI)();
195
- const { backend } = parseBackendArg(args);
196
- const effectiveBackend = getEffectiveBackend(backend);
197
- const parsedArgs = parseProfileArgs(args);
198
- console.log((0, ui_1.header)(`Create ${getBackendLabel(effectiveBackend)} Variant`));
199
- console.log('');
200
- // Step 1: Profile name
201
- let name = parsedArgs.name;
202
- if (!name) {
203
- name = await prompt_1.InteractivePrompt.input('Variant name (e.g., g3, flash, pro)', {
204
- validate: services_1.validateProfileName,
205
- });
206
- }
207
- else {
208
- const error = (0, services_1.validateProfileName)(name);
209
- if (error) {
210
- console.log((0, ui_1.fail)(error));
211
- process.exit(1);
212
- }
213
- }
214
- if ((0, services_1.variantExists)(name) && !parsedArgs.force) {
215
- console.log((0, ui_1.fail)(`Variant '${name}' already exists`));
216
- console.log(` Use ${(0, ui_1.color)('--force', 'command')} to overwrite`);
217
- process.exit(1);
218
- }
219
- // Step 2: Provider selection
220
- let provider = parsedArgs.provider;
221
- if (!provider) {
222
- const providerOptions = profile_detector_1.CLIPROXY_PROFILES.map((p) => ({
223
- id: p,
224
- label: p.charAt(0).toUpperCase() + p.slice(1),
225
- }));
226
- provider = (await prompt_1.InteractivePrompt.selectFromList('Select provider:', providerOptions));
227
- }
228
- else if (!profile_detector_1.CLIPROXY_PROFILES.includes(provider)) {
229
- console.log((0, ui_1.fail)(`Invalid provider: ${provider}`));
230
- console.log(` Available: ${profile_detector_1.CLIPROXY_PROFILES.join(', ')}`);
231
- process.exit(1);
232
- }
233
- // Step 2.5: Account selection
234
- let account = parsedArgs.account;
235
- const providerAccounts = (0, account_manager_1.getProviderAccounts)(provider);
236
- if (!account) {
237
- if (providerAccounts.length === 0) {
238
- console.log('');
239
- console.log((0, ui_1.warn)(`No accounts authenticated for ${provider}`));
240
- console.log('');
241
- const shouldAuth = await prompt_1.InteractivePrompt.confirm(`Authenticate with ${provider} now?`, {
242
- default: true,
243
- });
244
- if (!shouldAuth) {
245
- console.log('');
246
- console.log((0, ui_1.info)('Run authentication first:'));
247
- console.log(` ${(0, ui_1.color)(`ccs ${provider} --auth`, 'command')}`);
248
- process.exit(0);
249
- }
250
- console.log('');
251
- const newAccount = await (0, auth_handler_1.triggerOAuth)(provider, {
252
- add: true,
253
- verbose: args.includes('--verbose'),
254
- });
255
- if (!newAccount) {
256
- console.log((0, ui_1.fail)('Authentication failed'));
257
- process.exit(1);
258
- }
259
- account = newAccount.id;
260
- console.log('');
261
- console.log((0, ui_1.ok)(`Authenticated as ${newAccount.email || newAccount.id}`));
262
- }
263
- else if (providerAccounts.length === 1) {
264
- account = providerAccounts[0].id;
265
- }
266
- else {
267
- const ADD_NEW_ID = '__add_new__';
268
- const accountOptions = [
269
- ...providerAccounts.map((acc) => ({
270
- id: acc.id,
271
- label: `${acc.email || acc.id}${acc.isDefault ? ' (default)' : ''}`,
272
- })),
273
- { id: ADD_NEW_ID, label: (0, ui_1.color)('[+ Add new account...]', 'info') },
274
- ];
275
- const defaultIdx = providerAccounts.findIndex((a) => a.isDefault);
276
- const selectedAccount = await prompt_1.InteractivePrompt.selectFromList('Select account:', accountOptions, { defaultIndex: defaultIdx >= 0 ? defaultIdx : 0 });
277
- if (selectedAccount === ADD_NEW_ID) {
278
- console.log('');
279
- const newAccount = await (0, auth_handler_1.triggerOAuth)(provider, {
280
- add: true,
281
- verbose: args.includes('--verbose'),
282
- });
283
- if (!newAccount) {
284
- console.log((0, ui_1.fail)('Authentication failed'));
285
- process.exit(1);
286
- }
287
- account = newAccount.id;
288
- console.log('');
289
- console.log((0, ui_1.ok)(`Authenticated as ${newAccount.email || newAccount.id}`));
290
- }
291
- else {
292
- account = selectedAccount;
293
- }
294
- }
295
- }
296
- else {
297
- const exists = providerAccounts.find((a) => a.id === account);
298
- if (!exists) {
299
- console.log((0, ui_1.fail)(`Account '${account}' not found for ${provider}`));
300
- console.log('');
301
- console.log('Available accounts:');
302
- providerAccounts.forEach((a) => console.log(` - ${a.email || a.id}${a.isDefault ? ' (default)' : ''}`));
303
- process.exit(1);
304
- }
305
- }
306
- // Step 3: Model selection
307
- let model = parsedArgs.model;
308
- if (!model) {
309
- if ((0, model_catalog_1.supportsModelConfig)(provider)) {
310
- const catalog = (0, model_catalog_1.getProviderCatalog)(provider);
311
- if (catalog) {
312
- const modelOptions = catalog.models.map((m) => ({ id: m.id, label: formatModelOption(m) }));
313
- const defaultIdx = catalog.models.findIndex((m) => m.id === catalog.defaultModel);
314
- model = await prompt_1.InteractivePrompt.selectFromList('Select model:', modelOptions, {
315
- defaultIndex: defaultIdx >= 0 ? defaultIdx : 0,
316
- });
317
- }
318
- }
319
- if (!model) {
320
- model = await prompt_1.InteractivePrompt.input('Model name', {
321
- validate: (val) => (val ? null : 'Model is required'),
322
- });
323
- }
324
- }
325
- // Create variant
326
- console.log('');
327
- console.log((0, ui_1.info)(`Creating ${getBackendLabel(effectiveBackend)} variant...`));
328
- const result = (0, services_1.createVariant)(name, provider, model, account);
329
- if (!result.success) {
330
- console.log((0, ui_1.fail)(`Failed to create variant: ${result.error}`));
331
- process.exit(1);
332
- }
333
- console.log('');
334
- const configType = (0, unified_config_loader_1.isUnifiedMode)()
335
- ? 'CLIProxy Variant Created (Unified Config)'
336
- : 'CLIProxy Variant Created';
337
- const settingsDisplay = (0, unified_config_loader_1.isUnifiedMode)()
338
- ? '~/.ccs/config.yaml'
339
- : `~/.ccs/${path.basename(result.settingsPath || '')}`;
340
- const portInfo = result.variant?.port ? `Port: ${result.variant.port}\n` : '';
341
- console.log((0, ui_1.infoBox)(`Variant: ${name}\nProvider: ${provider}\nModel: ${model}\n${portInfo}${account ? `Account: ${account}\n` : ''}${(0, unified_config_loader_1.isUnifiedMode)() ? 'Config' : 'Settings'}: ${settingsDisplay}`, configType));
342
- console.log('');
343
- console.log((0, ui_1.header)('Usage'));
344
- console.log(` ${(0, ui_1.color)(`ccs ${name} "your prompt"`, 'command')}`);
345
- console.log('');
346
- console.log((0, ui_1.dim)('To change model later:'));
347
- console.log(` ${(0, ui_1.color)(`ccs ${name} --config`, 'command')}`);
348
- console.log('');
349
- }
350
- async function handleList() {
351
- await (0, ui_1.initUI)();
352
- console.log((0, ui_1.header)('CLIProxy Profiles'));
353
- console.log('');
354
- // Built-in profiles
355
- console.log((0, ui_1.subheader)('Built-in Profiles'));
356
- const authStatuses = (0, auth_handler_1.getAllAuthStatus)();
357
- for (const status of authStatuses) {
358
- const oauthConfig = (0, auth_handler_1.getOAuthConfig)(status.provider);
359
- const icon = status.authenticated ? (0, ui_1.ok)('') : (0, ui_1.warn)('');
360
- const authLabel = status.authenticated
361
- ? (0, ui_1.color)('authenticated', 'success')
362
- : (0, ui_1.dim)('not authenticated');
363
- const lastAuthStr = status.lastAuth ? (0, ui_1.dim)(` (${status.lastAuth.toLocaleDateString()})`) : '';
364
- console.log(` ${icon} ${(0, ui_1.color)(status.provider, 'command').padEnd(18)} ${oauthConfig.displayName.padEnd(16)} ${authLabel}${lastAuthStr}`);
365
- }
366
- console.log('');
367
- console.log((0, ui_1.dim)(' To authenticate: ccs <provider> --auth'));
368
- console.log((0, ui_1.dim)(' To logout: ccs <provider> --logout'));
369
- console.log('');
370
- // Custom variants
371
- const variants = (0, services_1.listVariants)();
372
- const variantNames = Object.keys(variants);
373
- if (variantNames.length > 0) {
374
- console.log((0, ui_1.subheader)('Custom Variants'));
375
- const rows = variantNames.map((name) => {
376
- const variant = variants[name];
377
- const portStr = variant.port ? String(variant.port) : '-';
378
- return [name, variant.provider, portStr, variant.settings || '-'];
379
- });
380
- console.log((0, ui_1.table)(rows, { head: ['Variant', 'Provider', 'Port', 'Settings'], colWidths: [15, 12, 8, 30] }));
381
- console.log('');
382
- console.log((0, ui_1.dim)(`Total: ${variantNames.length} custom variant(s)`));
383
- console.log('');
384
- }
385
- console.log((0, ui_1.dim)('To create a custom variant:'));
386
- console.log(` ${(0, ui_1.color)('ccs cliproxy create', 'command')}`);
387
- console.log('');
388
- }
389
- async function handleRemove(args) {
390
- await (0, ui_1.initUI)();
391
- const parsedArgs = parseProfileArgs(args);
392
- const variants = (0, services_1.listVariants)();
393
- const variantNames = Object.keys(variants);
394
- if (variantNames.length === 0) {
395
- console.log((0, ui_1.warn)('No CLIProxy variants to remove'));
396
- process.exit(0);
397
- }
398
- let name = parsedArgs.name;
399
- if (!name) {
400
- console.log((0, ui_1.header)('Remove CLIProxy Variant'));
401
- console.log('');
402
- console.log('Available variants:');
403
- variantNames.forEach((n, i) => console.log(` ${i + 1}. ${n} (${variants[n].provider})`));
404
- console.log('');
405
- name = await prompt_1.InteractivePrompt.input('Variant name to remove', {
406
- validate: (val) => {
407
- if (!val)
408
- return 'Variant name is required';
409
- if (!variantNames.includes(val))
410
- return `Variant '${val}' not found`;
411
- return null;
412
- },
413
- });
414
- }
415
- if (!variantNames.includes(name)) {
416
- console.log((0, ui_1.fail)(`Variant '${name}' not found`));
417
- console.log('');
418
- console.log('Available variants:');
419
- variantNames.forEach((n) => console.log(` - ${n}`));
420
- process.exit(1);
421
- }
422
- const variant = variants[name];
423
- console.log('');
424
- console.log(`Variant '${(0, ui_1.color)(name, 'command')}' will be removed.`);
425
- console.log(` Provider: ${variant.provider}`);
426
- if (variant.port) {
427
- console.log(` Port: ${variant.port}`);
428
- }
429
- console.log(` Settings: ${variant.settings || '-'}`);
430
- console.log('');
431
- const confirmed = parsedArgs.yes || (await prompt_1.InteractivePrompt.confirm('Delete this variant?', { default: false }));
432
- if (!confirmed) {
433
- console.log((0, ui_1.info)('Cancelled'));
434
- process.exit(0);
435
- }
436
- const result = (0, services_1.removeVariant)(name);
437
- if (!result.success) {
438
- console.log((0, ui_1.fail)(`Failed to remove variant: ${result.error}`));
439
- process.exit(1);
440
- }
441
- console.log((0, ui_1.ok)(`Variant removed: ${name}`));
442
- console.log('');
443
- }
444
- async function handleStop() {
445
- await (0, ui_1.initUI)();
446
- console.log((0, ui_1.header)('Stop CLIProxy'));
447
- console.log('');
448
- const result = await (0, services_1.stopProxy)();
449
- if (result.stopped) {
450
- console.log((0, ui_1.ok)(`CLIProxy stopped (PID ${result.pid})`));
451
- if (result.sessionCount && result.sessionCount > 0) {
452
- console.log((0, ui_1.info)(`${result.sessionCount} active session(s) were disconnected`));
453
- }
454
- }
455
- else {
456
- console.log((0, ui_1.warn)(result.error || 'Failed to stop CLIProxy'));
457
- }
458
- console.log('');
459
- }
460
- async function handleProxyStatus() {
461
- await (0, ui_1.initUI)();
462
- console.log((0, ui_1.header)('CLIProxy Status'));
463
- console.log('');
464
- const status = (0, services_1.getProxyStatus)();
465
- if (status.running) {
466
- console.log(` Status: ${(0, ui_1.color)('Running', 'success')}`);
467
- console.log(` PID: ${status.pid}`);
468
- console.log(` Port: ${status.port}`);
469
- console.log(` Sessions: ${status.sessionCount || 0} active`);
470
- if (status.startedAt) {
471
- console.log(` Started: ${new Date(status.startedAt).toLocaleString()}`);
472
- }
473
- console.log('');
474
- console.log((0, ui_1.dim)('To stop: ccs cliproxy stop'));
475
- }
476
- else {
477
- console.log(` Status: ${(0, ui_1.color)('Not running', 'warning')}`);
478
- console.log('');
479
- console.log((0, ui_1.dim)('CLIProxy starts automatically when you run ccs gemini, codex, etc.'));
480
- }
481
- console.log('');
482
- }
483
- async function showStatus(verbose, backend) {
484
- await (0, ui_1.initUI)();
485
- const status = (0, services_1.getBinaryStatus)(backend);
486
- console.log('');
487
- const backendLabel = getBackendLabel(backend);
488
- console.log((0, ui_1.color)(`${backendLabel} Status`, 'primary'));
489
- console.log('');
490
- console.log(` Backend: ${(0, ui_1.color)(backend, 'info')}${backend === platform_detector_1.DEFAULT_BACKEND ? (0, ui_1.dim)(' (default)') : ''}`);
491
- if (status.installed) {
492
- console.log(` Installed: ${(0, ui_1.color)('Yes', 'success')}`);
493
- const versionLabel = status.pinnedVersion
494
- ? `${(0, ui_1.color)(`v${status.currentVersion}`, 'info')} ${(0, ui_1.color)('(pinned)', 'warning')}`
495
- : (0, ui_1.color)(`v${status.currentVersion}`, 'info');
496
- console.log(` Version: ${versionLabel}`);
497
- console.log(` Binary: ${(0, ui_1.dim)(status.binaryPath)}`);
498
- }
499
- else {
500
- console.log(` Installed: ${(0, ui_1.color)('No', 'error')}`);
501
- console.log(` Fallback: ${(0, ui_1.color)(`v${status.fallbackVersion}`, 'info')}`);
502
- console.log(` ${(0, ui_1.dim)('Run "ccs gemini" or any provider to auto-install')}`);
503
- }
504
- const latestCheck = await (0, services_1.checkLatestVersion)();
505
- if (latestCheck.success && latestCheck.latestVersion) {
506
- console.log('');
507
- if (latestCheck.updateAvailable) {
508
- if (status.pinnedVersion) {
509
- console.log(` Latest: ${(0, ui_1.color)(`v${latestCheck.latestVersion}`, 'success')} ${(0, ui_1.dim)('(pinned to v' + status.pinnedVersion + ')')}`);
510
- console.log('');
511
- console.log(` ${(0, ui_1.dim)('Run "ccs cliproxy --update" to unpin and update')}`);
512
- }
513
- else {
514
- console.log(` Latest: ${(0, ui_1.color)(`v${latestCheck.latestVersion}`, 'success')} ${(0, ui_1.dim)('(update available)')}`);
515
- console.log('');
516
- console.log(` ${(0, ui_1.dim)('Run "ccs cliproxy --latest" to update')}`);
517
- }
518
- }
519
- else {
520
- console.log(` Latest: ${(0, ui_1.color)(`v${latestCheck.latestVersion}`, 'success')} ${(0, ui_1.dim)('(up to date)')}`);
521
- }
522
- }
523
- else if (verbose && latestCheck.error) {
524
- console.log(` Latest: ${(0, ui_1.dim)(`Could not fetch (${latestCheck.error})`)}`);
525
- }
526
- console.log('');
527
- console.log((0, ui_1.dim)('Run "ccs cliproxy --help" for all available commands'));
528
- console.log('');
529
- }
530
- async function handleInstallVersion(version, verbose, backend) {
531
- const label = getBackendLabel(backend);
532
- console.log((0, ui_1.info)(`Installing ${label} v${version}...`));
533
- console.log('');
534
- const result = await (0, services_1.installVersion)(version, verbose, backend);
535
- if (!result.success) {
536
- console.error('');
537
- console.error((0, ui_1.fail)(`Failed to install ${label} v${version}`));
538
- console.error(` ${result.error}`);
539
- console.error('');
540
- console.error('Possible causes:');
541
- console.error(' 1. Version does not exist on GitHub');
542
- console.error(' 2. Network connectivity issues');
543
- console.error(' 3. GitHub API rate limiting');
544
- console.error('');
545
- console.error('Check available versions at:');
546
- console.error(` https://github.com/${platform_detector_1.BACKEND_CONFIG[backend].repo}/releases`);
547
- process.exit(1);
548
- }
549
- console.log('');
550
- console.log((0, ui_1.ok)(`${label} v${version} installed (pinned)`));
551
- console.log('');
552
- console.log((0, ui_1.dim)('This version will be used until you run:'));
553
- console.log(` ${(0, ui_1.color)('ccs cliproxy --update', 'command')} ${(0, ui_1.dim)('# Update to latest and unpin')}`);
554
- console.log('');
555
- }
556
- async function handleInstallLatest(verbose, backend) {
557
- const label = getBackendLabel(backend);
558
- console.log((0, ui_1.info)(`Fetching latest ${label} version...`));
559
- const result = await (0, services_1.installLatest)(verbose, backend);
560
- if (!result.success) {
561
- console.error((0, ui_1.fail)(`Failed to install latest version: ${result.error}`));
562
- process.exit(1);
563
- }
564
- if (result.error?.startsWith('Already')) {
565
- console.log((0, ui_1.ok)(result.error));
566
- return;
567
- }
568
- console.log('');
569
- console.log((0, ui_1.ok)(`${label} updated to v${result.version}`));
570
- console.log((0, ui_1.dim)('Auto-update is now enabled.'));
571
- console.log('');
572
- }
573
- async function showHelp() {
574
- await (0, ui_1.initUI)();
575
- console.log('');
576
- console.log((0, ui_1.header)('CLIProxy Management'));
577
- console.log('');
578
- console.log((0, ui_1.subheader)('Usage:'));
579
- console.log(` ${(0, ui_1.color)('ccs cliproxy', 'command')} <command> [options]`);
580
- console.log('');
581
- const sections = [
582
- [
583
- 'Profile Commands:',
584
- [
585
- ['create [name]', 'Create new CLIProxy variant profile'],
586
- ['list', 'List all CLIProxy variant profiles'],
587
- ['remove <name>', 'Remove a CLIProxy variant profile'],
588
- ],
589
- ],
590
- [
591
- 'Local Sync:',
592
- [
593
- ['sync', 'Sync API profiles to local CLIProxy config'],
594
- ['sync --dry-run', 'Preview sync without applying'],
595
- ['sync --verbose', 'Show detailed sync information'],
596
- ],
597
- ],
598
- [
599
- 'Quota Management:',
600
- [
601
- ['default <account>', 'Set default account for rotation'],
602
- ['pause <account>', 'Pause account (skip in rotation)'],
603
- ['resume <account>', 'Resume paused account'],
604
- ['quota', 'Show quota status for all providers'],
605
- ['quota --provider <name>', 'Filter by provider (agy|codex|gemini)'],
606
- ],
607
- ],
608
- [
609
- 'Proxy Lifecycle:',
610
- [
611
- ['status', 'Show running CLIProxy status'],
612
- ['stop', 'Stop running CLIProxy instance'],
613
- ['doctor', 'Quota diagnostics and shared project detection'],
614
- ],
615
- ],
616
- [
617
- 'Binary Commands:',
618
- [
619
- ['--install <version>', 'Install and pin a specific version'],
620
- ['--latest', 'Install the latest version (no pin)'],
621
- ['--update', 'Unpin and update to latest version'],
622
- ],
623
- ],
624
- [
625
- 'Options:',
626
- [
627
- ['--backend <type>', 'Use specific backend: original | plus (default: from config)'],
628
- ['--verbose, -v', 'Show detailed quota fetch diagnostics'],
629
- ],
630
- ],
631
- ];
632
- for (const [title, cmds] of sections) {
633
- console.log((0, ui_1.subheader)(title));
634
- const maxLen = Math.max(...cmds.map(([cmd]) => cmd.length));
635
- for (const [cmd, desc] of cmds) {
636
- console.log(` ${(0, ui_1.color)(cmd.padEnd(maxLen + 2), 'command')} ${desc}`);
637
- }
638
- console.log('');
639
- }
640
- console.log((0, ui_1.dim)(' Note: CLIProxy now persists by default. Use "stop" to terminate.'));
641
- console.log('');
642
- console.log((0, ui_1.subheader)('Notes:'));
643
- console.log(` Default fallback version: ${(0, ui_1.color)((0, platform_detector_1.getFallbackVersion)(), 'info')}`);
644
- console.log(` Releases: ${(0, ui_1.color)(`https://github.com/${platform_detector_1.BACKEND_CONFIG[platform_detector_1.DEFAULT_BACKEND].repo}/releases`, 'path')}`);
645
- console.log('');
646
- }
647
- // ============================================================================
648
- // DOCTOR COMMAND - Quota diagnostics and shared project detection
649
- // ============================================================================
650
- async function handleDoctor(verbose = false) {
651
- await (0, ui_1.initUI)();
652
- console.log((0, ui_1.header)('CLIProxy Quota Diagnostics'));
653
- console.log('');
654
- // Check each OAuth provider (agy is the only one with quota)
655
- const provider = 'agy';
656
- const accounts = (0, account_manager_1.getProviderAccounts)(provider);
657
- if (accounts.length === 0) {
658
- console.log((0, ui_1.info)('No Antigravity accounts configured'));
659
- console.log(` Run: ${(0, ui_1.color)('ccs agy --auth', 'command')} to authenticate`);
660
- return;
661
- }
662
- console.log((0, ui_1.subheader)(`Antigravity Accounts (${accounts.length})`));
663
- console.log('');
664
- // Fetch quota for all accounts
665
- console.log((0, ui_1.dim)('Fetching quotas...'));
666
- const quotaResult = await (0, quota_fetcher_1.fetchAllProviderQuotas)(provider, verbose);
667
- // Display per-account quota status
668
- for (const { account, quota } of quotaResult.accounts) {
669
- const accountLabel = account.email || account.id || 'Unknown Account';
670
- const defaultBadge = account.isDefault ? (0, ui_1.color)(' (default)', 'info') : '';
671
- if (!quota.success) {
672
- console.log(` ${(0, ui_1.fail)(accountLabel)}${defaultBadge}`);
673
- console.log(` ${(0, ui_1.color)(quota.error || 'Failed to fetch quota', 'error')}`);
674
- if (quota.isUnprovisioned) {
675
- console.log(` ${(0, ui_1.warn)('Account not provisioned - open Gemini Code Assist in IDE first')}`);
676
- }
677
- console.log('');
678
- continue;
679
- }
680
- // Calculate overall quota health (guard against empty models array)
681
- const avgQuota = quota.models.length > 0
682
- ? quota.models.reduce((sum, m) => sum + m.percentage, 0) / quota.models.length
683
- : 0;
684
- const statusIcon = avgQuota > 50 ? (0, ui_1.ok)('') : avgQuota > 10 ? (0, ui_1.warn)('') : (0, ui_1.fail)('');
685
- console.log(` ${statusIcon}${accountLabel}${defaultBadge}`);
686
- if (quota.projectId) {
687
- console.log(` Project: ${(0, ui_1.dim)(quota.projectId)}`);
688
- }
689
- // Show model quotas
690
- for (const model of quota.models) {
691
- const bar = formatQuotaBar(model.percentage);
692
- console.log(` ${model.name.padEnd(20)} ${bar} ${model.percentage.toFixed(0)}%`);
693
- }
694
- console.log('');
695
- }
696
- // Check for shared GCP projects (critical warning)
697
- const sharedProjects = Object.entries(quotaResult.projectGroups).filter(([, accountIds]) => accountIds.length > 1);
698
- if (sharedProjects.length > 0) {
699
- console.log('');
700
- console.log((0, ui_1.subheader)('Shared Project Warning'));
701
- console.log('');
702
- for (const [projectId, accountIds] of sharedProjects) {
703
- console.log((0, ui_1.fail)(`Project ${projectId.substring(0, 20)}... shared by ${accountIds.length} accounts:`));
704
- for (const accountId of accountIds) {
705
- console.log(` - ${accountId}`);
706
- }
707
- console.log('');
708
- console.log((0, ui_1.warn)('These accounts share the same quota pool!'));
709
- console.log((0, ui_1.warn)('Failover between them will NOT help when quota is exhausted.'));
710
- console.log((0, ui_1.info)('Solution: Use accounts from different GCP projects.'));
711
- }
712
- }
713
- // Summary
714
- console.log('');
715
- console.log((0, ui_1.subheader)('Summary'));
716
- const healthyAccounts = quotaResult.accounts.filter(({ quota }) => quota.success && quota.models.some((m) => m.percentage > 5));
717
- console.log(` Accounts with quota: ${healthyAccounts.length}/${accounts.length}`);
718
- if (sharedProjects.length > 0) {
719
- console.log(` ${(0, ui_1.fail)(`Shared projects: ${sharedProjects.length} (failover limited)`)}`);
720
- }
721
- else if (accounts.length > 1) {
722
- console.log(` ${(0, ui_1.ok)('No shared projects (failover fully operational)')}`);
723
- }
724
- console.log('');
725
- }
726
- function formatQuotaBar(percentage) {
727
- const width = 20;
728
- const clampedPct = Math.max(0, Math.min(100, percentage));
729
- const filled = Math.round((clampedPct / 100) * width);
730
- const empty = width - filled;
731
- const filledChar = clampedPct > 50 ? '█' : clampedPct > 10 ? '▓' : '░';
732
- return `[${filledChar.repeat(filled)}${' '.repeat(empty)}]`;
733
- }
734
- // ============================================================================
735
- // QUOTA MANAGEMENT COMMANDS
736
- // ============================================================================
737
- async function handleSetDefault(args) {
738
- await (0, ui_1.initUI)();
739
- const parsed = parseProfileArgs(args);
740
- if (!parsed.name) {
741
- console.log((0, ui_1.fail)('Usage: ccs cliproxy default <account> [--provider <provider>]'));
742
- console.log('');
743
- console.log('Examples:');
744
- console.log(' ccs cliproxy default ultra@gmail.com');
745
- console.log(' ccs cliproxy default john --provider agy');
746
- process.exit(1);
747
- }
748
- const provider = (parsed.provider || 'agy');
749
- const account = (0, account_manager_1.findAccountByQuery)(provider, parsed.name);
750
- if (!account) {
751
- console.log((0, ui_1.fail)(`Account not found: ${parsed.name}`));
752
- console.log('');
753
- const accounts = (0, account_manager_1.getProviderAccounts)(provider);
754
- if (accounts.length > 0) {
755
- console.log('Available accounts:');
756
- for (const acc of accounts) {
757
- const badge = acc.isDefault ? (0, ui_1.color)(' (current default)', 'info') : '';
758
- console.log(` - ${acc.email || acc.id}${badge}`);
759
- }
760
- }
761
- else {
762
- console.log(`No accounts found for provider: ${provider}`);
763
- console.log(`Run: ccs ${provider} --auth`);
764
- }
765
- process.exit(1);
766
- }
767
- const success = (0, account_manager_1.setDefaultAccount)(provider, account.id);
768
- if (success) {
769
- console.log((0, ui_1.ok)(`Default account set to: ${account.email || account.id}`));
770
- console.log((0, ui_1.info)(`Provider: ${provider}`));
771
- }
772
- else {
773
- console.log((0, ui_1.fail)('Failed to set default account'));
774
- process.exit(1);
775
- }
776
- }
777
- async function handlePauseAccount(args) {
778
- await (0, ui_1.initUI)();
779
- const parsed = parseProfileArgs(args);
780
- if (!parsed.name) {
781
- console.log((0, ui_1.fail)('Usage: ccs cliproxy pause <account> [--provider <provider>]'));
782
- console.log('');
783
- console.log('Pauses an account so it will be skipped in quota rotation.');
784
- process.exit(1);
785
- }
786
- const provider = (parsed.provider || 'agy');
787
- const account = (0, account_manager_1.findAccountByQuery)(provider, parsed.name);
788
- if (!account) {
789
- console.log((0, ui_1.fail)(`Account not found: ${parsed.name}`));
790
- process.exit(1);
791
- }
792
- if (account.paused) {
793
- console.log((0, ui_1.warn)(`Account already paused: ${account.email || account.id}`));
794
- console.log((0, ui_1.info)(`Paused at: ${account.pausedAt || 'unknown'}`));
795
- return;
796
- }
797
- const success = (0, account_manager_1.pauseAccount)(provider, account.id);
798
- if (success) {
799
- console.log((0, ui_1.ok)(`Account paused: ${account.email || account.id}`));
800
- console.log((0, ui_1.info)('Account will be skipped in quota rotation'));
801
- }
802
- else {
803
- console.log((0, ui_1.fail)('Failed to pause account'));
804
- process.exit(1);
805
- }
806
- }
807
- async function handleResumeAccount(args) {
808
- await (0, ui_1.initUI)();
809
- const parsed = parseProfileArgs(args);
810
- if (!parsed.name) {
811
- console.log((0, ui_1.fail)('Usage: ccs cliproxy resume <account> [--provider <provider>]'));
812
- console.log('');
813
- console.log('Resumes a paused account for quota rotation.');
814
- process.exit(1);
815
- }
816
- const provider = (parsed.provider || 'agy');
817
- const account = (0, account_manager_1.findAccountByQuery)(provider, parsed.name);
818
- if (!account) {
819
- console.log((0, ui_1.fail)(`Account not found: ${parsed.name}`));
820
- process.exit(1);
821
- }
822
- if (!account.paused) {
823
- console.log((0, ui_1.warn)(`Account is not paused: ${account.email || account.id}`));
824
- return;
825
- }
826
- const success = (0, account_manager_1.resumeAccount)(provider, account.id);
827
- if (success) {
828
- console.log((0, ui_1.ok)(`Account resumed: ${account.email || account.id}`));
829
- console.log((0, ui_1.info)('Account is now active in quota rotation'));
830
- }
831
- else {
832
- console.log((0, ui_1.fail)('Failed to resume account'));
833
- process.exit(1);
834
- }
835
- }
836
- async function handleQuotaStatus(verbose = false, providerFilter = 'all') {
837
- await (0, ui_1.initUI)();
838
- console.log((0, ui_1.header)('Quota Status'));
839
- console.log('');
840
- const shouldFetch = {
841
- agy: providerFilter === 'all' || providerFilter === 'agy',
842
- codex: providerFilter === 'all' || providerFilter === 'codex',
843
- gemini: providerFilter === 'all' || providerFilter === 'gemini',
844
- };
845
- console.log((0, ui_1.dim)('Fetching quotas...'));
846
- // Parallel fetch from all requested providers
847
- const [agyResults, codexResults, geminiResults] = await Promise.all([
848
- shouldFetch.agy ? (0, quota_fetcher_1.fetchAllProviderQuotas)('agy', verbose) : null,
849
- shouldFetch.codex ? (0, quota_fetcher_codex_1.fetchAllCodexQuotas)(verbose) : null,
850
- shouldFetch.gemini ? (0, quota_fetcher_gemini_cli_1.fetchAllGeminiCliQuotas)(verbose) : null,
851
- ]);
852
- console.log('');
853
- // Display Antigravity section
854
- if (agyResults && agyResults.accounts.length > 0) {
855
- displayAntigravityQuotaSection(agyResults);
856
- }
857
- else if (shouldFetch.agy) {
858
- console.log((0, ui_1.subheader)('Antigravity (0 accounts)'));
859
- console.log((0, ui_1.info)('No Antigravity accounts configured'));
860
- console.log(` Run: ${(0, ui_1.color)('ccs agy --auth', 'command')} to authenticate`);
861
- console.log('');
862
- }
863
- // Display Codex section
864
- if (codexResults && codexResults.length > 0) {
865
- displayCodexQuotaSection(codexResults);
866
- }
867
- else if (shouldFetch.codex) {
868
- console.log((0, ui_1.subheader)('Codex (0 accounts)'));
869
- console.log((0, ui_1.info)('No Codex accounts configured'));
870
- console.log(` Run: ${(0, ui_1.color)('ccs codex --auth', 'command')} to authenticate`);
871
- console.log('');
872
- }
873
- // Display Gemini CLI section
874
- if (geminiResults && geminiResults.length > 0) {
875
- displayGeminiCliQuotaSection(geminiResults);
876
- }
877
- else if (shouldFetch.gemini) {
878
- console.log((0, ui_1.subheader)('Gemini CLI (0 accounts)'));
879
- console.log((0, ui_1.info)('No Gemini CLI accounts configured'));
880
- console.log(` Run: ${(0, ui_1.color)('ccs gemini --auth', 'command')} to authenticate`);
881
- console.log('');
882
- }
883
- }
884
- /**
885
- * Display Antigravity quota section
886
- */
887
- function displayAntigravityQuotaSection(quotaResult) {
888
- const provider = 'agy';
889
- const accounts = (0, account_manager_1.getProviderAccounts)(provider);
890
- console.log((0, ui_1.subheader)(`Antigravity (${accounts.length} account${accounts.length !== 1 ? 's' : ''})`));
891
- console.log('');
892
- // Build table rows
893
- const rows = [];
894
- for (const account of accounts) {
895
- const quotaData = quotaResult.accounts.find((q) => q.account.id === account.id);
896
- const quota = quotaData?.quota;
897
- // Calculate average quota
898
- let avgQuota = 'N/A';
899
- if (quota?.success && quota.models.length > 0) {
900
- const avg = Math.round(quota.models.reduce((sum, m) => sum + m.percentage, 0) / quota.models.length);
901
- avgQuota = `${avg}%`;
902
- }
903
- // Build status badges
904
- const statusParts = [];
905
- if (account.paused)
906
- statusParts.push((0, ui_1.color)('PAUSED', 'warning'));
907
- if ((0, quota_manager_1.isOnCooldown)(provider, account.id))
908
- statusParts.push((0, ui_1.color)('COOLDOWN', 'warning'));
909
- const defaultMark = account.isDefault ? (0, ui_1.color)('*', 'success') : ' ';
910
- const tier = account.tier || 'unknown';
911
- const status = statusParts.join(', ');
912
- rows.push([
913
- defaultMark,
914
- account.nickname || account.email || account.id,
915
- tier,
916
- avgQuota,
917
- status,
918
- ]);
919
- }
920
- console.log((0, ui_1.table)(rows, {
921
- head: ['', 'Account', 'Tier', 'Quota', 'Status'],
922
- colWidths: [3, 30, 10, 10, 20],
923
- }));
924
- console.log('');
925
- }
926
- /**
927
- * Display Codex quota section
928
- */
929
- function displayCodexQuotaSection(results) {
930
- console.log((0, ui_1.subheader)(`Codex (${results.length} account${results.length !== 1 ? 's' : ''})`));
931
- console.log('');
932
- for (const { account, quota } of results) {
933
- const accountInfo = (0, account_manager_1.findAccountByQuery)('codex', account);
934
- const defaultMark = accountInfo?.isDefault ? (0, ui_1.color)(' (default)', 'info') : '';
935
- if (!quota.success) {
936
- console.log(` ${(0, ui_1.fail)(account)}${defaultMark}`);
937
- console.log(` ${(0, ui_1.color)(quota.error || 'Failed to fetch quota', 'error')}`);
938
- console.log('');
939
- continue;
940
- }
941
- // Calculate overall quota health
942
- const avgQuota = quota.windows.length > 0
943
- ? quota.windows.reduce((sum, w) => sum + w.remainingPercent, 0) / quota.windows.length
944
- : 0;
945
- const statusIcon = avgQuota > 50 ? (0, ui_1.ok)('') : avgQuota > 10 ? (0, ui_1.warn)('') : (0, ui_1.fail)('');
946
- const planBadge = quota.planType ? (0, ui_1.color)(` [${quota.planType}]`, 'info') : '';
947
- console.log(` ${statusIcon}${account}${defaultMark}${planBadge}`);
948
- // Show windows
949
- for (const window of quota.windows) {
950
- const bar = formatQuotaBar(window.remainingPercent);
951
- const resetLabel = window.resetAfterSeconds
952
- ? (0, ui_1.dim)(` Resets ${formatResetTime(window.resetAfterSeconds)}`)
953
- : '';
954
- console.log(` ${window.label.padEnd(24)} ${bar} ${window.remainingPercent.toFixed(0)}%${resetLabel}`);
955
- }
956
- console.log('');
957
- }
958
- }
959
- /**
960
- * Display Gemini CLI quota section
961
- */
962
- function displayGeminiCliQuotaSection(results) {
963
- console.log((0, ui_1.subheader)(`Gemini CLI (${results.length} account${results.length !== 1 ? 's' : ''})`));
964
- console.log('');
965
- for (const { account, quota } of results) {
966
- const accountInfo = (0, account_manager_1.findAccountByQuery)('gemini', account);
967
- const defaultMark = accountInfo?.isDefault ? (0, ui_1.color)(' (default)', 'info') : '';
968
- if (!quota.success) {
969
- console.log(` ${(0, ui_1.fail)(account)}${defaultMark}`);
970
- console.log(` ${(0, ui_1.color)(quota.error || 'Failed to fetch quota', 'error')}`);
971
- console.log('');
972
- continue;
973
- }
974
- // Calculate overall quota health
975
- const avgQuota = quota.buckets.length > 0
976
- ? quota.buckets.reduce((sum, b) => sum + b.remainingPercent, 0) / quota.buckets.length
977
- : 0;
978
- const statusIcon = avgQuota > 50 ? (0, ui_1.ok)('') : avgQuota > 10 ? (0, ui_1.warn)('') : (0, ui_1.fail)('');
979
- console.log(` ${statusIcon}${account}${defaultMark}`);
980
- if (quota.projectId) {
981
- console.log(` Project: ${(0, ui_1.dim)(quota.projectId)}`);
982
- }
983
- // Show buckets
984
- for (const bucket of quota.buckets) {
985
- const bar = formatQuotaBar(bucket.remainingPercent);
986
- const tokenLabel = bucket.tokenType ? (0, ui_1.dim)(` (${bucket.tokenType})`) : '';
987
- const resetLabel = bucket.resetTime
988
- ? (0, ui_1.dim)(` Resets ${formatResetTimeISO(bucket.resetTime)}`)
989
- : '';
990
- console.log(` ${bucket.label.padEnd(24)} ${bar} ${bucket.remainingPercent.toFixed(0)}%${tokenLabel}${resetLabel}`);
991
- }
992
- console.log('');
993
- }
994
- }
995
- /**
996
- * Format reset time from seconds
997
- */
998
- function formatResetTime(seconds) {
999
- if (seconds <= 0)
1000
- return 'now';
1001
- if (seconds < 60)
1002
- return `in ${seconds}s`;
1003
- if (seconds < 3600)
1004
- return `in ${Math.round(seconds / 60)}m`;
1005
- return `in ${Math.round(seconds / 3600)}h`;
1006
- }
1007
- /**
1008
- * Format reset time from ISO timestamp
1009
- */
1010
- function formatResetTimeISO(isoTime) {
1011
- if (!isoTime)
1012
- return 'unknown';
1013
- const resetDate = new Date(isoTime);
1014
- if (isNaN(resetDate.getTime()))
1015
- return 'unknown';
1016
- const seconds = Math.max(0, Math.round((resetDate.getTime() - Date.now()) / 1000));
1017
- return formatResetTime(seconds);
1018
- }
1019
- // ============================================================================
1020
- // MAIN ROUTER
1021
- // ============================================================================
1022
- async function handleCliproxyCommand(args) {
1023
- // Parse --backend flag first (before other processing)
1024
- const { backend: cliBackend, remainingArgs } = parseBackendArg(args);
1025
- const effectiveBackend = getEffectiveBackend(cliBackend);
1026
- const verbose = remainingArgs.includes('--verbose') || remainingArgs.includes('-v');
1027
- const command = remainingArgs[0];
1028
- if (remainingArgs.includes('--help') || remainingArgs.includes('-h')) {
1029
- await showHelp();
1030
- return;
1031
- }
1032
- if (command === 'create') {
1033
- await handleCreate(remainingArgs.slice(1));
1034
- return;
1035
- }
1036
- if (command === 'list' || command === 'ls') {
1037
- await handleList();
1038
- return;
1039
- }
1040
- if (command === 'remove' || command === 'delete' || command === 'rm') {
1041
- await handleRemove(remainingArgs.slice(1));
1042
- return;
1043
- }
1044
- if (command === 'sync') {
1045
- await (0, cliproxy_sync_handler_1.handleSync)(remainingArgs.slice(1));
1046
- return;
1047
- }
1048
- if (command === 'stop') {
1049
- await handleStop();
1050
- return;
1051
- }
1052
- if (command === 'status') {
1053
- await handleProxyStatus();
1054
- return;
1055
- }
1056
- if (command === 'doctor' || command === 'diag') {
1057
- await handleDoctor(verbose);
1058
- return;
1059
- }
1060
- // Quota management commands
1061
- if (command === 'default') {
1062
- await handleSetDefault(remainingArgs.slice(1));
1063
- return;
1064
- }
1065
- if (command === 'pause') {
1066
- await handlePauseAccount(remainingArgs.slice(1));
1067
- return;
1068
- }
1069
- if (command === 'resume') {
1070
- await handleResumeAccount(remainingArgs.slice(1));
1071
- return;
1072
- }
1073
- if (command === 'quota') {
1074
- const { provider: providerFilter } = parseProviderArg(remainingArgs.slice(1));
1075
- await handleQuotaStatus(verbose, providerFilter);
1076
- return;
1077
- }
1078
- const installIdx = remainingArgs.indexOf('--install');
1079
- if (installIdx !== -1) {
1080
- let version = remainingArgs[installIdx + 1];
1081
- if (!version || version.startsWith('-')) {
1082
- console.error((0, ui_1.fail)('Missing version argument for --install'));
1083
- console.error(' Usage: ccs cliproxy --install <version>');
1084
- console.error(' Example: ccs cliproxy --install 6.6.80-0');
1085
- process.exit(1);
1086
- }
1087
- // Strip leading 'v' prefix and whitespace (user may type " v6.6.80-0 ")
1088
- version = version.trim().replace(/^v/, '');
1089
- await handleInstallVersion(version, verbose, effectiveBackend);
1090
- return;
1091
- }
1092
- if (remainingArgs.includes('--latest') || remainingArgs.includes('--update')) {
1093
- await handleInstallLatest(verbose, effectiveBackend);
1094
- return;
1095
- }
1096
- await showStatus(verbose, effectiveBackend);
1097
- }
1098
- exports.handleCliproxyCommand = handleCliproxyCommand;
12
+ var index_1 = require("./cliproxy/index");
13
+ Object.defineProperty(exports, "handleCliproxyCommand", { enumerable: true, get: function () { return index_1.handleCliproxyCommand; } });
1099
14
  //# sourceMappingURL=cliproxy-command.js.map