@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.
- package/dist/cliproxy/account-manager.d.ts +5 -207
- package/dist/cliproxy/account-manager.d.ts.map +1 -1
- package/dist/cliproxy/account-manager.js +35 -795
- package/dist/cliproxy/account-manager.js.map +1 -1
- package/dist/cliproxy/accounts/bulk-ops.d.ts +22 -0
- package/dist/cliproxy/accounts/bulk-ops.d.ts.map +1 -0
- package/dist/cliproxy/accounts/bulk-ops.js +88 -0
- package/dist/cliproxy/accounts/bulk-ops.js.map +1 -0
- package/dist/cliproxy/accounts/index.d.ts +19 -0
- package/dist/cliproxy/accounts/index.d.ts.map +1 -0
- package/dist/cliproxy/accounts/index.js +54 -0
- package/dist/cliproxy/accounts/index.js.map +1 -0
- package/dist/cliproxy/accounts/query.d.ts +36 -0
- package/dist/cliproxy/accounts/query.d.ts.map +1 -0
- package/dist/cliproxy/accounts/query.js +94 -0
- package/dist/cliproxy/accounts/query.js.map +1 -0
- package/dist/cliproxy/accounts/registry.d.ts +74 -0
- package/dist/cliproxy/accounts/registry.d.ts.map +1 -0
- package/dist/cliproxy/accounts/registry.js +510 -0
- package/dist/cliproxy/accounts/registry.js.map +1 -0
- package/dist/cliproxy/accounts/token-file-ops.d.ts +64 -0
- package/dist/cliproxy/accounts/token-file-ops.d.ts.map +1 -0
- package/dist/cliproxy/accounts/token-file-ops.js +206 -0
- package/dist/cliproxy/accounts/token-file-ops.js.map +1 -0
- package/dist/cliproxy/accounts/types.d.ts +66 -0
- package/dist/cliproxy/accounts/types.d.ts.map +1 -0
- package/dist/cliproxy/accounts/types.js +12 -0
- package/dist/cliproxy/accounts/types.js.map +1 -0
- package/dist/cliproxy/auth/token-expiry-checker.d.ts.map +1 -1
- package/dist/cliproxy/auth/token-expiry-checker.js +5 -1
- package/dist/cliproxy/auth/token-expiry-checker.js.map +1 -1
- package/dist/cliproxy/cliproxy-executor.d.ts +11 -28
- package/dist/cliproxy/cliproxy-executor.d.ts.map +1 -1
- package/dist/cliproxy/cliproxy-executor.js +20 -1028
- package/dist/cliproxy/cliproxy-executor.js.map +1 -1
- package/dist/cliproxy/config/env-builder.d.ts +60 -0
- package/dist/cliproxy/config/env-builder.d.ts.map +1 -0
- package/dist/cliproxy/config/env-builder.js +311 -0
- package/dist/cliproxy/config/env-builder.js.map +1 -0
- package/dist/cliproxy/config/generator.d.ts +63 -0
- package/dist/cliproxy/config/generator.d.ts.map +1 -0
- package/dist/cliproxy/config/generator.js +336 -0
- package/dist/cliproxy/config/generator.js.map +1 -0
- package/dist/cliproxy/config/index.d.ts +10 -0
- package/dist/cliproxy/config/index.d.ts.map +1 -0
- package/dist/cliproxy/config/index.js +26 -0
- package/dist/cliproxy/config/index.js.map +1 -0
- package/dist/cliproxy/config/path-resolver.d.ts +47 -0
- package/dist/cliproxy/config/path-resolver.d.ts.map +1 -0
- package/dist/cliproxy/config/path-resolver.js +104 -0
- package/dist/cliproxy/config/path-resolver.js.map +1 -0
- package/dist/cliproxy/config/port-manager.d.ts +33 -0
- package/dist/cliproxy/config/port-manager.d.ts.map +1 -0
- package/dist/cliproxy/config/port-manager.js +68 -0
- package/dist/cliproxy/config/port-manager.js.map +1 -0
- package/dist/cliproxy/config/thinking-config.d.ts +39 -0
- package/dist/cliproxy/config/thinking-config.d.ts.map +1 -0
- package/dist/cliproxy/config/thinking-config.js +143 -0
- package/dist/cliproxy/config/thinking-config.js.map +1 -0
- package/dist/cliproxy/config-generator.d.ts +9 -221
- package/dist/cliproxy/config-generator.d.ts.map +1 -1
- package/dist/cliproxy/config-generator.js +12 -856
- package/dist/cliproxy/config-generator.js.map +1 -1
- package/dist/cliproxy/executor/env-resolver.d.ts +45 -0
- package/dist/cliproxy/executor/env-resolver.d.ts.map +1 -0
- package/dist/cliproxy/executor/env-resolver.js +106 -0
- package/dist/cliproxy/executor/env-resolver.js.map +1 -0
- package/dist/cliproxy/executor/index.d.ts +24 -0
- package/dist/cliproxy/executor/index.d.ts.map +1 -0
- package/dist/cliproxy/executor/index.js +669 -0
- package/dist/cliproxy/executor/index.js.map +1 -0
- package/dist/cliproxy/executor/lifecycle-manager.d.ts +33 -0
- package/dist/cliproxy/executor/lifecycle-manager.d.ts.map +1 -0
- package/dist/cliproxy/executor/lifecycle-manager.js +161 -0
- package/dist/cliproxy/executor/lifecycle-manager.js.map +1 -0
- package/dist/cliproxy/executor/retry-handler.d.ts +27 -0
- package/dist/cliproxy/executor/retry-handler.d.ts.map +1 -0
- package/dist/cliproxy/executor/retry-handler.js +109 -0
- package/dist/cliproxy/executor/retry-handler.js.map +1 -0
- package/dist/cliproxy/executor/session-bridge.d.ts +30 -0
- package/dist/cliproxy/executor/session-bridge.d.ts.map +1 -0
- package/dist/cliproxy/executor/session-bridge.js +232 -0
- package/dist/cliproxy/executor/session-bridge.js.map +1 -0
- package/dist/commands/cliproxy/auth-subcommand.d.ts +10 -0
- package/dist/commands/cliproxy/auth-subcommand.d.ts.map +1 -0
- package/dist/commands/cliproxy/auth-subcommand.js +55 -0
- package/dist/commands/cliproxy/auth-subcommand.js.map +1 -0
- package/dist/commands/cliproxy/help-subcommand.d.ts +8 -0
- package/dist/commands/cliproxy/help-subcommand.d.ts.map +1 -0
- package/dist/commands/cliproxy/help-subcommand.js +87 -0
- package/dist/commands/cliproxy/help-subcommand.js.map +1 -0
- package/dist/commands/cliproxy/index.d.ts +11 -0
- package/dist/commands/cliproxy/index.d.ts.map +1 -0
- package/dist/commands/cliproxy/index.js +196 -0
- package/dist/commands/cliproxy/index.js.map +1 -0
- package/dist/commands/cliproxy/install-subcommand.d.ts +14 -0
- package/dist/commands/cliproxy/install-subcommand.d.ts.map +1 -0
- package/dist/commands/cliproxy/install-subcommand.js +112 -0
- package/dist/commands/cliproxy/install-subcommand.js.map +1 -0
- package/dist/commands/cliproxy/proxy-lifecycle-subcommand.d.ts +10 -0
- package/dist/commands/cliproxy/proxy-lifecycle-subcommand.d.ts.map +1 -0
- package/dist/commands/cliproxy/proxy-lifecycle-subcommand.js +54 -0
- package/dist/commands/cliproxy/proxy-lifecycle-subcommand.js.map +1 -0
- package/dist/commands/cliproxy/quota-subcommand.d.ts +16 -0
- package/dist/commands/cliproxy/quota-subcommand.d.ts.map +1 -0
- package/dist/commands/cliproxy/quota-subcommand.js +383 -0
- package/dist/commands/cliproxy/quota-subcommand.js.map +1 -0
- package/dist/commands/cliproxy/variant-subcommand.d.ts +11 -0
- package/dist/commands/cliproxy/variant-subcommand.d.ts.map +1 -0
- package/dist/commands/cliproxy/variant-subcommand.js +292 -0
- package/dist/commands/cliproxy/variant-subcommand.js.map +1 -0
- package/dist/commands/cliproxy-command.d.ts +4 -15
- package/dist/commands/cliproxy-command.d.ts.map +1 -1
- package/dist/commands/cliproxy-command.js +5 -1090
- package/dist/commands/cliproxy-command.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,1098 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* CLIProxy Command Handler
|
|
4
4
|
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
47
|
-
|
|
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
|