@kaitranntt/ccs 7.37.0 → 7.37.1-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/dist/commands/version-command.js +1 -1
- package/dist/commands/version-command.js.map +1 -1
- package/dist/ui/assets/{accounts-DmKrgrCj.js → accounts-D0KU1a-4.js} +1 -1
- package/dist/ui/assets/{alert-dialog-CHcjWgRn.js → alert-dialog-BNRDV_Kh.js} +1 -1
- package/dist/ui/assets/{api-BFHZ3RKb.js → api-ClkFu9LY.js} +1 -1
- package/dist/ui/assets/{auth-section-DAI6Q5vU.js → auth-section-CNhNN1Jp.js} +1 -1
- package/dist/ui/assets/{backups-section-B1O8i4FM.js → backups-section-CZ721HIu.js} +1 -1
- package/dist/ui/assets/cliproxy-DeGucfVg.js +3 -0
- package/dist/ui/assets/{cliproxy-control-panel-3zVE7jZH.js → cliproxy-control-panel-T9i_Ldh0.js} +1 -1
- package/dist/ui/assets/{confirm-dialog-DV0JzL4A.js → confirm-dialog-Cy1__gRc.js} +1 -1
- package/dist/ui/assets/{copilot-BX8vT5RR.js → copilot-Cdqs2Jbh.js} +3 -3
- package/dist/ui/assets/{globalenv-section-BAft0m9b.js → globalenv-section-BEKtYN6I.js} +1 -1
- package/dist/ui/assets/{health-ct5TpFUE.js → health-BaHazGgS.js} +1 -1
- package/dist/ui/assets/{index-BOsGipAU.js → index-BTBpI5th.js} +1 -1
- package/dist/ui/assets/{index-CU3zgD6n.js → index-BZn6kF0u.js} +1 -1
- package/dist/ui/assets/{index-B8-K01HW.js → index-C2MS84sf.js} +1 -1
- package/dist/ui/assets/{index-gfWiPznS.js → index-CiULLjNI.js} +8 -8
- package/dist/ui/assets/{index-RZ6b7YTz.js → index-PjscK1jS.js} +1 -1
- package/dist/ui/assets/{proxy-status-widget-Ng9oZdrw.js → proxy-status-widget-BY6Lnr80.js} +1 -1
- package/dist/ui/assets/{shared-DpInRmag.js → shared-dZGNvBe9.js} +1 -1
- package/dist/ui/assets/{switch-DkRSlAu-.js → switch-BCFbehJz.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/cliproxy-BsSI8qFW.js +0 -3
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Account Manager for CLIProxyAPI Multi-Account Support
|
|
4
4
|
*
|
|
5
|
+
* DEPRECATED: This file is now a re-export shim for backwards compatibility.
|
|
6
|
+
* New code should import directly from './accounts/' module.
|
|
7
|
+
*
|
|
5
8
|
* Manages multiple OAuth accounts per provider (Gemini, Codex, etc.).
|
|
6
9
|
* Each provider can have multiple accounts, with one designated as default.
|
|
7
10
|
*
|
|
@@ -9,800 +12,37 @@
|
|
|
9
12
|
* Token storage: ~/.ccs/cliproxy/auth/ (flat structure, CLIProxyAPI discovers by type field)
|
|
10
13
|
* Paused tokens: ~/.ccs/cliproxy/auth-paused/ (sibling dir, outside CLIProxyAPI scan path)
|
|
11
14
|
*/
|
|
12
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
-
if (k2 === undefined) k2 = k;
|
|
14
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
-
}
|
|
18
|
-
Object.defineProperty(o, k2, desc);
|
|
19
|
-
}) : (function(o, m, k, k2) {
|
|
20
|
-
if (k2 === undefined) k2 = k;
|
|
21
|
-
o[k2] = m[k];
|
|
22
|
-
}));
|
|
23
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
-
}) : function(o, v) {
|
|
26
|
-
o["default"] = v;
|
|
27
|
-
});
|
|
28
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
exports
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return 'default';
|
|
69
|
-
}
|
|
70
|
-
exports.extractAccountIdFromTokenFile = extractAccountIdFromTokenFile;
|
|
71
|
-
/**
|
|
72
|
-
* Generate nickname from email
|
|
73
|
-
* Takes prefix before @ symbol, sanitizes whitespace
|
|
74
|
-
* Validation: 1-50 chars, any non-whitespace (permissive per user preference)
|
|
75
|
-
*/
|
|
76
|
-
function generateNickname(email) {
|
|
77
|
-
if (!email)
|
|
78
|
-
return 'default';
|
|
79
|
-
const prefix = email.split('@')[0];
|
|
80
|
-
// Sanitize: remove whitespace, limit to 50 chars
|
|
81
|
-
return prefix.replace(/\s+/g, '').slice(0, 50) || 'default';
|
|
82
|
-
}
|
|
83
|
-
exports.generateNickname = generateNickname;
|
|
84
|
-
/**
|
|
85
|
-
* Validate nickname
|
|
86
|
-
* Rules: 1-50 chars, no whitespace, URL-safe, no reserved patterns
|
|
87
|
-
* @returns null if valid, error message if invalid
|
|
88
|
-
*/
|
|
89
|
-
function validateNickname(nickname) {
|
|
90
|
-
if (!nickname || nickname.length === 0) {
|
|
91
|
-
return 'Nickname is required';
|
|
92
|
-
}
|
|
93
|
-
if (nickname.length > 50) {
|
|
94
|
-
return 'Nickname must be 50 characters or less';
|
|
95
|
-
}
|
|
96
|
-
if (/\s/.test(nickname)) {
|
|
97
|
-
return 'Nickname cannot contain whitespace';
|
|
98
|
-
}
|
|
99
|
-
// Block URL-unsafe chars that break routing
|
|
100
|
-
if (/[%\/&?#]/.test(nickname)) {
|
|
101
|
-
return 'Nickname cannot contain special URL characters (%, /, &, ?, #)';
|
|
102
|
-
}
|
|
103
|
-
// Block reserved patterns used by auto-discovery (kiro-1, ghcp-2, etc.)
|
|
104
|
-
if (/^(kiro|ghcp)-\d+$/i.test(nickname)) {
|
|
105
|
-
return 'Nickname cannot match reserved pattern (kiro-N, ghcp-N)';
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
exports.validateNickname = validateNickname;
|
|
110
|
-
/**
|
|
111
|
-
* Get path to accounts registry file
|
|
112
|
-
*/
|
|
113
|
-
function getAccountsRegistryPath() {
|
|
114
|
-
return path.join((0, config_generator_1.getCliproxyDir)(), 'accounts.json');
|
|
115
|
-
}
|
|
116
|
-
exports.getAccountsRegistryPath = getAccountsRegistryPath;
|
|
117
|
-
/**
|
|
118
|
-
* Get path to paused tokens directory
|
|
119
|
-
* Paused tokens are moved here so CLIProxyAPI won't discover them
|
|
120
|
-
*
|
|
121
|
-
* Uses sibling directory (auth-paused/) instead of subdirectory (auth/paused/)
|
|
122
|
-
* because CLIProxyAPI's watcher uses filepath.Walk() which recursively scans
|
|
123
|
-
* all subdirectories of auth/. A sibling directory is completely outside
|
|
124
|
-
* CLIProxyAPI's scan path, preventing token refresh loops.
|
|
125
|
-
*/
|
|
126
|
-
function getPausedDir() {
|
|
127
|
-
return path.join((0, config_generator_1.getCliproxyDir)(), 'auth-paused');
|
|
128
|
-
}
|
|
129
|
-
exports.getPausedDir = getPausedDir;
|
|
130
|
-
/**
|
|
131
|
-
* Load accounts registry
|
|
132
|
-
*/
|
|
133
|
-
function loadAccountsRegistry() {
|
|
134
|
-
const registryPath = getAccountsRegistryPath();
|
|
135
|
-
if (!fs.existsSync(registryPath)) {
|
|
136
|
-
return { ...DEFAULT_REGISTRY };
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
const content = fs.readFileSync(registryPath, 'utf-8');
|
|
140
|
-
const data = JSON.parse(content);
|
|
141
|
-
return {
|
|
142
|
-
version: data.version || 1,
|
|
143
|
-
providers: data.providers || {},
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
return { ...DEFAULT_REGISTRY };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
exports.loadAccountsRegistry = loadAccountsRegistry;
|
|
151
|
-
/**
|
|
152
|
-
* Save accounts registry
|
|
153
|
-
*/
|
|
154
|
-
function saveAccountsRegistry(registry) {
|
|
155
|
-
const registryPath = getAccountsRegistryPath();
|
|
156
|
-
const dir = path.dirname(registryPath);
|
|
157
|
-
if (!fs.existsSync(dir)) {
|
|
158
|
-
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
159
|
-
}
|
|
160
|
-
fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2) + '\n', {
|
|
161
|
-
mode: 0o600,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
exports.saveAccountsRegistry = saveAccountsRegistry;
|
|
165
|
-
/**
|
|
166
|
-
* Sync registry with actual token files
|
|
167
|
-
* Removes stale entries where token file no longer exists
|
|
168
|
-
* For paused accounts, checks both auth/ and paused/ directories
|
|
169
|
-
* Called automatically when loading accounts
|
|
170
|
-
*/
|
|
171
|
-
function syncRegistryWithTokenFiles(registry) {
|
|
172
|
-
const authDir = (0, config_generator_1.getAuthDir)();
|
|
173
|
-
const pausedDir = getPausedDir();
|
|
174
|
-
let modified = false;
|
|
175
|
-
for (const [_providerName, providerAccounts] of Object.entries(registry.providers)) {
|
|
176
|
-
if (!providerAccounts)
|
|
177
|
-
continue;
|
|
178
|
-
const staleIds = [];
|
|
179
|
-
for (const [accountId, meta] of Object.entries(providerAccounts.accounts)) {
|
|
180
|
-
const tokenPath = path.join(authDir, meta.tokenFile);
|
|
181
|
-
const pausedPath = path.join(pausedDir, meta.tokenFile);
|
|
182
|
-
// For paused accounts, check paused dir; for active accounts, check auth dir
|
|
183
|
-
const expectedPath = meta.paused ? pausedPath : tokenPath;
|
|
184
|
-
// Also accept if file exists in either location (handles edge cases)
|
|
185
|
-
const existsAnywhere = fs.existsSync(tokenPath) || fs.existsSync(pausedPath);
|
|
186
|
-
if (!fs.existsSync(expectedPath) && !existsAnywhere) {
|
|
187
|
-
staleIds.push(accountId);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
// Remove stale accounts
|
|
191
|
-
for (const id of staleIds) {
|
|
192
|
-
delete providerAccounts.accounts[id];
|
|
193
|
-
modified = true;
|
|
194
|
-
// Update default if deleted
|
|
195
|
-
if (providerAccounts.default === id) {
|
|
196
|
-
const remainingIds = Object.keys(providerAccounts.accounts);
|
|
197
|
-
providerAccounts.default = remainingIds[0] || 'default';
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return modified;
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Get all accounts for a provider
|
|
205
|
-
*/
|
|
206
|
-
function getProviderAccounts(provider) {
|
|
207
|
-
const registry = loadAccountsRegistry();
|
|
208
|
-
// Sync with actual token files (removes stale entries)
|
|
209
|
-
if (syncRegistryWithTokenFiles(registry)) {
|
|
210
|
-
saveAccountsRegistry(registry);
|
|
211
|
-
}
|
|
212
|
-
const providerAccounts = registry.providers[provider];
|
|
213
|
-
if (!providerAccounts) {
|
|
214
|
-
return [];
|
|
215
|
-
}
|
|
216
|
-
return Object.entries(providerAccounts.accounts).map(([id, meta]) => ({
|
|
217
|
-
id,
|
|
218
|
-
provider,
|
|
219
|
-
isDefault: id === providerAccounts.default,
|
|
220
|
-
...meta,
|
|
221
|
-
}));
|
|
222
|
-
}
|
|
223
|
-
exports.getProviderAccounts = getProviderAccounts;
|
|
224
|
-
/**
|
|
225
|
-
* Get default account for a provider
|
|
226
|
-
*/
|
|
227
|
-
function getDefaultAccount(provider) {
|
|
228
|
-
const accounts = getProviderAccounts(provider);
|
|
229
|
-
return accounts.find((a) => a.isDefault) || accounts[0] || null;
|
|
230
|
-
}
|
|
231
|
-
exports.getDefaultAccount = getDefaultAccount;
|
|
232
|
-
/**
|
|
233
|
-
* Get specific account by ID
|
|
234
|
-
*/
|
|
235
|
-
function getAccount(provider, accountId) {
|
|
236
|
-
const accounts = getProviderAccounts(provider);
|
|
237
|
-
return accounts.find((a) => a.id === accountId) || null;
|
|
238
|
-
}
|
|
239
|
-
exports.getAccount = getAccount;
|
|
240
|
-
/**
|
|
241
|
-
* Find account by query (nickname, email, or id)
|
|
242
|
-
* Supports partial matching for convenience
|
|
243
|
-
*/
|
|
244
|
-
function findAccountByQuery(provider, query) {
|
|
245
|
-
const accounts = getProviderAccounts(provider);
|
|
246
|
-
const lowerQuery = query.toLowerCase();
|
|
247
|
-
// Exact match first (id, email, nickname)
|
|
248
|
-
const exactMatch = accounts.find((a) => a.id === query ||
|
|
249
|
-
a.email?.toLowerCase() === lowerQuery ||
|
|
250
|
-
a.nickname?.toLowerCase() === lowerQuery);
|
|
251
|
-
if (exactMatch)
|
|
252
|
-
return exactMatch;
|
|
253
|
-
// Partial match on nickname or email prefix
|
|
254
|
-
const partialMatch = accounts.find((a) => a.nickname?.toLowerCase().startsWith(lowerQuery) ||
|
|
255
|
-
a.email?.toLowerCase().startsWith(lowerQuery));
|
|
256
|
-
return partialMatch || null;
|
|
257
|
-
}
|
|
258
|
-
exports.findAccountByQuery = findAccountByQuery;
|
|
259
|
-
/**
|
|
260
|
-
* Register a new account
|
|
261
|
-
* Called after successful OAuth to record the account
|
|
262
|
-
*
|
|
263
|
-
* For providers without email (kiro, ghcp):
|
|
264
|
-
* - nickname is REQUIRED and used as accountId
|
|
265
|
-
* - Uniqueness is enforced to prevent overwriting
|
|
266
|
-
*
|
|
267
|
-
* For providers with email:
|
|
268
|
-
* - email is used as accountId
|
|
269
|
-
* - nickname is auto-generated from email if not provided
|
|
270
|
-
*/
|
|
271
|
-
function registerAccount(provider, tokenFile, email, nickname, projectId) {
|
|
272
|
-
const registry = loadAccountsRegistry();
|
|
273
|
-
// Initialize provider section if needed
|
|
274
|
-
if (!registry.providers[provider]) {
|
|
275
|
-
registry.providers[provider] = {
|
|
276
|
-
default: 'default',
|
|
277
|
-
accounts: {},
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
const providerAccounts = registry.providers[provider];
|
|
281
|
-
if (!providerAccounts) {
|
|
282
|
-
throw new Error('Failed to initialize provider accounts');
|
|
283
|
-
}
|
|
284
|
-
// Determine account ID based on provider type
|
|
285
|
-
let accountId;
|
|
286
|
-
let accountNickname;
|
|
287
|
-
if (exports.PROVIDERS_WITHOUT_EMAIL.includes(provider)) {
|
|
288
|
-
// For kiro/ghcp: nickname is REQUIRED and used as accountId
|
|
289
|
-
if (!nickname || nickname === 'default') {
|
|
290
|
-
throw new Error(`Nickname is required when adding ${provider} accounts. ` +
|
|
291
|
-
`Use --nickname <name> or provide a nickname in the UI.`);
|
|
292
|
-
}
|
|
293
|
-
// Validate nickname format
|
|
294
|
-
const validationError = validateNickname(nickname);
|
|
295
|
-
if (validationError) {
|
|
296
|
-
throw new Error(validationError);
|
|
297
|
-
}
|
|
298
|
-
// Check uniqueness
|
|
299
|
-
for (const [existingId, _account] of Object.entries(providerAccounts.accounts)) {
|
|
300
|
-
if (existingId.toLowerCase() === nickname.toLowerCase()) {
|
|
301
|
-
throw new Error(`An account with nickname "${nickname}" already exists for ${provider}. ` +
|
|
302
|
-
`Choose a different nickname.`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
accountId = nickname;
|
|
306
|
-
accountNickname = nickname;
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
// For other providers: use email as accountId, fallback to filename extraction
|
|
310
|
-
accountId = extractAccountIdFromTokenFile(tokenFile, email);
|
|
311
|
-
accountNickname = nickname || generateNickname(email);
|
|
312
|
-
}
|
|
313
|
-
const isFirstAccount = Object.keys(providerAccounts.accounts).length === 0;
|
|
314
|
-
// Create or update account
|
|
315
|
-
const accountMeta = {
|
|
316
|
-
email,
|
|
317
|
-
nickname: accountNickname,
|
|
318
|
-
tokenFile,
|
|
319
|
-
createdAt: new Date().toISOString(),
|
|
320
|
-
lastUsedAt: new Date().toISOString(),
|
|
321
|
-
};
|
|
322
|
-
// Include projectId for Antigravity accounts
|
|
323
|
-
if (provider === 'agy' && projectId) {
|
|
324
|
-
accountMeta.projectId = projectId;
|
|
325
|
-
}
|
|
326
|
-
providerAccounts.accounts[accountId] = accountMeta;
|
|
327
|
-
// Set as default if first account
|
|
328
|
-
if (isFirstAccount) {
|
|
329
|
-
providerAccounts.default = accountId;
|
|
330
|
-
}
|
|
331
|
-
saveAccountsRegistry(registry);
|
|
332
|
-
return {
|
|
333
|
-
id: accountId,
|
|
334
|
-
provider,
|
|
335
|
-
isDefault: accountId === providerAccounts.default,
|
|
336
|
-
email,
|
|
337
|
-
nickname: accountNickname,
|
|
338
|
-
tokenFile,
|
|
339
|
-
createdAt: providerAccounts.accounts[accountId].createdAt,
|
|
340
|
-
lastUsedAt: providerAccounts.accounts[accountId].lastUsedAt,
|
|
341
|
-
projectId: providerAccounts.accounts[accountId].projectId,
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
exports.registerAccount = registerAccount;
|
|
345
|
-
/**
|
|
346
|
-
* Set default account for a provider
|
|
347
|
-
*/
|
|
348
|
-
function setDefaultAccount(provider, accountId) {
|
|
349
|
-
const registry = loadAccountsRegistry();
|
|
350
|
-
const providerAccounts = registry.providers[provider];
|
|
351
|
-
if (!providerAccounts || !providerAccounts.accounts[accountId]) {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
providerAccounts.default = accountId;
|
|
355
|
-
saveAccountsRegistry(registry);
|
|
356
|
-
return true;
|
|
357
|
-
}
|
|
358
|
-
exports.setDefaultAccount = setDefaultAccount;
|
|
359
|
-
/**
|
|
360
|
-
* Pause an account (skip in quota rotation)
|
|
361
|
-
* Moves token file to paused/ subdir so CLIProxyAPI won't discover it
|
|
362
|
-
*/
|
|
363
|
-
function pauseAccount(provider, accountId) {
|
|
364
|
-
const registry = loadAccountsRegistry();
|
|
365
|
-
const providerAccounts = registry.providers[provider];
|
|
366
|
-
if (!providerAccounts?.accounts[accountId]) {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
const accountMeta = providerAccounts.accounts[accountId];
|
|
370
|
-
// Skip if already paused (idempotent)
|
|
371
|
-
if (accountMeta.paused) {
|
|
372
|
-
return true;
|
|
373
|
-
}
|
|
374
|
-
const authDir = (0, config_generator_1.getAuthDir)();
|
|
375
|
-
const pausedDir = getPausedDir();
|
|
376
|
-
const tokenPath = path.join(authDir, accountMeta.tokenFile);
|
|
377
|
-
const pausedPath = path.join(pausedDir, accountMeta.tokenFile);
|
|
378
|
-
// Move token file to paused directory (if it exists in auth dir)
|
|
379
|
-
if (fs.existsSync(tokenPath)) {
|
|
380
|
-
try {
|
|
381
|
-
// Create paused directory if it doesn't exist
|
|
382
|
-
if (!fs.existsSync(pausedDir)) {
|
|
383
|
-
fs.mkdirSync(pausedDir, { recursive: true, mode: 0o700 });
|
|
384
|
-
}
|
|
385
|
-
fs.renameSync(tokenPath, pausedPath);
|
|
386
|
-
}
|
|
387
|
-
catch {
|
|
388
|
-
// File operation failed, but continue with registry update
|
|
389
|
-
// syncRegistryWithTokenFiles() will handle recovery on next load
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
providerAccounts.accounts[accountId].paused = true;
|
|
393
|
-
providerAccounts.accounts[accountId].pausedAt = new Date().toISOString();
|
|
394
|
-
saveAccountsRegistry(registry);
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
exports.pauseAccount = pauseAccount;
|
|
398
|
-
/**
|
|
399
|
-
* Resume a paused account
|
|
400
|
-
* Moves token file back from paused/ to auth/ so CLIProxyAPI can discover it
|
|
401
|
-
*/
|
|
402
|
-
function resumeAccount(provider, accountId) {
|
|
403
|
-
const registry = loadAccountsRegistry();
|
|
404
|
-
const providerAccounts = registry.providers[provider];
|
|
405
|
-
if (!providerAccounts?.accounts[accountId]) {
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
const accountMeta = providerAccounts.accounts[accountId];
|
|
409
|
-
// Skip if already active (idempotent)
|
|
410
|
-
if (!accountMeta.paused) {
|
|
411
|
-
return true;
|
|
412
|
-
}
|
|
413
|
-
const authDir = (0, config_generator_1.getAuthDir)();
|
|
414
|
-
const pausedDir = getPausedDir();
|
|
415
|
-
const tokenPath = path.join(authDir, accountMeta.tokenFile);
|
|
416
|
-
const pausedPath = path.join(pausedDir, accountMeta.tokenFile);
|
|
417
|
-
// Move token file back from paused directory (if it exists in paused dir)
|
|
418
|
-
if (fs.existsSync(pausedPath)) {
|
|
419
|
-
try {
|
|
420
|
-
fs.renameSync(pausedPath, tokenPath);
|
|
421
|
-
}
|
|
422
|
-
catch {
|
|
423
|
-
// File operation failed, but continue with registry update
|
|
424
|
-
// syncRegistryWithTokenFiles() will handle recovery on next load
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
providerAccounts.accounts[accountId].paused = false;
|
|
428
|
-
providerAccounts.accounts[accountId].pausedAt = undefined;
|
|
429
|
-
saveAccountsRegistry(registry);
|
|
430
|
-
return true;
|
|
431
|
-
}
|
|
432
|
-
exports.resumeAccount = resumeAccount;
|
|
433
|
-
/**
|
|
434
|
-
* Check if an account is paused
|
|
435
|
-
*/
|
|
436
|
-
function isAccountPaused(provider, accountId) {
|
|
437
|
-
const accounts = getProviderAccounts(provider);
|
|
438
|
-
const account = accounts.find((a) => a.id === accountId);
|
|
439
|
-
return account?.paused ?? false;
|
|
440
|
-
}
|
|
441
|
-
exports.isAccountPaused = isAccountPaused;
|
|
442
|
-
/**
|
|
443
|
-
* Update account tier
|
|
444
|
-
*/
|
|
445
|
-
function setAccountTier(provider, accountId, tier) {
|
|
446
|
-
const registry = loadAccountsRegistry();
|
|
447
|
-
const providerAccounts = registry.providers[provider];
|
|
448
|
-
if (!providerAccounts?.accounts[accountId]) {
|
|
449
|
-
return false;
|
|
450
|
-
}
|
|
451
|
-
providerAccounts.accounts[accountId].tier = tier;
|
|
452
|
-
saveAccountsRegistry(registry);
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
|
-
exports.setAccountTier = setAccountTier;
|
|
456
|
-
/**
|
|
457
|
-
* Get non-paused accounts for a provider
|
|
458
|
-
*/
|
|
459
|
-
function getActiveAccounts(provider) {
|
|
460
|
-
return getProviderAccounts(provider).filter((a) => !a.paused);
|
|
461
|
-
}
|
|
462
|
-
exports.getActiveAccounts = getActiveAccounts;
|
|
463
|
-
/**
|
|
464
|
-
* Remove an account
|
|
465
|
-
*/
|
|
466
|
-
function removeAccount(provider, accountId) {
|
|
467
|
-
const registry = loadAccountsRegistry();
|
|
468
|
-
const providerAccounts = registry.providers[provider];
|
|
469
|
-
if (!providerAccounts || !providerAccounts.accounts[accountId]) {
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
// Get token file to delete (check both auth and paused directories)
|
|
473
|
-
const tokenFile = providerAccounts.accounts[accountId].tokenFile;
|
|
474
|
-
const tokenPath = path.join((0, config_generator_1.getAuthDir)(), tokenFile);
|
|
475
|
-
const pausedPath = path.join(getPausedDir(), tokenFile);
|
|
476
|
-
// Delete token file from auth directory
|
|
477
|
-
if (fs.existsSync(tokenPath)) {
|
|
478
|
-
try {
|
|
479
|
-
fs.unlinkSync(tokenPath);
|
|
480
|
-
}
|
|
481
|
-
catch {
|
|
482
|
-
// Ignore deletion errors
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
// Also delete from paused directory if it exists there
|
|
486
|
-
if (fs.existsSync(pausedPath)) {
|
|
487
|
-
try {
|
|
488
|
-
fs.unlinkSync(pausedPath);
|
|
489
|
-
}
|
|
490
|
-
catch {
|
|
491
|
-
// Ignore deletion errors
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
// Remove from registry
|
|
495
|
-
delete providerAccounts.accounts[accountId];
|
|
496
|
-
// Update default if needed
|
|
497
|
-
const remainingAccounts = Object.keys(providerAccounts.accounts);
|
|
498
|
-
if (providerAccounts.default === accountId && remainingAccounts.length > 0) {
|
|
499
|
-
providerAccounts.default = remainingAccounts[0];
|
|
500
|
-
}
|
|
501
|
-
saveAccountsRegistry(registry);
|
|
502
|
-
return true;
|
|
503
|
-
}
|
|
504
|
-
exports.removeAccount = removeAccount;
|
|
505
|
-
/**
|
|
506
|
-
* Rename an account's nickname
|
|
507
|
-
*/
|
|
508
|
-
function renameAccount(provider, accountId, newNickname) {
|
|
509
|
-
const validationError = validateNickname(newNickname);
|
|
510
|
-
if (validationError) {
|
|
511
|
-
throw new Error(validationError);
|
|
512
|
-
}
|
|
513
|
-
const registry = loadAccountsRegistry();
|
|
514
|
-
const providerAccounts = registry.providers[provider];
|
|
515
|
-
if (!providerAccounts?.accounts[accountId]) {
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
|
-
// Check if nickname is already used by another account
|
|
519
|
-
for (const [id, account] of Object.entries(providerAccounts.accounts)) {
|
|
520
|
-
if (id !== accountId && account.nickname?.toLowerCase() === newNickname.toLowerCase()) {
|
|
521
|
-
throw new Error(`Nickname "${newNickname}" is already used by another account`);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
providerAccounts.accounts[accountId].nickname = newNickname;
|
|
525
|
-
saveAccountsRegistry(registry);
|
|
526
|
-
return true;
|
|
527
|
-
}
|
|
528
|
-
exports.renameAccount = renameAccount;
|
|
529
|
-
/**
|
|
530
|
-
* Update last used timestamp for an account
|
|
531
|
-
*/
|
|
532
|
-
function touchAccount(provider, accountId) {
|
|
533
|
-
const registry = loadAccountsRegistry();
|
|
534
|
-
const providerAccounts = registry.providers[provider];
|
|
535
|
-
if (providerAccounts?.accounts[accountId]) {
|
|
536
|
-
providerAccounts.accounts[accountId].lastUsedAt = new Date().toISOString();
|
|
537
|
-
saveAccountsRegistry(registry);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
exports.touchAccount = touchAccount;
|
|
541
|
-
/**
|
|
542
|
-
* Get token file path for an account
|
|
543
|
-
* Returns path in paused/ dir if account is paused, otherwise auth/
|
|
544
|
-
*/
|
|
545
|
-
function getAccountTokenPath(provider, accountId) {
|
|
546
|
-
const account = accountId ? getAccount(provider, accountId) : getDefaultAccount(provider);
|
|
547
|
-
if (!account) {
|
|
548
|
-
return null;
|
|
549
|
-
}
|
|
550
|
-
// Return path from paused directory if account is paused
|
|
551
|
-
const baseDir = account.paused ? getPausedDir() : (0, config_generator_1.getAuthDir)();
|
|
552
|
-
return path.join(baseDir, account.tokenFile);
|
|
553
|
-
}
|
|
554
|
-
exports.getAccountTokenPath = getAccountTokenPath;
|
|
555
|
-
/**
|
|
556
|
-
* Auto-discover accounts from existing token files
|
|
557
|
-
* Called during migration or first run to populate accounts registry
|
|
558
|
-
*
|
|
559
|
-
* For kiro/ghcp providers without email, generates unique accountId from:
|
|
560
|
-
* 1. OAuth provider + profile ID from filename (e.g., github-ABC123)
|
|
561
|
-
* 2. Fallback: provider + index (e.g., kiro-1, kiro-2)
|
|
562
|
-
*/
|
|
563
|
-
function discoverExistingAccounts() {
|
|
564
|
-
const authDir = (0, config_generator_1.getAuthDir)();
|
|
565
|
-
if (!fs.existsSync(authDir)) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
const registry = loadAccountsRegistry();
|
|
569
|
-
const files = fs.readdirSync(authDir);
|
|
570
|
-
for (const file of files) {
|
|
571
|
-
if (!file.endsWith('.json'))
|
|
572
|
-
continue;
|
|
573
|
-
const filePath = path.join(authDir, file);
|
|
574
|
-
try {
|
|
575
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
576
|
-
const data = JSON.parse(content);
|
|
577
|
-
// Skip if no type field
|
|
578
|
-
if (!data.type)
|
|
579
|
-
continue;
|
|
580
|
-
// Build reverse mapping from PROVIDER_TYPE_VALUES (type value -> provider)
|
|
581
|
-
// e.g., "antigravity" -> "agy", "kiro" -> "kiro", "codewhisperer" -> "kiro"
|
|
582
|
-
const typeValue = data.type.toLowerCase();
|
|
583
|
-
let provider;
|
|
584
|
-
for (const [prov, typeValues] of Object.entries(auth_types_1.PROVIDER_TYPE_VALUES)) {
|
|
585
|
-
if (typeValues.includes(typeValue)) {
|
|
586
|
-
provider = prov;
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
// Skip if unknown provider type
|
|
591
|
-
if (!provider) {
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
// Extract email if available, fallback to filename-based ID
|
|
595
|
-
let email = data.email || undefined;
|
|
596
|
-
// Fallback: extract email from filename (e.g., "kiro-google-user@example.com.json")
|
|
597
|
-
if (!email && file.includes('@')) {
|
|
598
|
-
const match = file.match(/([^-]+@[^.]+\.[^.]+)(?=\.json$)/);
|
|
599
|
-
if (match) {
|
|
600
|
-
email = match[1];
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
// Initialize provider section if needed
|
|
604
|
-
if (!registry.providers[provider]) {
|
|
605
|
-
registry.providers[provider] = {
|
|
606
|
-
default: 'default',
|
|
607
|
-
accounts: {},
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
const providerAccounts = registry.providers[provider];
|
|
611
|
-
if (!providerAccounts)
|
|
612
|
-
continue;
|
|
613
|
-
// Skip if token file already registered (under any accountId)
|
|
614
|
-
const existingTokenFiles = Object.values(providerAccounts.accounts).map((a) => a.tokenFile);
|
|
615
|
-
if (existingTokenFiles.includes(file)) {
|
|
616
|
-
// Token file exists - check if we need to update projectId for agy accounts
|
|
617
|
-
const projectIdValue = typeof data.project_id === 'string' && data.project_id.trim()
|
|
618
|
-
? data.project_id.trim()
|
|
619
|
-
: null;
|
|
620
|
-
if (provider === 'agy' && projectIdValue) {
|
|
621
|
-
const existingEntry = Object.entries(providerAccounts.accounts).find(([, meta]) => meta.tokenFile === file);
|
|
622
|
-
// Update if missing or changed
|
|
623
|
-
if (existingEntry && existingEntry[1].projectId !== projectIdValue) {
|
|
624
|
-
existingEntry[1].projectId = projectIdValue;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
// Determine accountId based on provider type
|
|
630
|
-
let accountId;
|
|
631
|
-
if (exports.PROVIDERS_WITHOUT_EMAIL.includes(provider) && !email) {
|
|
632
|
-
// For kiro/ghcp without email: extract from filename or generate unique
|
|
633
|
-
// Pattern: kiro-github-ABC123.json -> github-ABC123
|
|
634
|
-
const filenameId = extractAccountIdFromTokenFile(file, undefined);
|
|
635
|
-
if (filenameId !== 'default') {
|
|
636
|
-
accountId = filenameId;
|
|
637
|
-
}
|
|
638
|
-
else {
|
|
639
|
-
// Generate unique ID: provider + incrementing index
|
|
640
|
-
let index = 1;
|
|
641
|
-
while (providerAccounts.accounts[`${provider}-${index}`]) {
|
|
642
|
-
index++;
|
|
643
|
-
}
|
|
644
|
-
accountId = `${provider}-${index}`;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
// For providers with email: use email or filename extraction
|
|
649
|
-
accountId = extractAccountIdFromTokenFile(file, email);
|
|
650
|
-
}
|
|
651
|
-
// Skip if account already registered
|
|
652
|
-
if (providerAccounts.accounts[accountId]) {
|
|
653
|
-
continue;
|
|
654
|
-
}
|
|
655
|
-
// Set as default if first account
|
|
656
|
-
if (Object.keys(providerAccounts.accounts).length === 0) {
|
|
657
|
-
providerAccounts.default = accountId;
|
|
658
|
-
}
|
|
659
|
-
// Get file stats for creation time
|
|
660
|
-
const stats = fs.statSync(filePath);
|
|
661
|
-
// Register account with auto-generated nickname
|
|
662
|
-
// Use mtime as lastUsedAt (when token was last modified = last auth/refresh)
|
|
663
|
-
const lastModified = stats.mtime || stats.birthtime || new Date();
|
|
664
|
-
const accountMeta = {
|
|
665
|
-
email,
|
|
666
|
-
nickname: generateNickname(email),
|
|
667
|
-
tokenFile: file,
|
|
668
|
-
createdAt: stats.birthtime?.toISOString() || new Date().toISOString(),
|
|
669
|
-
lastUsedAt: lastModified.toISOString(),
|
|
670
|
-
};
|
|
671
|
-
// Read project_id for Antigravity accounts (read-only field from auth token)
|
|
672
|
-
const discoveredProjectId = typeof data.project_id === 'string' && data.project_id.trim()
|
|
673
|
-
? data.project_id.trim()
|
|
674
|
-
: null;
|
|
675
|
-
if (provider === 'agy' && discoveredProjectId) {
|
|
676
|
-
accountMeta.projectId = discoveredProjectId;
|
|
677
|
-
}
|
|
678
|
-
providerAccounts.accounts[accountId] = accountMeta;
|
|
679
|
-
}
|
|
680
|
-
catch {
|
|
681
|
-
// Skip invalid files
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
// Reload-merge pattern: reduce race condition with concurrent OAuth registration
|
|
686
|
-
// Reload fresh registry and merge discovered accounts (fresh registry wins on conflicts)
|
|
687
|
-
const freshRegistry = loadAccountsRegistry();
|
|
688
|
-
for (const [providerName, discovered] of Object.entries(registry.providers)) {
|
|
689
|
-
if (!discovered)
|
|
690
|
-
continue;
|
|
691
|
-
const prov = providerName;
|
|
692
|
-
if (!freshRegistry.providers[prov]) {
|
|
693
|
-
freshRegistry.providers[prov] = discovered;
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
// Merge accounts, preferring fresh registry's existing entries but updating projectId
|
|
697
|
-
const freshProviderAccounts = freshRegistry.providers[prov];
|
|
698
|
-
if (!freshProviderAccounts)
|
|
699
|
-
continue;
|
|
700
|
-
for (const [id, meta] of Object.entries(discovered.accounts)) {
|
|
701
|
-
if (!freshProviderAccounts.accounts[id]) {
|
|
702
|
-
freshProviderAccounts.accounts[id] = meta;
|
|
703
|
-
// Set default if none exists
|
|
704
|
-
if (!freshProviderAccounts.default || freshProviderAccounts.default === 'default') {
|
|
705
|
-
freshProviderAccounts.default = id;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
else if (meta.projectId && !freshProviderAccounts.accounts[id].projectId) {
|
|
709
|
-
// Update existing account with projectId if discovered from auth file
|
|
710
|
-
freshProviderAccounts.accounts[id].projectId = meta.projectId;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
saveAccountsRegistry(freshRegistry);
|
|
716
|
-
}
|
|
717
|
-
exports.discoverExistingAccounts = discoverExistingAccounts;
|
|
718
|
-
/** Provider-level locks for preventing race conditions in solo mode */
|
|
719
|
-
const providerLocks = new Map();
|
|
720
|
-
/**
|
|
721
|
-
* Bulk pause multiple accounts
|
|
722
|
-
* Pauses each account, collecting successes and failures (no fail-fast)
|
|
723
|
-
*/
|
|
724
|
-
function bulkPauseAccounts(provider, accountIds) {
|
|
725
|
-
const result = { succeeded: [], failed: [] };
|
|
726
|
-
for (const id of accountIds) {
|
|
727
|
-
const success = pauseAccount(provider, id);
|
|
728
|
-
if (success) {
|
|
729
|
-
result.succeeded.push(id);
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
result.failed.push({ id, reason: 'Account not found' });
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
return result;
|
|
736
|
-
}
|
|
737
|
-
exports.bulkPauseAccounts = bulkPauseAccounts;
|
|
738
|
-
/**
|
|
739
|
-
* Bulk resume multiple accounts
|
|
740
|
-
* Resumes each account, collecting successes and failures (no fail-fast)
|
|
741
|
-
*/
|
|
742
|
-
function bulkResumeAccounts(provider, accountIds) {
|
|
743
|
-
const result = { succeeded: [], failed: [] };
|
|
744
|
-
for (const id of accountIds) {
|
|
745
|
-
const success = resumeAccount(provider, id);
|
|
746
|
-
if (success) {
|
|
747
|
-
result.succeeded.push(id);
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
result.failed.push({ id, reason: 'Account not found' });
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
return result;
|
|
754
|
-
}
|
|
755
|
-
exports.bulkResumeAccounts = bulkResumeAccounts;
|
|
756
|
-
/**
|
|
757
|
-
* Solo mode: activate one account, pause all others in same provider
|
|
758
|
-
* Per validation: auto-resumes target if paused, keeps default unchanged
|
|
759
|
-
*/
|
|
760
|
-
async function soloAccount(provider, accountId) {
|
|
761
|
-
// Wait for any pending operation on this provider
|
|
762
|
-
const pending = providerLocks.get(provider);
|
|
763
|
-
if (pending)
|
|
764
|
-
await pending;
|
|
765
|
-
const operation = (async () => {
|
|
766
|
-
const accounts = getProviderAccounts(provider);
|
|
767
|
-
const targetAccount = accounts.find((a) => a.id === accountId);
|
|
768
|
-
if (!targetAccount) {
|
|
769
|
-
return null;
|
|
770
|
-
}
|
|
771
|
-
const result = { activated: accountId, paused: [] };
|
|
772
|
-
// Resume target account if paused (per validation decision)
|
|
773
|
-
if (targetAccount.paused) {
|
|
774
|
-
resumeAccount(provider, accountId);
|
|
775
|
-
}
|
|
776
|
-
// Pause all other accounts
|
|
777
|
-
for (const account of accounts) {
|
|
778
|
-
if (account.id !== accountId && !account.paused) {
|
|
779
|
-
const success = pauseAccount(provider, account.id);
|
|
780
|
-
if (success) {
|
|
781
|
-
result.paused.push(account.id);
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
return result;
|
|
786
|
-
})();
|
|
787
|
-
providerLocks.set(provider, operation);
|
|
788
|
-
try {
|
|
789
|
-
return await operation;
|
|
790
|
-
}
|
|
791
|
-
finally {
|
|
792
|
-
providerLocks.delete(provider);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
exports.soloAccount = soloAccount;
|
|
796
|
-
/**
|
|
797
|
-
* Get summary of all accounts across providers
|
|
798
|
-
*/
|
|
799
|
-
function getAllAccountsSummary() {
|
|
800
|
-
const providers = [...profile_detector_1.CLIPROXY_PROFILES];
|
|
801
|
-
const summary = {};
|
|
802
|
-
for (const provider of providers) {
|
|
803
|
-
summary[provider] = getProviderAccounts(provider);
|
|
804
|
-
}
|
|
805
|
-
return summary;
|
|
806
|
-
}
|
|
807
|
-
exports.getAllAccountsSummary = getAllAccountsSummary;
|
|
16
|
+
exports.soloAccount = exports.bulkResumeAccounts = exports.bulkPauseAccounts = exports.getAllAccountsSummary = exports.isAccountPaused = exports.getActiveAccounts = exports.findAccountByQuery = exports.getAccount = exports.getDefaultAccount = exports.getProviderAccounts = exports.discoverExistingAccounts = exports.setAccountTier = exports.touchAccount = exports.renameAccount = exports.removeAccount = exports.resumeAccount = exports.pauseAccount = exports.setDefaultAccount = exports.registerAccount = exports.syncRegistryWithTokenFiles = exports.saveAccountsRegistry = exports.loadAccountsRegistry = exports.tokenFileExists = exports.validateNickname = exports.generateNickname = exports.extractAccountIdFromTokenFile = exports.getAccountTokenPath = exports.getPausedDir = exports.getAccountsRegistryPath = exports.PROVIDERS_WITHOUT_EMAIL = void 0;
|
|
17
|
+
var accounts_1 = require("./accounts");
|
|
18
|
+
Object.defineProperty(exports, "PROVIDERS_WITHOUT_EMAIL", { enumerable: true, get: function () { return accounts_1.PROVIDERS_WITHOUT_EMAIL; } });
|
|
19
|
+
Object.defineProperty(exports, "getAccountsRegistryPath", { enumerable: true, get: function () { return accounts_1.getAccountsRegistryPath; } });
|
|
20
|
+
Object.defineProperty(exports, "getPausedDir", { enumerable: true, get: function () { return accounts_1.getPausedDir; } });
|
|
21
|
+
Object.defineProperty(exports, "getAccountTokenPath", { enumerable: true, get: function () { return accounts_1.getAccountTokenPath; } });
|
|
22
|
+
Object.defineProperty(exports, "extractAccountIdFromTokenFile", { enumerable: true, get: function () { return accounts_1.extractAccountIdFromTokenFile; } });
|
|
23
|
+
Object.defineProperty(exports, "generateNickname", { enumerable: true, get: function () { return accounts_1.generateNickname; } });
|
|
24
|
+
Object.defineProperty(exports, "validateNickname", { enumerable: true, get: function () { return accounts_1.validateNickname; } });
|
|
25
|
+
Object.defineProperty(exports, "tokenFileExists", { enumerable: true, get: function () { return accounts_1.tokenFileExists; } });
|
|
26
|
+
Object.defineProperty(exports, "loadAccountsRegistry", { enumerable: true, get: function () { return accounts_1.loadAccountsRegistry; } });
|
|
27
|
+
Object.defineProperty(exports, "saveAccountsRegistry", { enumerable: true, get: function () { return accounts_1.saveAccountsRegistry; } });
|
|
28
|
+
Object.defineProperty(exports, "syncRegistryWithTokenFiles", { enumerable: true, get: function () { return accounts_1.syncRegistryWithTokenFiles; } });
|
|
29
|
+
Object.defineProperty(exports, "registerAccount", { enumerable: true, get: function () { return accounts_1.registerAccount; } });
|
|
30
|
+
Object.defineProperty(exports, "setDefaultAccount", { enumerable: true, get: function () { return accounts_1.setDefaultAccount; } });
|
|
31
|
+
Object.defineProperty(exports, "pauseAccount", { enumerable: true, get: function () { return accounts_1.pauseAccount; } });
|
|
32
|
+
Object.defineProperty(exports, "resumeAccount", { enumerable: true, get: function () { return accounts_1.resumeAccount; } });
|
|
33
|
+
Object.defineProperty(exports, "removeAccount", { enumerable: true, get: function () { return accounts_1.removeAccount; } });
|
|
34
|
+
Object.defineProperty(exports, "renameAccount", { enumerable: true, get: function () { return accounts_1.renameAccount; } });
|
|
35
|
+
Object.defineProperty(exports, "touchAccount", { enumerable: true, get: function () { return accounts_1.touchAccount; } });
|
|
36
|
+
Object.defineProperty(exports, "setAccountTier", { enumerable: true, get: function () { return accounts_1.setAccountTier; } });
|
|
37
|
+
Object.defineProperty(exports, "discoverExistingAccounts", { enumerable: true, get: function () { return accounts_1.discoverExistingAccounts; } });
|
|
38
|
+
Object.defineProperty(exports, "getProviderAccounts", { enumerable: true, get: function () { return accounts_1.getProviderAccounts; } });
|
|
39
|
+
Object.defineProperty(exports, "getDefaultAccount", { enumerable: true, get: function () { return accounts_1.getDefaultAccount; } });
|
|
40
|
+
Object.defineProperty(exports, "getAccount", { enumerable: true, get: function () { return accounts_1.getAccount; } });
|
|
41
|
+
Object.defineProperty(exports, "findAccountByQuery", { enumerable: true, get: function () { return accounts_1.findAccountByQuery; } });
|
|
42
|
+
Object.defineProperty(exports, "getActiveAccounts", { enumerable: true, get: function () { return accounts_1.getActiveAccounts; } });
|
|
43
|
+
Object.defineProperty(exports, "isAccountPaused", { enumerable: true, get: function () { return accounts_1.isAccountPaused; } });
|
|
44
|
+
Object.defineProperty(exports, "getAllAccountsSummary", { enumerable: true, get: function () { return accounts_1.getAllAccountsSummary; } });
|
|
45
|
+
Object.defineProperty(exports, "bulkPauseAccounts", { enumerable: true, get: function () { return accounts_1.bulkPauseAccounts; } });
|
|
46
|
+
Object.defineProperty(exports, "bulkResumeAccounts", { enumerable: true, get: function () { return accounts_1.bulkResumeAccounts; } });
|
|
47
|
+
Object.defineProperty(exports, "soloAccount", { enumerable: true, get: function () { return accounts_1.soloAccount; } });
|
|
808
48
|
//# sourceMappingURL=account-manager.js.map
|