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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +757 -0
  3. package/bin/cli.js +146 -0
  4. package/package.json +97 -0
  5. package/public/Complaint Details.pdf +0 -0
  6. package/public/Cyber Crime Portal.pdf +0 -0
  7. package/public/app.js +229 -0
  8. package/public/css/src/input.css +523 -0
  9. package/public/css/style.css +1 -0
  10. package/public/favicon.png +0 -0
  11. package/public/index.html +549 -0
  12. package/public/js/components/account-manager.js +356 -0
  13. package/public/js/components/add-account-modal.js +414 -0
  14. package/public/js/components/claude-config.js +420 -0
  15. package/public/js/components/dashboard/charts.js +605 -0
  16. package/public/js/components/dashboard/filters.js +362 -0
  17. package/public/js/components/dashboard/stats.js +110 -0
  18. package/public/js/components/dashboard.js +236 -0
  19. package/public/js/components/logs-viewer.js +100 -0
  20. package/public/js/components/models.js +36 -0
  21. package/public/js/components/server-config.js +349 -0
  22. package/public/js/config/constants.js +102 -0
  23. package/public/js/data-store.js +375 -0
  24. package/public/js/settings-store.js +58 -0
  25. package/public/js/store.js +99 -0
  26. package/public/js/translations/en.js +367 -0
  27. package/public/js/translations/id.js +412 -0
  28. package/public/js/translations/pt.js +308 -0
  29. package/public/js/translations/tr.js +358 -0
  30. package/public/js/translations/zh.js +373 -0
  31. package/public/js/utils/account-actions.js +189 -0
  32. package/public/js/utils/error-handler.js +96 -0
  33. package/public/js/utils/model-config.js +42 -0
  34. package/public/js/utils/ui-logger.js +143 -0
  35. package/public/js/utils/validators.js +77 -0
  36. package/public/js/utils.js +69 -0
  37. package/public/proxy-server-64.png +0 -0
  38. package/public/views/accounts.html +361 -0
  39. package/public/views/dashboard.html +484 -0
  40. package/public/views/logs.html +97 -0
  41. package/public/views/models.html +331 -0
  42. package/public/views/settings.html +1327 -0
  43. package/src/account-manager/credentials.js +378 -0
  44. package/src/account-manager/index.js +462 -0
  45. package/src/account-manager/onboarding.js +112 -0
  46. package/src/account-manager/rate-limits.js +369 -0
  47. package/src/account-manager/storage.js +160 -0
  48. package/src/account-manager/strategies/base-strategy.js +109 -0
  49. package/src/account-manager/strategies/hybrid-strategy.js +339 -0
  50. package/src/account-manager/strategies/index.js +79 -0
  51. package/src/account-manager/strategies/round-robin-strategy.js +76 -0
  52. package/src/account-manager/strategies/sticky-strategy.js +138 -0
  53. package/src/account-manager/strategies/trackers/health-tracker.js +162 -0
  54. package/src/account-manager/strategies/trackers/index.js +9 -0
  55. package/src/account-manager/strategies/trackers/quota-tracker.js +120 -0
  56. package/src/account-manager/strategies/trackers/token-bucket-tracker.js +155 -0
  57. package/src/auth/database.js +169 -0
  58. package/src/auth/oauth.js +548 -0
  59. package/src/auth/token-extractor.js +117 -0
  60. package/src/cli/accounts.js +648 -0
  61. package/src/cloudcode/index.js +29 -0
  62. package/src/cloudcode/message-handler.js +510 -0
  63. package/src/cloudcode/model-api.js +248 -0
  64. package/src/cloudcode/rate-limit-parser.js +235 -0
  65. package/src/cloudcode/request-builder.js +93 -0
  66. package/src/cloudcode/session-manager.js +47 -0
  67. package/src/cloudcode/sse-parser.js +121 -0
  68. package/src/cloudcode/sse-streamer.js +293 -0
  69. package/src/cloudcode/streaming-handler.js +615 -0
  70. package/src/config.js +125 -0
  71. package/src/constants.js +407 -0
  72. package/src/errors.js +242 -0
  73. package/src/fallback-config.js +29 -0
  74. package/src/format/content-converter.js +193 -0
  75. package/src/format/index.js +20 -0
  76. package/src/format/request-converter.js +255 -0
  77. package/src/format/response-converter.js +120 -0
  78. package/src/format/schema-sanitizer.js +673 -0
  79. package/src/format/signature-cache.js +88 -0
  80. package/src/format/thinking-utils.js +648 -0
  81. package/src/index.js +148 -0
  82. package/src/modules/usage-stats.js +205 -0
  83. package/src/providers/anthropic-provider.js +258 -0
  84. package/src/providers/base-provider.js +157 -0
  85. package/src/providers/cloudcode.js +94 -0
  86. package/src/providers/copilot.js +399 -0
  87. package/src/providers/github-provider.js +287 -0
  88. package/src/providers/google-provider.js +192 -0
  89. package/src/providers/index.js +211 -0
  90. package/src/providers/openai-compatible.js +265 -0
  91. package/src/providers/openai-provider.js +271 -0
  92. package/src/providers/openrouter-provider.js +325 -0
  93. package/src/providers/setup.js +83 -0
  94. package/src/server.js +870 -0
  95. package/src/utils/claude-config.js +245 -0
  96. package/src/utils/helpers.js +51 -0
  97. package/src/utils/logger.js +142 -0
  98. package/src/utils/native-module-helper.js +162 -0
  99. package/src/webui/index.js +1134 -0
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Add Account Modal Component
3
+ * Registers itself to window.Components for Alpine.js to consume
4
+ * Supports multi-provider account addition (Google OAuth, API keys, PAT, Device Auth)
5
+ */
6
+ window.Components = window.Components || {};
7
+
8
+ window.Components.addAccountModal = () => ({
9
+ // Provider selection
10
+ providers: [],
11
+ selectedProvider: 'google',
12
+
13
+ // Form fields
14
+ email: '',
15
+ apiKey: '',
16
+ customEndpoint: '',
17
+
18
+ // OAuth state (Google)
19
+ manualMode: false,
20
+ authUrl: '',
21
+ authState: '',
22
+ callbackInput: '',
23
+ submitting: false,
24
+
25
+ // Copilot Device Auth state
26
+ copilotFlowId: null,
27
+ copilotUserCode: '',
28
+ copilotVerificationUri: '',
29
+ copilotPolling: false,
30
+ copilotPollTimer: null,
31
+
32
+ async init() {
33
+ // Fetch available providers on modal init
34
+ await this.loadProviders();
35
+ },
36
+
37
+ async loadProviders() {
38
+ try {
39
+ const store = Alpine.store('global');
40
+ const { response, newPassword } = await window.utils.request(
41
+ '/api/providers',
42
+ {},
43
+ store.webuiPassword
44
+ );
45
+ if (newPassword) store.webuiPassword = newPassword;
46
+
47
+ const data = await response.json();
48
+ if (data.status === 'ok' && Array.isArray(data.providers)) {
49
+ this.providers = data.providers.map(p => ({
50
+ id: p.id,
51
+ name: p.name,
52
+ authType: p.authType,
53
+ color: p.config?.color || '#4285f4'
54
+ }));
55
+ }
56
+ } catch (e) {
57
+ console.error('Failed to load providers:', e);
58
+ // Fallback to default providers if API fails
59
+ this.providers = [
60
+ { id: 'google', name: 'Google Cloud Code', authType: 'oauth', color: '#4285f4' },
61
+ { id: 'anthropic', name: 'Anthropic', authType: 'api-key', color: '#d97706' },
62
+ { id: 'openai', name: 'OpenAI', authType: 'api-key', color: '#10b981' },
63
+ { id: 'github', name: 'GitHub Models', authType: 'api-key', color: '#6366f1' },
64
+ { id: 'copilot', name: 'GitHub Copilot', authType: 'device-auth', color: '#f97316' },
65
+ { id: 'openrouter', name: 'OpenRouter', authType: 'api-key', color: '#6d28d9' }
66
+ ];
67
+ }
68
+ },
69
+
70
+ get currentProvider() {
71
+ return this.providers.find(p => p.id === this.selectedProvider) || this.providers[0];
72
+ },
73
+
74
+ get isOAuthProvider() {
75
+ return this.currentProvider?.authType === 'oauth';
76
+ },
77
+
78
+ get requiresApiKey() {
79
+ return this.currentProvider?.authType === 'api-key' || this.currentProvider?.authType === 'pat';
80
+ },
81
+
82
+ get isDeviceAuthProvider() {
83
+ return this.currentProvider?.authType === 'device-auth';
84
+ },
85
+
86
+ get apiKeyLabel() {
87
+ if (this.currentProvider?.authType === 'pat') {
88
+ return 'Personal Access Token';
89
+ }
90
+ return 'API Key';
91
+ },
92
+
93
+ get apiKeyPlaceholder() {
94
+ const provider = this.currentProvider;
95
+ if (!provider) return 'Enter your API key';
96
+
97
+ if (provider.id === 'anthropic') {
98
+ return 'sk-ant-api03-...';
99
+ } else if (provider.id === 'openai') {
100
+ return 'sk-...';
101
+ } else if (provider.id === 'github') {
102
+ return 'github_pat_...';
103
+ } else if (provider.id === 'openrouter') {
104
+ return 'sk-or-v1-...';
105
+ }
106
+ return 'Enter your API key or token';
107
+ },
108
+
109
+ onProviderChange() {
110
+ // Reset form fields when provider changes
111
+ this.email = '';
112
+ this.apiKey = '';
113
+ this.customEndpoint = '';
114
+ this.authUrl = '';
115
+ this.callbackInput = '';
116
+ this.manualMode = false;
117
+ // Stop any active Copilot polling
118
+ this.stopCopilotPolling();
119
+ this.copilotFlowId = null;
120
+ this.copilotUserCode = '';
121
+ this.copilotVerificationUri = '';
122
+ },
123
+
124
+ // ==================== OAuth Methods (Google) ====================
125
+
126
+ async addAccountWeb() {
127
+ const store = Alpine.store('global');
128
+ const password = store.webuiPassword;
129
+
130
+ try {
131
+ store.showOAuthProgress();
132
+ const { response, newPassword } = await window.utils.request('/api/auth/url', {}, password);
133
+ if (newPassword) store.webuiPassword = newPassword;
134
+
135
+ const data = await response.json();
136
+ if (data.status === 'ok') {
137
+ window.open(data.url, 'google_oauth', 'width=600,height=700,scrollbars=yes');
138
+ } else {
139
+ store.showToast(data.error || store.t('authUrlFailed'), 'error');
140
+ store.hideOAuthProgress();
141
+ }
142
+ } catch (e) {
143
+ store.showToast(store.t('authUrlFailed') + ': ' + e.message, 'error');
144
+ store.hideOAuthProgress();
145
+ }
146
+ },
147
+
148
+ async initManualAuth(event) {
149
+ if (!event.target.open || this.authUrl) return;
150
+
151
+ try {
152
+ const password = Alpine.store('global').webuiPassword;
153
+ const {
154
+ response,
155
+ newPassword
156
+ } = await window.utils.request('/api/auth/url', {}, password);
157
+ if (newPassword) Alpine.store('global').webuiPassword = newPassword;
158
+ const data = await response.json();
159
+ if (data.status === 'ok') {
160
+ this.authUrl = data.url;
161
+ this.authState = data.state;
162
+ }
163
+ } catch (e) {
164
+ Alpine.store('global').showToast(e.message, 'error');
165
+ }
166
+ },
167
+
168
+ async copyLink() {
169
+ if (!this.authUrl) return;
170
+ await navigator.clipboard.writeText(this.authUrl);
171
+ Alpine.store('global').showToast(Alpine.store('global').t('linkCopied'), 'success');
172
+ },
173
+
174
+ async completeManualAuth() {
175
+ if (!this.callbackInput || !this.authState) return;
176
+ this.submitting = true;
177
+ try {
178
+ const store = Alpine.store('global');
179
+ const {
180
+ response,
181
+ newPassword
182
+ } = await window.utils.request('/api/auth/complete', {
183
+ method: 'POST',
184
+ headers: {
185
+ 'Content-Type': 'application/json'
186
+ },
187
+ body: JSON.stringify({
188
+ callbackInput: this.callbackInput,
189
+ state: this.authState
190
+ })
191
+ }, store.webuiPassword);
192
+ if (newPassword) store.webuiPassword = newPassword;
193
+ const data = await response.json();
194
+ if (data.status === 'ok') {
195
+ store.showToast(store.t('accountAddedSuccess'), 'success');
196
+ Alpine.store('data').fetchData();
197
+ document.getElementById('add_account_modal').close();
198
+ this.resetState();
199
+ } else {
200
+ store.showToast(data.error || store.t('authFailed'), 'error');
201
+ }
202
+ } catch (e) {
203
+ Alpine.store('global').showToast(e.message, 'error');
204
+ } finally {
205
+ this.submitting = false;
206
+ }
207
+ },
208
+
209
+ // ==================== Copilot Device Auth Methods ====================
210
+
211
+ async startCopilotDeviceAuth() {
212
+ const store = Alpine.store('global');
213
+ this.submitting = true;
214
+
215
+ try {
216
+ const { response, newPassword } = await window.utils.request(
217
+ '/api/copilot/device-auth',
218
+ { method: 'POST', headers: { 'Content-Type': 'application/json' } },
219
+ store.webuiPassword
220
+ );
221
+ if (newPassword) store.webuiPassword = newPassword;
222
+
223
+ const data = await response.json();
224
+ if (data.status === 'ok') {
225
+ this.copilotFlowId = data.flowId;
226
+ this.copilotUserCode = data.userCode;
227
+ this.copilotVerificationUri = data.verificationUri;
228
+
229
+ // Open GitHub verification page
230
+ window.open(data.verificationUri, '_blank', 'width=600,height=700,scrollbars=yes');
231
+
232
+ store.showToast('Enter the code shown below on GitHub to authorize', 'info');
233
+
234
+ // Start polling for token
235
+ this.startCopilotPolling(data.interval || 5);
236
+ } else {
237
+ store.showToast(data.error || 'Failed to start device auth', 'error');
238
+ }
239
+ } catch (e) {
240
+ store.showToast('Device auth failed: ' + e.message, 'error');
241
+ } finally {
242
+ this.submitting = false;
243
+ }
244
+ },
245
+
246
+ startCopilotPolling(interval) {
247
+ this.copilotPolling = true;
248
+ const poll = async () => {
249
+ if (!this.copilotPolling || !this.copilotFlowId) return;
250
+
251
+ try {
252
+ const store = Alpine.store('global');
253
+ const { response, newPassword } = await window.utils.request(
254
+ '/api/copilot/poll-token',
255
+ {
256
+ method: 'POST',
257
+ headers: { 'Content-Type': 'application/json' },
258
+ body: JSON.stringify({ flowId: this.copilotFlowId })
259
+ },
260
+ store.webuiPassword
261
+ );
262
+ if (newPassword) store.webuiPassword = newPassword;
263
+
264
+ const data = await response.json();
265
+
266
+ if (data.completed) {
267
+ // Success!
268
+ this.stopCopilotPolling();
269
+ store.showToast(`Copilot account ${data.email} added successfully!`, 'success');
270
+ Alpine.store('data').fetchData();
271
+ document.getElementById('add_account_modal').close();
272
+ this.resetState();
273
+ return;
274
+ }
275
+
276
+ if (data.status === 'error') {
277
+ this.stopCopilotPolling();
278
+ store.showToast(data.error || 'Device auth failed', 'error');
279
+ return;
280
+ }
281
+
282
+ // Update interval if server says to slow down
283
+ if (data.interval) {
284
+ interval = data.interval;
285
+ }
286
+
287
+ // Continue polling
288
+ this.copilotPollTimer = setTimeout(poll, interval * 1000);
289
+ } catch (e) {
290
+ this.stopCopilotPolling();
291
+ Alpine.store('global').showToast('Polling error: ' + e.message, 'error');
292
+ }
293
+ };
294
+
295
+ this.copilotPollTimer = setTimeout(poll, interval * 1000);
296
+ },
297
+
298
+ stopCopilotPolling() {
299
+ this.copilotPolling = false;
300
+ if (this.copilotPollTimer) {
301
+ clearTimeout(this.copilotPollTimer);
302
+ this.copilotPollTimer = null;
303
+ }
304
+ },
305
+
306
+ async copyCopilotCode() {
307
+ if (!this.copilotUserCode) return;
308
+ await navigator.clipboard.writeText(this.copilotUserCode);
309
+ Alpine.store('global').showToast('Code copied to clipboard', 'success');
310
+ },
311
+
312
+ // ==================== API Key Methods (Anthropic, OpenAI, GitHub) ==
313
+
314
+ async addAccountWithProvider() {
315
+ return await window.ErrorHandler.withLoading(async () => {
316
+ const store = Alpine.store('global');
317
+
318
+ // Validate inputs
319
+ if (!this.email || !this.email.includes('@')) {
320
+ throw new Error(store.t('invalidEmail') || 'Invalid email address');
321
+ }
322
+ if (!this.apiKey) {
323
+ throw new Error(store.t('apiKeyRequired') || `${this.apiKeyLabel} is required`);
324
+ }
325
+
326
+ // Validate credentials before adding
327
+ store.showToast(store.t('validatingCredentials') || 'Validating credentials...', 'info');
328
+ const validationResult = await this.validateCredentials();
329
+ if (!validationResult.valid) {
330
+ throw new Error(validationResult.error || 'Credential validation failed');
331
+ }
332
+
333
+ // Add account
334
+ const payload = {
335
+ provider: this.selectedProvider,
336
+ email: this.email,
337
+ apiKey: this.apiKey
338
+ };
339
+
340
+ if (this.customEndpoint && this.customEndpoint.trim()) {
341
+ payload.customApiEndpoint = this.customEndpoint.trim();
342
+ }
343
+
344
+ const { response, newPassword } = await window.utils.request(
345
+ '/api/accounts/add',
346
+ {
347
+ method: 'POST',
348
+ headers: { 'Content-Type': 'application/json' },
349
+ body: JSON.stringify(payload)
350
+ },
351
+ store.webuiPassword
352
+ );
353
+ if (newPassword) store.webuiPassword = newPassword;
354
+
355
+ const data = await response.json();
356
+ if (data.status === 'ok') {
357
+ store.showToast(store.t('accountAdded', { email: this.email }), 'success');
358
+ Alpine.store('data').fetchData();
359
+ document.getElementById('add_account_modal').close();
360
+ this.resetState();
361
+ } else {
362
+ throw new Error(data.error || store.t('addAccountFailed'));
363
+ }
364
+ }, this, 'submitting', { errorMessage: 'Failed to add account' });
365
+ },
366
+
367
+ async validateCredentials() {
368
+ const store = Alpine.store('global');
369
+
370
+ try {
371
+ const { response, newPassword } = await window.utils.request(
372
+ `/api/providers/${this.selectedProvider}/validate`,
373
+ {
374
+ method: 'POST',
375
+ headers: { 'Content-Type': 'application/json' },
376
+ body: JSON.stringify({
377
+ apiKey: this.apiKey,
378
+ customApiEndpoint: this.customEndpoint || undefined
379
+ })
380
+ },
381
+ store.webuiPassword
382
+ );
383
+ if (newPassword) store.webuiPassword = newPassword;
384
+
385
+ const data = await response.json();
386
+ return data; // { valid: boolean, error?: string }
387
+ } catch (e) {
388
+ return { valid: false, error: e.message };
389
+ }
390
+ },
391
+
392
+ /**
393
+ * Reset all state to initial values
394
+ */
395
+ resetState() {
396
+ this.selectedProvider = 'google';
397
+ this.email = '';
398
+ this.apiKey = '';
399
+ this.customEndpoint = '';
400
+ this.manualMode = false;
401
+ this.authUrl = '';
402
+ this.authState = '';
403
+ this.callbackInput = '';
404
+ this.submitting = false;
405
+ // Stop Copilot polling
406
+ this.stopCopilotPolling();
407
+ this.copilotFlowId = null;
408
+ this.copilotUserCode = '';
409
+ this.copilotVerificationUri = '';
410
+ // Close any open details elements
411
+ const details = document.querySelectorAll('#add_account_modal details[open]');
412
+ details.forEach(d => d.removeAttribute('open'));
413
+ }
414
+ });