antigravity-claude-proxy 2.9.3 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.js +4 -11
  3. package/package.json +5 -5
  4. package/public/app.js +7 -4
  5. package/public/css/src/input.css +0 -7
  6. package/public/css/style.css +1 -1
  7. package/public/js/components/claude-config.js +0 -8
  8. package/public/js/components/dashboard/charts.js +8 -8
  9. package/public/js/components/dashboard/stats.js +1 -1
  10. package/public/js/components/logs-viewer.js +1 -1
  11. package/public/js/components/models.js +2 -3
  12. package/public/js/components/server-config.js +1 -9
  13. package/public/js/data-store.js +2 -2
  14. package/public/js/store.js +1 -1
  15. package/public/js/translations/en.js +0 -2
  16. package/public/js/translations/id.js +0 -1
  17. package/public/js/translations/pt.js +0 -1
  18. package/public/js/translations/tr.js +0 -2
  19. package/public/js/translations/zh.js +0 -2
  20. package/public/js/utils/error-handler.js +0 -2
  21. package/public/js/utils/ui-logger.js +4 -3
  22. package/src/account-manager/index.js +0 -4
  23. package/src/account-manager/rate-limits.js +0 -5
  24. package/src/account-manager/strategies/base-strategy.js +4 -4
  25. package/src/account-manager/strategies/hybrid-strategy.js +3 -3
  26. package/src/auth/database.js +1 -0
  27. package/src/auth/oauth.js +43 -41
  28. package/src/auth/token-extractor.js +1 -1
  29. package/src/cli/accounts.js +2 -1
  30. package/src/cloudcode/model-registry.js +0 -3
  31. package/src/cloudcode/rate-limit-state.js +1 -7
  32. package/src/config.js +1 -1
  33. package/src/constants.js +0 -2
  34. package/src/format/content-converter.js +0 -4
  35. package/src/format/request-converter.js +1 -6
  36. package/src/format/response-converter.js +0 -3
  37. package/src/format/schema-sanitizer.js +3 -22
  38. package/src/format/thinking-utils.js +1 -13
  39. package/src/index.js +1 -6
  40. package/src/modules/usage-stats.js +1 -1
  41. package/src/server.js +8 -29
  42. package/src/utils/claude-config.js +2 -6
  43. package/src/utils/native-module-helper.js +0 -4
  44. package/src/utils/proxy.js +1 -10
  45. package/src/utils/version-detector.js +14 -5
  46. package/src/webui/index.js +1 -37
@@ -203,10 +203,6 @@ window.Components.claudeConfig = () => ({
203
203
  }
204
204
  },
205
205
 
206
- // ==========================================
207
- // Presets Management
208
- // ==========================================
209
-
210
206
  /**
211
207
  * Fetch all saved presets from the server
212
208
  */
@@ -438,10 +434,6 @@ window.Components.claudeConfig = () => ({
438
434
  }
439
435
  },
440
436
 
441
- // ==========================================
442
- // Mode Toggle (Proxy/Paid)
443
- // ==========================================
444
-
445
437
  /**
446
438
  * Fetch current mode from server
447
439
  */
@@ -75,7 +75,7 @@ function isCanvasReady(canvas) {
75
75
  try {
76
76
  const ctx = canvas.getContext("2d");
77
77
  return !!ctx;
78
- } catch (e) {
78
+ } catch {
79
79
  return false;
80
80
  }
81
81
  }
@@ -139,14 +139,14 @@ window.DashboardCharts.updateCharts = function (component) {
139
139
 
140
140
  // Safety checks
141
141
  if (!canvas) {
142
- console.debug("quotaChart canvas not found");
142
+ window.UILogger?.debug("quotaChart canvas not found");
143
143
  return;
144
144
  }
145
145
 
146
146
  // FORCE DESTROY: Check for existing chart on the canvas element property
147
147
  // This handles cases where Component state is lost but DOM persists
148
148
  if (canvas._chartInstance) {
149
- console.debug("Destroying existing quota chart from canvas property");
149
+ window.UILogger?.debug("Destroying existing quota chart from canvas property");
150
150
  try {
151
151
  canvas._chartInstance.destroy();
152
152
  } catch(e) { if (window.UILogger) window.UILogger.debug(e); }
@@ -157,7 +157,7 @@ window.DashboardCharts.updateCharts = function (component) {
157
157
  if (component.charts.quotaDistribution) {
158
158
  try {
159
159
  component.charts.quotaDistribution.destroy();
160
- } catch(e) { }
160
+ } catch (e) { window.UILogger?.debug("quotaDistribution destroy failed", e); }
161
161
  component.charts.quotaDistribution = null;
162
162
  }
163
163
 
@@ -165,7 +165,7 @@ window.DashboardCharts.updateCharts = function (component) {
165
165
  if (typeof Chart !== "undefined" && Chart.getChart) {
166
166
  const regChart = Chart.getChart(canvas);
167
167
  if (regChart) {
168
- try { regChart.destroy(); } catch(e) {}
168
+ try { regChart.destroy(); } catch (e) { window.UILogger?.debug("Registry chart destroy failed", e); }
169
169
  }
170
170
  }
171
171
 
@@ -332,7 +332,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
332
332
  // FORCE DESTROY: Check for existing chart on the canvas element property
333
333
  if (canvas) {
334
334
  if (canvas._chartInstance) {
335
- console.debug("Destroying existing trend chart from canvas property");
335
+ window.UILogger?.debug("Destroying existing trend chart from canvas property");
336
336
  try {
337
337
  canvas._chartInstance.stop();
338
338
  canvas._chartInstance.destroy();
@@ -344,7 +344,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
344
344
  if (typeof Chart !== "undefined" && Chart.getChart) {
345
345
  const regChart = Chart.getChart(canvas);
346
346
  if (regChart) {
347
- try { regChart.stop(); regChart.destroy(); } catch(e) {}
347
+ try { regChart.stop(); regChart.destroy(); } catch (e) { window.UILogger?.debug("Registry chart destroy failed", e); }
348
348
  }
349
349
  }
350
350
  }
@@ -354,7 +354,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
354
354
  try {
355
355
  component.charts.usageTrend.stop();
356
356
  component.charts.usageTrend.destroy();
357
- } catch (e) { }
357
+ } catch (e) { window.UILogger?.debug("usageTrend destroy failed", e); }
358
358
  component.charts.usageTrend = null;
359
359
  }
360
360
 
@@ -81,7 +81,7 @@ window.DashboardStats.updateStats = function(component) {
81
81
 
82
82
  enabledAccounts.forEach(acc => {
83
83
  const limits = Object.entries(acc.limits || {});
84
- limits.forEach(([id, l]) => {
84
+ limits.forEach(([_id, l]) => {
85
85
  totalTrackedModels++;
86
86
  if (!l || l.remainingFraction == null || l.remainingFraction <= 0.05) {
87
87
  totalLimitedModels++;
@@ -28,7 +28,7 @@ window.Components.logsViewer = () => ({
28
28
  try {
29
29
  const regex = new RegExp(query, 'i');
30
30
  matcher = (msg) => regex.test(msg);
31
- } catch (e) {
31
+ } catch {
32
32
  // Invalid regex, fallback to case-insensitive string search
33
33
  const lowerQuery = query.toLowerCase();
34
34
  matcher = (msg) => msg.toLowerCase().includes(lowerQuery);
@@ -32,7 +32,6 @@ window.Components.models = () => ({
32
32
  originalPct: 0
33
33
  },
34
34
 
35
- // Tracks which model rows have their per-account breakdown expanded beyond the cap
36
35
  expandedModels: new Set(),
37
36
 
38
37
  isExpanded(modelId) {
@@ -197,7 +196,7 @@ window.Components.models = () => ({
197
196
  const previousModelThresholds = account.modelQuotaThresholds ? { ...account.modelQuotaThresholds } : {};
198
197
 
199
198
  // Build full modelQuotaThresholds for API (full replacement, not merge)
200
- const existingModelThresholds = { ...(account.modelQuotaThresholds || {}) };
199
+ const existingModelThresholds = { ...account.modelQuotaThresholds };
201
200
 
202
201
  // Preserve the account-level quotaThreshold
203
202
  const quotaThreshold = account.quotaThreshold !== undefined ? account.quotaThreshold : null;
@@ -251,7 +250,7 @@ window.Components.models = () => ({
251
250
  * Markers within 2% of each other are considered overlapping.
252
251
  * Returns a CSS pixel offset string (e.g., '6px' or '-6px').
253
252
  */
254
- getMarkerOffset(q, row, qIdx) {
253
+ getMarkerOffset(q, row, _qIdx) {
255
254
  const pct = this.getMarkerPct(q, row);
256
255
  const visible = row.quotaInfo.filter(item => item.thresholdPct > 0 || this.isDragging(item, row));
257
256
  // Find all markers within 2% of this one
@@ -452,10 +452,6 @@ window.Components.serverConfig = () => ({
452
452
  return descriptions[strategy] || '';
453
453
  },
454
454
 
455
- // ==========================================
456
- // Server Configuration Presets
457
- // ==========================================
458
-
459
455
  async fetchServerPresets() {
460
456
  const password = Alpine.store('global').webuiPassword;
461
457
  try {
@@ -847,10 +843,6 @@ window.Components.serverConfig = () => ({
847
843
  };
848
844
  },
849
845
 
850
- // ==========================================
851
- // Inline Preset Config Editing
852
- // ==========================================
853
-
854
846
  /**
855
847
  * Flatten a nested config object into a flat draft for UI editing.
856
848
  * Converts fractions to percentages where needed.
@@ -1040,7 +1032,7 @@ window.Components.serverConfig = () => ({
1040
1032
  // Validate capacityBackoffTiersMs text
1041
1033
  this.validatePresetConfigField('capacityBackoffTiersMs', this.editingConfigDraft.capacityBackoffTiersMs);
1042
1034
  this.configEditMode = 'ui';
1043
- } catch (e) {
1035
+ } catch {
1044
1036
  this.jsonParseError = Alpine.store('global').t('invalidJson') || 'Invalid JSON';
1045
1037
  }
1046
1038
  }
@@ -51,7 +51,7 @@ document.addEventListener('alpine:init', () => {
51
51
  if (saved.placeholderMode) {
52
52
  this.setPlaceholderMode(true, saved.placeholderIncludeReal !== false);
53
53
  }
54
- } catch (e) { /* ignore parse errors */ }
54
+ } catch { /* ignore parse errors */ }
55
55
 
56
56
  // Watch filters to recompute
57
57
  // Alpine stores don't have $watch automatically unless inside a component?
@@ -187,7 +187,7 @@ document.addEventListener('alpine:init', () => {
187
187
  if (data.config) {
188
188
  this.devMode = !!data.config.devMode;
189
189
  }
190
- } catch (e) { /* ignore parse errors */ }
190
+ } catch { /* ignore parse errors */ }
191
191
  } else {
192
192
  this.connectionStatus = 'disconnected';
193
193
  }
@@ -68,7 +68,7 @@ document.addEventListener('alpine:init', () => {
68
68
  }
69
69
  }
70
70
  } catch (error) {
71
- console.debug('Could not fetch version:', error);
71
+ window.UILogger?.debug('Could not fetch version:', error);
72
72
  }
73
73
  },
74
74
 
@@ -88,7 +88,6 @@ window.translations.en = {
88
88
  toggleVisibility: "Toggle Visibility",
89
89
  noModels: "NO MODELS DETECTED",
90
90
  modelMappingHint: "Server-side model routing. Claude Code users: see 'Claude CLI' tab for client-side setup.",
91
- modelMapping: "Mapping (Target Model ID)",
92
91
  // Settings - Claude
93
92
  proxyConnection: "Proxy Connection",
94
93
  modelSelection: "Model Selection",
@@ -179,7 +178,6 @@ window.translations.en = {
179
178
  requestVolume: "Request Volume",
180
179
  filter: "Filter",
181
180
  all: "All",
182
- none: "None",
183
181
  noDataTracked: "No data tracked yet",
184
182
  selectFamilies: "Select families to display",
185
183
  selectModels: "Select models to display",
@@ -228,7 +228,6 @@ window.translations.id = {
228
228
  reauthenticating: "Autentikasi ulang {email}...",
229
229
  authUrlFailed: "Gagal mendapatkan URL autentikasi",
230
230
  deletedAccount: "{email} berhasil dihapus",
231
- deleteFailed: "Gagal menghapus",
232
231
  accountsReloaded: "Akun berhasil dimuat ulang",
233
232
  reloadFailed: "Gagal memuat ulang",
234
233
 
@@ -155,7 +155,6 @@ window.translations.pt = {
155
155
  requestVolume: "Volume de Requisições",
156
156
  filter: "Filtrar",
157
157
  all: "Todos",
158
- none: "Nenhum",
159
158
  noDataTracked: "Nenhum dado rastreado ainda",
160
159
  selectFamilies: "Selecione famílias para exibir",
161
160
  selectModels: "Selecione modelos para exibir",
@@ -88,7 +88,6 @@ window.translations.tr = {
88
88
  toggleVisibility: "Görünürlüğü Değiştir",
89
89
  noModels: "MODEL ALGILANMADI",
90
90
  modelMappingHint: "Sunucu tarafı model yönlendirme. Claude Code kullanıcıları: istemci tarafı kurulumu için 'Claude CLI' sekmesine bakın.",
91
- modelMapping: "Eşleştirme (Hedef Model ID)",
92
91
  // Settings - Claude
93
92
  proxyConnection: "Proxy Bağlantısı",
94
93
  modelSelection: "Model Seçimi",
@@ -158,7 +157,6 @@ window.translations.tr = {
158
157
  requestVolume: "İstek Hacmi",
159
158
  filter: "Filtrele",
160
159
  all: "Tümü",
161
- none: "Hiçbiri",
162
160
  noDataTracked: "Henüz veri izlenmedi",
163
161
  selectFamilies: "Görüntülenecek aileleri seçin",
164
162
  selectModels: "Görüntülenecek modelleri seçin",
@@ -88,7 +88,6 @@ window.translations.zh = {
88
88
  toggleVisibility: "切换可见性",
89
89
  noModels: "未检测到模型",
90
90
  modelMappingHint: "服务端模型路由功能。Claude Code 用户请使用 'Claude CLI' 标签页以便捷配置。",
91
- modelMapping: "映射 (目标模型 ID)",
92
91
  // Settings - Claude
93
92
  proxyConnection: "代理连接",
94
93
  modelSelection: "模型选择",
@@ -179,7 +178,6 @@ window.translations.zh = {
179
178
  requestVolume: "请求量",
180
179
  filter: "筛选",
181
180
  all: "全选",
182
- none: "清空",
183
181
  noDataTracked: "暂无追踪数据",
184
182
  selectFamilies: "选择要显示的系列",
185
183
  selectModels: "选择要显示的模型",
@@ -21,7 +21,6 @@ window.ErrorHandler.safeAsync = async function(fn, errorMessage = null, options
21
21
  try {
22
22
  return await fn();
23
23
  } catch (error) {
24
- // Log error for debugging
25
24
  console.error(`[ErrorHandler] ${defaultErrorMessage}:`, error);
26
25
 
27
26
  // Show toast notification
@@ -86,7 +85,6 @@ window.ErrorHandler.withLoading = async function(asyncFn, context, loadingKey =
86
85
  context[loadingKey] = true;
87
86
 
88
87
  try {
89
- // Execute the async function with error handling
90
88
  const result = await window.ErrorHandler.safeAsync(asyncFn, options.errorMessage, options);
91
89
  return result;
92
90
  } finally {
@@ -28,7 +28,7 @@
28
28
  // Check localStorage
29
29
  try {
30
30
  return localStorage.getItem('ag_debug') === 'true';
31
- } catch (e) {
31
+ } catch {
32
32
  return false;
33
33
  }
34
34
  }
@@ -53,7 +53,7 @@
53
53
  debugEnabled = true;
54
54
  console.info('[UILogger] Debug mode enabled. Refresh page to see all logs.');
55
55
  } catch (e) {
56
- console.warn('[UILogger] Could not save debug preference');
56
+ console.warn('[UILogger] Could not save debug preference', e);
57
57
  }
58
58
  },
59
59
 
@@ -66,7 +66,7 @@
66
66
  debugEnabled = false;
67
67
  console.info('[UILogger] Debug mode disabled.');
68
68
  } catch (e) {
69
- // Ignore
69
+ console.warn('[UILogger] Could not clear debug preference', e);
70
70
  }
71
71
  },
72
72
 
@@ -103,6 +103,7 @@
103
103
  */
104
104
  log(...args) {
105
105
  if (debugEnabled) {
106
+ // aislop-ignore-next-line console-leftover -- UILogger's own output sink
106
107
  console.log(...args);
107
108
  }
108
109
  },
@@ -328,10 +328,6 @@ export class AccountManager {
328
328
  return getLimitInfo(this.#accounts, email, modelId);
329
329
  }
330
330
 
331
- // ============================================================================
332
- // Cooldown Methods (matches opencode-antigravity-auth)
333
- // ============================================================================
334
-
335
331
  /**
336
332
  * Mark an account as cooling down for a specified duration
337
333
  * Used for temporary backoff separate from rate limits
@@ -309,11 +309,6 @@ export function incrementConsecutiveFailures(accounts, email) {
309
309
  return account.consecutiveFailures;
310
310
  }
311
311
 
312
- // ============================================================================
313
- // Cooldown Mechanism (matches opencode-antigravity-auth)
314
- // Separate from rate limits - used for temporary backoff after failures
315
- // ============================================================================
316
-
317
312
  /**
318
313
  * Cooldown reasons for debugging/logging
319
314
  */
@@ -36,7 +36,7 @@ export class BaseStrategy {
36
36
  * @param {Function} [options.onSave] - Callback to save changes
37
37
  * @returns {SelectionResult} The selected account and index
38
38
  */
39
- selectAccount(accounts, modelId, options = {}) {
39
+ selectAccount(_accounts, _modelId, _options = {}) {
40
40
  throw new Error('selectAccount must be implemented by subclass');
41
41
  }
42
42
 
@@ -45,7 +45,7 @@ export class BaseStrategy {
45
45
  * @param {Object} account - The account that was used
46
46
  * @param {string} modelId - The model ID that was used
47
47
  */
48
- onSuccess(account, modelId) {
48
+ onSuccess(_account, _modelId) {
49
49
  // Default: no-op, override in subclass if needed
50
50
  }
51
51
 
@@ -54,7 +54,7 @@ export class BaseStrategy {
54
54
  * @param {Object} account - The account that was rate-limited
55
55
  * @param {string} modelId - The model ID that was rate-limited
56
56
  */
57
- onRateLimit(account, modelId) {
57
+ onRateLimit(_account, _modelId) {
58
58
  // Default: no-op, override in subclass if needed
59
59
  }
60
60
 
@@ -63,7 +63,7 @@ export class BaseStrategy {
63
63
  * @param {Object} account - The account that failed
64
64
  * @param {string} modelId - The model ID that failed
65
65
  */
66
- onFailure(account, modelId) {
66
+ onFailure(_account, _modelId) {
67
67
  // Default: no-op, override in subclass if needed
68
68
  }
69
69
 
@@ -117,7 +117,7 @@ export class HybridStrategy extends BaseStrategy {
117
117
  /**
118
118
  * Called after a successful request
119
119
  */
120
- onSuccess(account, modelId) {
120
+ onSuccess(account, _modelId) {
121
121
  if (account && account.email) {
122
122
  this.#healthTracker.recordSuccess(account.email);
123
123
  }
@@ -126,7 +126,7 @@ export class HybridStrategy extends BaseStrategy {
126
126
  /**
127
127
  * Called when a request is rate-limited
128
128
  */
129
- onRateLimit(account, modelId) {
129
+ onRateLimit(account, _modelId) {
130
130
  if (account && account.email) {
131
131
  this.#healthTracker.recordRateLimit(account.email);
132
132
  }
@@ -135,7 +135,7 @@ export class HybridStrategy extends BaseStrategy {
135
135
  /**
136
136
  * Called when a request fails
137
137
  */
138
- onFailure(account, modelId) {
138
+ onFailure(account, _modelId) {
139
139
  if (account && account.email) {
140
140
  this.#healthTracker.recordFailure(account.email);
141
141
  // Refund the token since the request didn't complete
@@ -55,6 +55,7 @@ function loadDatabaseModule() {
55
55
  return Database;
56
56
  } catch (retryError) {
57
57
  // Rebuild succeeded but reload failed - user needs to restart
58
+ logger.debug(`[Database] Reload after rebuild failed: ${retryError.message}`);
58
59
  moduleLoadError = new NativeModuleError(
59
60
  'Native module rebuild completed. Please restart the server to apply the fix.',
60
61
  true, // rebuildSucceeded
package/src/auth/oauth.js CHANGED
@@ -178,7 +178,7 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
178
178
  let actualPort = OAUTH_CONFIG.callbackPort;
179
179
  const host = process.env.HOST || '0.0.0.0';
180
180
 
181
- const promise = new Promise(async (resolve, reject) => {
181
+ const promise = new Promise((resolve, reject) => {
182
182
  // Build list of ports to try: primary + fallbacks
183
183
  const portsToTry = [OAUTH_CONFIG.callbackPort, ...(OAUTH_CONFIG.callbackFallbackPorts || [])];
184
184
  const errors = [];
@@ -265,37 +265,38 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
265
265
  });
266
266
 
267
267
  // Try ports with fallback logic (issue #176 - Windows EACCES fix)
268
- let boundSuccessfully = false;
269
- for (const port of portsToTry) {
270
- try {
271
- await tryBindPort(server, port, host);
272
- actualPort = port;
273
- boundSuccessfully = true;
274
-
275
- if (port !== OAUTH_CONFIG.callbackPort) {
276
- logger.warn(`[OAuth] Primary port ${OAUTH_CONFIG.callbackPort} unavailable, using fallback port ${port}`);
277
- } else {
278
- logger.info(`[OAuth] Callback server listening on ${host}:${port}`);
268
+ (async () => {
269
+ let boundSuccessfully = false;
270
+ for (const port of portsToTry) {
271
+ try {
272
+ await tryBindPort(server, port, host);
273
+ actualPort = port;
274
+ boundSuccessfully = true;
275
+
276
+ if (port !== OAUTH_CONFIG.callbackPort) {
277
+ logger.warn(`[OAuth] Primary port ${OAUTH_CONFIG.callbackPort} unavailable, using fallback port ${port}`);
278
+ } else {
279
+ logger.info(`[OAuth] Callback server listening on ${host}:${port}`);
280
+ }
281
+ break;
282
+ } catch (err) {
283
+ const errMsg = err.code === 'EACCES'
284
+ ? `Permission denied on port ${port}`
285
+ : err.code === 'EADDRINUSE'
286
+ ? `Port ${port} already in use`
287
+ : `Failed to bind port ${port}: ${err.message}`;
288
+ errors.push(errMsg);
289
+ logger.warn(`[OAuth] ${errMsg}`);
279
290
  }
280
- break;
281
- } catch (err) {
282
- const errMsg = err.code === 'EACCES'
283
- ? `Permission denied on port ${port}`
284
- : err.code === 'EADDRINUSE'
285
- ? `Port ${port} already in use`
286
- : `Failed to bind port ${port}: ${err.message}`;
287
- errors.push(errMsg);
288
- logger.warn(`[OAuth] ${errMsg}`);
289
291
  }
290
- }
291
292
 
292
- if (!boundSuccessfully) {
293
- // All ports failed - provide helpful error message
294
- const isWindows = process.platform === 'win32';
295
- let errorMsg = `Failed to start OAuth callback server.\nTried ports: ${portsToTry.join(', ')}\n\nErrors:\n${errors.join('\n')}`;
293
+ if (!boundSuccessfully) {
294
+ // All ports failed - provide helpful error message
295
+ const isWindows = process.platform === 'win32';
296
+ let errorMsg = `Failed to start OAuth callback server.\nTried ports: ${portsToTry.join(', ')}\n\nErrors:\n${errors.join('\n')}`;
296
297
 
297
- if (isWindows) {
298
- errorMsg += `\n
298
+ if (isWindows) {
299
+ errorMsg += `\n
299
300
  ================== WINDOWS TROUBLESHOOTING ==================
300
301
  The default port range may be reserved by Hyper-V/WSL2/Docker.
301
302
 
@@ -311,21 +312,22 @@ Option 3: Check reserved port ranges
311
312
  Option 4: Exclude port from reservation (run as Administrator)
312
313
  netsh int ipv4 add excludedportrange protocol=tcp startport=51121 numberofports=1
313
314
  ==============================================================`;
314
- } else {
315
- errorMsg += `\n\nTry setting a custom port: OAUTH_CALLBACK_PORT=3456`;
316
- }
317
-
318
- reject(new Error(errorMsg));
319
- return;
320
- }
315
+ } else {
316
+ errorMsg += `\n\nTry setting a custom port: OAUTH_CALLBACK_PORT=3456`;
317
+ }
321
318
 
322
- // Timeout after specified duration
323
- timeoutId = setTimeout(() => {
324
- if (!isAborted) {
325
- server.close();
326
- reject(new Error('OAuth callback timeout - no response received'));
319
+ reject(new Error(errorMsg));
320
+ return;
327
321
  }
328
- }, timeoutMs);
322
+
323
+ // Timeout after specified duration
324
+ timeoutId = setTimeout(() => {
325
+ if (!isAborted) {
326
+ server.close();
327
+ reject(new Error('OAuth callback timeout - no response received'));
328
+ }
329
+ }, timeoutMs);
330
+ })().catch(reject);
329
331
  });
330
332
 
331
333
  // Abort function to clean up server when manual completion happens
@@ -60,7 +60,7 @@ async function getTokenData() {
60
60
  return dbData;
61
61
  }
62
62
  } catch (err) {
63
- logger.warn('[Token] DB extraction failed, trying HTML page...');
63
+ logger.warn(`[Token] DB extraction failed (${err.message}), trying HTML page...`);
64
64
  }
65
65
 
66
66
  // Fallback to HTML page
@@ -50,7 +50,7 @@ function isServerRunning() {
50
50
  resolve(false);
51
51
  });
52
52
 
53
- socket.on('error', (err) => {
53
+ socket.on('error', () => {
54
54
  socket.destroy();
55
55
  resolve(false); // Port free
56
56
  });
@@ -447,6 +447,7 @@ async function verifyAccounts() {
447
447
  console.log(` ✓ ${email} - OK`);
448
448
  } catch (error) {
449
449
  console.log(` ✗ ${account.email} - ${error.message}`);
450
+ process.exitCode = 1;
450
451
  }
451
452
  }
452
453
  }
@@ -88,9 +88,6 @@ export function getRegisteredModel(modelId) {
88
88
  return registry.get(modelId);
89
89
  }
90
90
 
91
- /**
92
- * Clear all recorded metadata (used by tests).
93
- */
94
91
  export function clearModelRegistry() {
95
92
  registry.clear();
96
93
  }
@@ -1,10 +1,4 @@
1
- /**
2
- * Rate Limit State Management
3
- *
4
- * Shared utilities for rate limit tracking, backoff calculation,
5
- * and error classification. Used by both streaming and non-streaming handlers.
6
- */
7
-
1
+ // Rate limit tracking, backoff and error classification shared by streaming and non-streaming handlers.
8
2
  import {
9
3
  RATE_LIMIT_DEDUP_WINDOW_MS,
10
4
  RATE_LIMIT_STATE_RESET_MS,
package/src/config.js CHANGED
@@ -100,7 +100,7 @@ if (!fs.existsSync(CONFIG_DIR)) {
100
100
  try {
101
101
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
102
102
  } catch (err) {
103
- // Ignore
103
+ logger.warn(`[Config] Could not create config directory ${CONFIG_DIR}: ${err.message}`);
104
104
  }
105
105
  }
106
106
 
package/src/constants.js CHANGED
@@ -301,13 +301,11 @@ export const BUILTIN_MODEL_MAPPINGS = {
301
301
  'claude-sonnet-5': 'claude-sonnet-4-6'
302
302
  };
303
303
 
304
- // Default test models for each family (used by test suite)
305
304
  export const TEST_MODELS = {
306
305
  claude: 'claude-sonnet-4-6',
307
306
  gemini: 'gemini-3.8-flash-tiered'
308
307
  };
309
308
 
310
- // Default Claude CLI presets (used by WebUI settings)
311
309
  export const DEFAULT_PRESETS = [
312
310
  {
313
311
  name: 'Claude Thinking',
@@ -46,7 +46,6 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
46
46
  parts.push({ text: block.text });
47
47
  }
48
48
  } else if (block.type === 'image') {
49
- // Handle image content
50
49
  if (block.source?.type === 'base64') {
51
50
  // Base64-encoded image
52
51
  parts.push({
@@ -93,7 +92,6 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
93
92
  functionCall.id = block.id;
94
93
  }
95
94
 
96
- // Build the part with functionCall
97
95
  const part = { functionCall };
98
96
 
99
97
  // For Gemini models, include thoughtSignature at the part level
@@ -133,7 +131,6 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
133
131
  }
134
132
  }
135
133
 
136
- // Extract text content
137
134
  const texts = responseContent
138
135
  .filter(c => c.type === 'text')
139
136
  .map(c => c.text)
@@ -157,7 +154,6 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
157
154
  // This ensures all functionResponse parts are consecutive
158
155
  deferredInlineData.push(...imageParts);
159
156
  } else if (block.type === 'thinking') {
160
- // Handle thinking blocks with signature compatibility check
161
157
  if (block.signature && block.signature.length >= MIN_SIGNATURE_LENGTH) {
162
158
  const signatureFamily = getCachedSignatureFamily(block.signature);
163
159
  const targetFamily = isClaudeModel ? 'claude' : isGeminiModel ? 'gemini' : null;