commons-proxy 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +757 -0
- package/bin/cli.js +146 -0
- package/package.json +97 -0
- package/public/Complaint Details.pdf +0 -0
- package/public/Cyber Crime Portal.pdf +0 -0
- package/public/app.js +229 -0
- package/public/css/src/input.css +523 -0
- package/public/css/style.css +1 -0
- package/public/favicon.png +0 -0
- package/public/index.html +549 -0
- package/public/js/components/account-manager.js +356 -0
- package/public/js/components/add-account-modal.js +414 -0
- package/public/js/components/claude-config.js +420 -0
- package/public/js/components/dashboard/charts.js +605 -0
- package/public/js/components/dashboard/filters.js +362 -0
- package/public/js/components/dashboard/stats.js +110 -0
- package/public/js/components/dashboard.js +236 -0
- package/public/js/components/logs-viewer.js +100 -0
- package/public/js/components/models.js +36 -0
- package/public/js/components/server-config.js +349 -0
- package/public/js/config/constants.js +102 -0
- package/public/js/data-store.js +375 -0
- package/public/js/settings-store.js +58 -0
- package/public/js/store.js +99 -0
- package/public/js/translations/en.js +367 -0
- package/public/js/translations/id.js +412 -0
- package/public/js/translations/pt.js +308 -0
- package/public/js/translations/tr.js +358 -0
- package/public/js/translations/zh.js +373 -0
- package/public/js/utils/account-actions.js +189 -0
- package/public/js/utils/error-handler.js +96 -0
- package/public/js/utils/model-config.js +42 -0
- package/public/js/utils/ui-logger.js +143 -0
- package/public/js/utils/validators.js +77 -0
- package/public/js/utils.js +69 -0
- package/public/proxy-server-64.png +0 -0
- package/public/views/accounts.html +361 -0
- package/public/views/dashboard.html +484 -0
- package/public/views/logs.html +97 -0
- package/public/views/models.html +331 -0
- package/public/views/settings.html +1327 -0
- package/src/account-manager/credentials.js +378 -0
- package/src/account-manager/index.js +462 -0
- package/src/account-manager/onboarding.js +112 -0
- package/src/account-manager/rate-limits.js +369 -0
- package/src/account-manager/storage.js +160 -0
- package/src/account-manager/strategies/base-strategy.js +109 -0
- package/src/account-manager/strategies/hybrid-strategy.js +339 -0
- package/src/account-manager/strategies/index.js +79 -0
- package/src/account-manager/strategies/round-robin-strategy.js +76 -0
- package/src/account-manager/strategies/sticky-strategy.js +138 -0
- package/src/account-manager/strategies/trackers/health-tracker.js +162 -0
- package/src/account-manager/strategies/trackers/index.js +9 -0
- package/src/account-manager/strategies/trackers/quota-tracker.js +120 -0
- package/src/account-manager/strategies/trackers/token-bucket-tracker.js +155 -0
- package/src/auth/database.js +169 -0
- package/src/auth/oauth.js +548 -0
- package/src/auth/token-extractor.js +117 -0
- package/src/cli/accounts.js +648 -0
- package/src/cloudcode/index.js +29 -0
- package/src/cloudcode/message-handler.js +510 -0
- package/src/cloudcode/model-api.js +248 -0
- package/src/cloudcode/rate-limit-parser.js +235 -0
- package/src/cloudcode/request-builder.js +93 -0
- package/src/cloudcode/session-manager.js +47 -0
- package/src/cloudcode/sse-parser.js +121 -0
- package/src/cloudcode/sse-streamer.js +293 -0
- package/src/cloudcode/streaming-handler.js +615 -0
- package/src/config.js +125 -0
- package/src/constants.js +407 -0
- package/src/errors.js +242 -0
- package/src/fallback-config.js +29 -0
- package/src/format/content-converter.js +193 -0
- package/src/format/index.js +20 -0
- package/src/format/request-converter.js +255 -0
- package/src/format/response-converter.js +120 -0
- package/src/format/schema-sanitizer.js +673 -0
- package/src/format/signature-cache.js +88 -0
- package/src/format/thinking-utils.js +648 -0
- package/src/index.js +148 -0
- package/src/modules/usage-stats.js +205 -0
- package/src/providers/anthropic-provider.js +258 -0
- package/src/providers/base-provider.js +157 -0
- package/src/providers/cloudcode.js +94 -0
- package/src/providers/copilot.js +399 -0
- package/src/providers/github-provider.js +287 -0
- package/src/providers/google-provider.js +192 -0
- package/src/providers/index.js +211 -0
- package/src/providers/openai-compatible.js +265 -0
- package/src/providers/openai-provider.js +271 -0
- package/src/providers/openrouter-provider.js +325 -0
- package/src/providers/setup.js +83 -0
- package/src/server.js +870 -0
- package/src/utils/claude-config.js +245 -0
- package/src/utils/helpers.js +51 -0
- package/src/utils/logger.js +142 -0
- package/src/utils/native-module-helper.js +162 -0
- package/src/webui/index.js +1134 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials Management
|
|
3
|
+
*
|
|
4
|
+
* Handles OAuth token handling and project discovery.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
CLOUDCODE_DB_PATH,
|
|
9
|
+
TOKEN_REFRESH_INTERVAL_MS,
|
|
10
|
+
LOAD_CODE_ASSIST_ENDPOINTS,
|
|
11
|
+
LOAD_CODE_ASSIST_HEADERS,
|
|
12
|
+
DEFAULT_PROJECT_ID
|
|
13
|
+
} from '../constants.js';
|
|
14
|
+
import { refreshAccessToken, parseRefreshParts, formatRefreshParts } from '../auth/oauth.js';
|
|
15
|
+
import { getAuthStatus } from '../auth/database.js';
|
|
16
|
+
import { logger } from '../utils/logger.js';
|
|
17
|
+
import { isNetworkError } from '../utils/helpers.js';
|
|
18
|
+
import { onboardUser, getDefaultTierId } from './onboarding.js';
|
|
19
|
+
import { parseTierId } from '../cloudcode/model-api.js';
|
|
20
|
+
import { getProviderForAccount } from '../providers/index.js';
|
|
21
|
+
|
|
22
|
+
// Track accounts currently fetching subscription to avoid duplicate calls
|
|
23
|
+
const subscriptionFetchInProgress = new Set();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Fetch subscription tier and save it (blocking)
|
|
27
|
+
* Used when we have a cached project but missing subscription data
|
|
28
|
+
*
|
|
29
|
+
* @param {string} token - OAuth access token
|
|
30
|
+
* @param {Object} account - Account object
|
|
31
|
+
* @param {Function} [onSave] - Callback to save account changes
|
|
32
|
+
*/
|
|
33
|
+
async function fetchAndSaveSubscription(token, account, onSave) {
|
|
34
|
+
// Avoid duplicate fetches for the same account
|
|
35
|
+
if (subscriptionFetchInProgress.has(account.email)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
subscriptionFetchInProgress.add(account.email);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// Call discoverProject just to get subscription info
|
|
42
|
+
const { subscription } = await discoverProject(token, account.projectId);
|
|
43
|
+
if (subscription && subscription.tier !== 'unknown') {
|
|
44
|
+
account.subscription = subscription;
|
|
45
|
+
if (onSave) {
|
|
46
|
+
await onSave();
|
|
47
|
+
}
|
|
48
|
+
logger.info(`[AccountManager] Updated subscription tier for ${account.email}: ${subscription.tier}`);
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {
|
|
51
|
+
logger.debug(`[AccountManager] Subscription fetch failed for ${account.email}: ${e.message}`);
|
|
52
|
+
} finally {
|
|
53
|
+
subscriptionFetchInProgress.delete(account.email);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get OAuth token for an account using provider abstraction
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} account - Account object with email and credentials
|
|
61
|
+
* @param {Map} tokenCache - Token cache map
|
|
62
|
+
* @param {Function} onInvalid - Callback when account is invalid (email, reason)
|
|
63
|
+
* @param {Function} onSave - Callback to save changes
|
|
64
|
+
* @returns {Promise<string>} OAuth access token
|
|
65
|
+
* @throws {Error} If token refresh fails
|
|
66
|
+
*/
|
|
67
|
+
export async function getTokenForAccount(account, tokenCache, onInvalid, onSave) {
|
|
68
|
+
// Check cache first
|
|
69
|
+
const cached = tokenCache.get(account.email);
|
|
70
|
+
if (cached && (Date.now() - cached.extractedAt) < TOKEN_REFRESH_INTERVAL_MS) {
|
|
71
|
+
return cached.token;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let token;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// Use provider system to get access token
|
|
78
|
+
const provider = getProviderForAccount(account);
|
|
79
|
+
token = await provider.getAccessToken(account);
|
|
80
|
+
|
|
81
|
+
// Clear invalid flag on success
|
|
82
|
+
if (account.isInvalid) {
|
|
83
|
+
account.isInvalid = false;
|
|
84
|
+
account.invalidReason = null;
|
|
85
|
+
if (onSave) await onSave();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
logger.success(`[AccountManager] Got access token for ${account.email} (${provider.name})`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Check if it's a transient network error
|
|
91
|
+
if (isNetworkError(error)) {
|
|
92
|
+
logger.warn(`[AccountManager] Failed to get token for ${account.email} due to network error: ${error.message}`);
|
|
93
|
+
// Do NOT mark as invalid, just throw so caller knows it failed
|
|
94
|
+
throw new Error(`AUTH_NETWORK_ERROR: ${error.message}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
logger.error(`[AccountManager] Failed to get token for ${account.email}:`, error.message);
|
|
98
|
+
|
|
99
|
+
// Check if provider recommends invalidating credentials
|
|
100
|
+
try {
|
|
101
|
+
const provider = getProviderForAccount(account);
|
|
102
|
+
if (provider.shouldInvalidateCredentials(error)) {
|
|
103
|
+
if (onInvalid) onInvalid(account.email, error.message);
|
|
104
|
+
}
|
|
105
|
+
} catch (providerError) {
|
|
106
|
+
// Provider detection failed, use legacy behavior
|
|
107
|
+
logger.debug('[AccountManager] Provider detection failed, using legacy error handling');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw new Error(`AUTH_INVALID: ${account.email}: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Cache the token
|
|
114
|
+
tokenCache.set(account.email, {
|
|
115
|
+
token,
|
|
116
|
+
extractedAt: Date.now()
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return token;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get project ID for an account
|
|
124
|
+
* Aligned with opencode-cloudcode-auth: parses refresh token for stored project IDs
|
|
125
|
+
*
|
|
126
|
+
* @param {Object} account - Account object
|
|
127
|
+
* @param {string} token - OAuth access token
|
|
128
|
+
* @param {Map} projectCache - Project cache map
|
|
129
|
+
* @param {Function} [onSave] - Callback to save account changes
|
|
130
|
+
* @returns {Promise<string>} Project ID
|
|
131
|
+
*/
|
|
132
|
+
export async function getProjectForAccount(account, token, projectCache, onSave = null) {
|
|
133
|
+
// Check cache first
|
|
134
|
+
const cached = projectCache.get(account.email);
|
|
135
|
+
if (cached) {
|
|
136
|
+
return cached;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Parse refresh token to get stored project IDs (aligned with opencode-cloudcode-auth)
|
|
140
|
+
const parts = account.refreshToken ? parseRefreshParts(account.refreshToken) : { refreshToken: null, projectId: undefined, managedProjectId: undefined };
|
|
141
|
+
|
|
142
|
+
// If we have a managedProjectId in the refresh token, use it
|
|
143
|
+
if (parts.managedProjectId) {
|
|
144
|
+
projectCache.set(account.email, parts.managedProjectId);
|
|
145
|
+
// If subscription is missing/unknown, fetch it now (blocking)
|
|
146
|
+
if (!account.subscription || account.subscription.tier === 'unknown') {
|
|
147
|
+
await fetchAndSaveSubscription(token, account, onSave);
|
|
148
|
+
}
|
|
149
|
+
return parts.managedProjectId;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Legacy: check account.projectId for backward compatibility
|
|
153
|
+
if (account.projectId) {
|
|
154
|
+
projectCache.set(account.email, account.projectId);
|
|
155
|
+
// If subscription is missing/unknown, fetch it now (blocking)
|
|
156
|
+
if (!account.subscription || account.subscription.tier === 'unknown') {
|
|
157
|
+
await fetchAndSaveSubscription(token, account, onSave);
|
|
158
|
+
}
|
|
159
|
+
return account.projectId;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Discover managed project, passing projectId for metadata.duetProject
|
|
163
|
+
// Reference: opencode-cloudcode-auth - discoverProject handles fallback internally
|
|
164
|
+
const { project, subscription } = await discoverProject(token, parts.projectId);
|
|
165
|
+
|
|
166
|
+
// Store managedProjectId back in refresh token (if we got a real project)
|
|
167
|
+
if (project && project !== DEFAULT_PROJECT_ID) {
|
|
168
|
+
let needsSave = false;
|
|
169
|
+
|
|
170
|
+
if (account.refreshToken) {
|
|
171
|
+
// OAuth accounts: encode in refresh token
|
|
172
|
+
account.refreshToken = formatRefreshParts({
|
|
173
|
+
refreshToken: parts.refreshToken,
|
|
174
|
+
projectId: parts.projectId,
|
|
175
|
+
managedProjectId: project,
|
|
176
|
+
});
|
|
177
|
+
needsSave = true;
|
|
178
|
+
} else if (account.source === 'database' || account.source === 'manual') {
|
|
179
|
+
// Database/manual accounts: store in projectId field
|
|
180
|
+
account.projectId = project;
|
|
181
|
+
needsSave = true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Save subscription tier if discovered
|
|
185
|
+
if (subscription) {
|
|
186
|
+
account.subscription = subscription;
|
|
187
|
+
needsSave = true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Trigger save to persist the updated project and subscription
|
|
191
|
+
if (needsSave && onSave) {
|
|
192
|
+
try {
|
|
193
|
+
await onSave();
|
|
194
|
+
} catch (e) {
|
|
195
|
+
logger.warn(`[AccountManager] Failed to save updated project: ${e.message}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else if (subscription) {
|
|
199
|
+
// Even if no project discovered, save subscription if we got it
|
|
200
|
+
account.subscription = subscription;
|
|
201
|
+
if (onSave) {
|
|
202
|
+
try {
|
|
203
|
+
await onSave();
|
|
204
|
+
} catch (e) {
|
|
205
|
+
logger.warn(`[AccountManager] Failed to save subscription: ${e.message}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
projectCache.set(account.email, project);
|
|
211
|
+
return project;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Discover project ID via Cloud Code API
|
|
216
|
+
*
|
|
217
|
+
* @param {string} token - OAuth access token
|
|
218
|
+
* @param {string} [projectId] - Optional project ID from refresh token (for metadata.duetProject)
|
|
219
|
+
* @returns {Promise<{project: string, subscription: {tier: string, projectId: string|null, detectedAt: string}|null}>} Project and subscription info
|
|
220
|
+
*/
|
|
221
|
+
export async function discoverProject(token, projectId = undefined) {
|
|
222
|
+
let lastError = null;
|
|
223
|
+
let gotSuccessfulResponse = false;
|
|
224
|
+
let loadCodeAssistData = null;
|
|
225
|
+
|
|
226
|
+
const metadata = {
|
|
227
|
+
ideType: 'IDE_UNSPECIFIED',
|
|
228
|
+
platform: 'PLATFORM_UNSPECIFIED',
|
|
229
|
+
pluginType: 'GEMINI'
|
|
230
|
+
};
|
|
231
|
+
if (projectId) {
|
|
232
|
+
metadata.duetProject = projectId;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const endpoint of LOAD_CODE_ASSIST_ENDPOINTS) {
|
|
236
|
+
try {
|
|
237
|
+
const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
|
|
238
|
+
method: 'POST',
|
|
239
|
+
headers: {
|
|
240
|
+
'Authorization': `Bearer ${token}`,
|
|
241
|
+
'Content-Type': 'application/json',
|
|
242
|
+
...LOAD_CODE_ASSIST_HEADERS
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify({ metadata })
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
const errorText = await response.text();
|
|
249
|
+
lastError = `${response.status} - ${errorText}`;
|
|
250
|
+
logger.debug(`[AccountManager] loadCodeAssist failed at ${endpoint}: ${lastError}`);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const data = await response.json();
|
|
255
|
+
gotSuccessfulResponse = true;
|
|
256
|
+
loadCodeAssistData = data;
|
|
257
|
+
|
|
258
|
+
logger.debug(`[AccountManager] loadCodeAssist response from ${endpoint}:`, JSON.stringify(data));
|
|
259
|
+
|
|
260
|
+
// Extract subscription tier from response
|
|
261
|
+
const subscription = extractSubscriptionFromResponse(data);
|
|
262
|
+
|
|
263
|
+
if (typeof data.cloudaicompanionProject === 'string') {
|
|
264
|
+
logger.success(`[AccountManager] Discovered project: ${data.cloudaicompanionProject}`);
|
|
265
|
+
return { project: data.cloudaicompanionProject, subscription };
|
|
266
|
+
}
|
|
267
|
+
if (data.cloudaicompanionProject?.id) {
|
|
268
|
+
logger.success(`[AccountManager] Discovered project: ${data.cloudaicompanionProject.id}`);
|
|
269
|
+
return { project: data.cloudaicompanionProject.id, subscription };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// No project found - log tier data and try to onboard the user
|
|
273
|
+
logger.info(`[AccountManager] No project in loadCodeAssist response, attempting onboardUser...`);
|
|
274
|
+
logger.debug(`[AccountManager] Tier data for onboarding: paidTier=${JSON.stringify(data.paidTier)}, currentTier=${JSON.stringify(data.currentTier)}, allowedTiers=${JSON.stringify(data.allowedTiers?.map(t => ({ id: t?.id, isDefault: t?.isDefault })))}`);
|
|
275
|
+
break;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
lastError = error.message;
|
|
278
|
+
logger.debug(`[AccountManager] loadCodeAssist error at ${endpoint}:`, error.message);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// If we got a successful response but no project, try onboarding
|
|
283
|
+
if (gotSuccessfulResponse && loadCodeAssistData) {
|
|
284
|
+
// Only use allowedTiers for onboarding (matching opencode-cloudcode-auth and oauth.js)
|
|
285
|
+
// Note: paidTier (g1-pro-tier, g1-ultra-tier) is NOT valid for onboardUser API
|
|
286
|
+
// The paidTier is used for subscription detection only, not for onboarding
|
|
287
|
+
const tierId = getDefaultTierId(loadCodeAssistData.allowedTiers) || 'free-tier';
|
|
288
|
+
logger.info(`[AccountManager] Onboarding user with tier: ${tierId}`);
|
|
289
|
+
|
|
290
|
+
// Pass projectId for metadata.duetProject (without fallback, matching reference)
|
|
291
|
+
// Reference: opencode-cloudcode-auth passes parts.projectId (not fallback) to onboardManagedProject
|
|
292
|
+
const onboardedProject = await onboardUser(
|
|
293
|
+
token,
|
|
294
|
+
tierId,
|
|
295
|
+
projectId // Original projectId without fallback
|
|
296
|
+
);
|
|
297
|
+
if (onboardedProject) {
|
|
298
|
+
logger.success(`[AccountManager] Successfully onboarded, project: ${onboardedProject}`);
|
|
299
|
+
const subscription = extractSubscriptionFromResponse(loadCodeAssistData);
|
|
300
|
+
return { project: onboardedProject, subscription };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
logger.warn(`[AccountManager] Onboarding failed - account may not work correctly`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Only warn if all endpoints failed with errors (not just missing project)
|
|
307
|
+
if (!gotSuccessfulResponse) {
|
|
308
|
+
logger.warn(`[AccountManager] loadCodeAssist failed for all endpoints: ${lastError}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Fallback: use projectId if available, otherwise use default
|
|
312
|
+
// Reference: opencode-cloudcode-auth/src/plugin/project.ts
|
|
313
|
+
if (projectId) {
|
|
314
|
+
return { project: projectId, subscription: null };
|
|
315
|
+
}
|
|
316
|
+
return { project: DEFAULT_PROJECT_ID, subscription: null };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Extract subscription tier from loadCodeAssist response
|
|
321
|
+
*
|
|
322
|
+
* @param {Object} data - loadCodeAssist response data
|
|
323
|
+
* @returns {{tier: string, projectId: string|null, detectedAt: string}|null} Subscription info
|
|
324
|
+
*/
|
|
325
|
+
function extractSubscriptionFromResponse(data) {
|
|
326
|
+
if (!data) return null;
|
|
327
|
+
|
|
328
|
+
// Priority: paidTier > currentTier (consistent with model-api.js)
|
|
329
|
+
let tier = 'free';
|
|
330
|
+
let cloudProject = null;
|
|
331
|
+
|
|
332
|
+
if (data.paidTier?.id) {
|
|
333
|
+
tier = parseTierId(data.paidTier.id);
|
|
334
|
+
} else if (data.currentTier?.id) {
|
|
335
|
+
tier = parseTierId(data.currentTier.id);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Get project ID
|
|
339
|
+
if (typeof data.cloudaicompanionProject === 'string') {
|
|
340
|
+
cloudProject = data.cloudaicompanionProject;
|
|
341
|
+
} else if (data.cloudaicompanionProject?.id) {
|
|
342
|
+
cloudProject = data.cloudaicompanionProject.id;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
tier,
|
|
347
|
+
projectId: cloudProject,
|
|
348
|
+
detectedAt: new Date().toISOString()
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Clear project cache for an account
|
|
354
|
+
*
|
|
355
|
+
* @param {Map} projectCache - Project cache map
|
|
356
|
+
* @param {string|null} email - Email to clear cache for, or null to clear all
|
|
357
|
+
*/
|
|
358
|
+
export function clearProjectCache(projectCache, email = null) {
|
|
359
|
+
if (email) {
|
|
360
|
+
projectCache.delete(email);
|
|
361
|
+
} else {
|
|
362
|
+
projectCache.clear();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Clear token cache for an account
|
|
368
|
+
*
|
|
369
|
+
* @param {Map} tokenCache - Token cache map
|
|
370
|
+
* @param {string|null} email - Email to clear cache for, or null to clear all
|
|
371
|
+
*/
|
|
372
|
+
export function clearTokenCache(tokenCache, email = null) {
|
|
373
|
+
if (email) {
|
|
374
|
+
tokenCache.delete(email);
|
|
375
|
+
} else {
|
|
376
|
+
tokenCache.clear();
|
|
377
|
+
}
|
|
378
|
+
}
|