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.
- package/README.md +1 -1
- package/bin/cli.js +4 -11
- package/package.json +5 -5
- package/public/app.js +7 -4
- package/public/css/src/input.css +0 -7
- package/public/css/style.css +1 -1
- package/public/js/components/claude-config.js +0 -8
- package/public/js/components/dashboard/charts.js +8 -8
- package/public/js/components/dashboard/stats.js +1 -1
- package/public/js/components/logs-viewer.js +1 -1
- package/public/js/components/models.js +2 -3
- package/public/js/components/server-config.js +1 -9
- package/public/js/data-store.js +2 -2
- package/public/js/store.js +1 -1
- package/public/js/translations/en.js +0 -2
- package/public/js/translations/id.js +0 -1
- package/public/js/translations/pt.js +0 -1
- package/public/js/translations/tr.js +0 -2
- package/public/js/translations/zh.js +0 -2
- package/public/js/utils/error-handler.js +0 -2
- package/public/js/utils/ui-logger.js +4 -3
- package/src/account-manager/index.js +0 -4
- package/src/account-manager/rate-limits.js +0 -5
- package/src/account-manager/strategies/base-strategy.js +4 -4
- package/src/account-manager/strategies/hybrid-strategy.js +3 -3
- package/src/auth/database.js +1 -0
- package/src/auth/oauth.js +43 -41
- package/src/auth/token-extractor.js +1 -1
- package/src/cli/accounts.js +2 -1
- package/src/cloudcode/model-registry.js +0 -3
- package/src/cloudcode/rate-limit-state.js +1 -7
- package/src/config.js +1 -1
- package/src/constants.js +0 -2
- package/src/format/content-converter.js +0 -4
- package/src/format/request-converter.js +1 -6
- package/src/format/response-converter.js +0 -3
- package/src/format/schema-sanitizer.js +3 -22
- package/src/format/thinking-utils.js +1 -13
- package/src/index.js +1 -6
- package/src/modules/usage-stats.js +1 -1
- package/src/server.js +8 -29
- package/src/utils/claude-config.js +2 -6
- package/src/utils/native-module-helper.js +0 -4
- package/src/utils/proxy.js +1 -10
- package/src/utils/version-detector.js +14 -5
- package/src/webui/index.js +1 -37
|
@@ -51,7 +51,7 @@ export function convertAnthropicToGoogle(anthropicRequest) {
|
|
|
51
51
|
// before any other processing, following the pattern from Antigravity-Manager.
|
|
52
52
|
const messages = cleanCacheControl(anthropicRequest.messages || []);
|
|
53
53
|
|
|
54
|
-
const { system, max_tokens, temperature, top_p, top_k, stop_sequences, tools,
|
|
54
|
+
const { system, max_tokens, temperature, top_p, top_k, stop_sequences, tools, thinking } = anthropicRequest;
|
|
55
55
|
const modelName = anthropicRequest.model || '';
|
|
56
56
|
const modelFamily = getModelFamily(modelName);
|
|
57
57
|
const isClaudeModel = modelFamily === 'claude';
|
|
@@ -63,7 +63,6 @@ export function convertAnthropicToGoogle(anthropicRequest) {
|
|
|
63
63
|
generationConfig: {}
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
// Handle system instruction
|
|
67
66
|
if (system) {
|
|
68
67
|
let systemParts = [];
|
|
69
68
|
if (typeof system === 'string') {
|
|
@@ -126,7 +125,6 @@ export function convertAnthropicToGoogle(anthropicRequest) {
|
|
|
126
125
|
if ((msg.role === 'assistant' || msg.role === 'model') && Array.isArray(msgContent)) {
|
|
127
126
|
// First, try to restore signatures for unsigned thinking blocks from cache
|
|
128
127
|
msgContent = restoreThinkingSignatures(msgContent);
|
|
129
|
-
// Remove trailing unsigned thinking blocks
|
|
130
128
|
msgContent = removeTrailingThinkingBlocks(msgContent);
|
|
131
129
|
// Reorder: thinking first, then text, then tool_use
|
|
132
130
|
msgContent = reorderAssistantContent(msgContent);
|
|
@@ -228,13 +226,10 @@ export function convertAnthropicToGoogle(anthropicRequest) {
|
|
|
228
226
|
// Convert tools to Google format
|
|
229
227
|
if (tools && tools.length > 0) {
|
|
230
228
|
const functionDeclarations = tools.map((tool, idx) => {
|
|
231
|
-
// Extract name from various possible locations
|
|
232
229
|
const name = tool.name || tool.function?.name || tool.custom?.name || `tool-${idx}`;
|
|
233
230
|
|
|
234
|
-
// Extract description from various possible locations
|
|
235
231
|
const description = tool.description || tool.function?.description || tool.custom?.description || '';
|
|
236
232
|
|
|
237
|
-
// Extract schema from various possible locations
|
|
238
233
|
const schema = tool.input_schema
|
|
239
234
|
|| tool.function?.input_schema
|
|
240
235
|
|| tool.function?.parameters
|
|
@@ -15,7 +15,6 @@ import { cacheSignature, cacheThinkingSignature } from './signature-cache.js';
|
|
|
15
15
|
* @returns {Object} Anthropic format response
|
|
16
16
|
*/
|
|
17
17
|
export function convertGoogleToAnthropic(googleResponse, model) {
|
|
18
|
-
// Handle the response wrapper
|
|
19
18
|
const response = googleResponse.response || googleResponse;
|
|
20
19
|
|
|
21
20
|
const candidates = response.candidates || [];
|
|
@@ -29,7 +28,6 @@ export function convertGoogleToAnthropic(googleResponse, model) {
|
|
|
29
28
|
|
|
30
29
|
for (const part of parts) {
|
|
31
30
|
if (part.text !== undefined) {
|
|
32
|
-
// Handle thinking blocks
|
|
33
31
|
if (part.thought === true) {
|
|
34
32
|
const signature = part.thoughtSignature || '';
|
|
35
33
|
|
|
@@ -72,7 +70,6 @@ export function convertGoogleToAnthropic(googleResponse, model) {
|
|
|
72
70
|
anthropicContent.push(toolUseBlock);
|
|
73
71
|
hasToolCalls = true;
|
|
74
72
|
} else if (part.inlineData) {
|
|
75
|
-
// Handle image content from Google format
|
|
76
73
|
anthropicContent.push({
|
|
77
74
|
type: 'image',
|
|
78
75
|
source: {
|
|
@@ -163,7 +163,7 @@ function mergeAllOf(schema) {
|
|
|
163
163
|
|
|
164
164
|
// Merge properties (allOf properties override parent for same keys)
|
|
165
165
|
if (Object.keys(mergedProperties).length > 0) {
|
|
166
|
-
result.properties = { ...mergedProperties, ...
|
|
166
|
+
result.properties = { ...mergedProperties, ...result.properties };
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// Merge required
|
|
@@ -289,10 +289,6 @@ function flattenAnyOfOneOf(schema) {
|
|
|
289
289
|
return result;
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
// ============================================================================
|
|
293
|
-
// Enhanced Schema Hints (for preserving semantic information)
|
|
294
|
-
// ============================================================================
|
|
295
|
-
|
|
296
292
|
/**
|
|
297
293
|
* Add hints for enum values (if ≤10 values).
|
|
298
294
|
* This preserves enum information in the description since Gemini
|
|
@@ -611,7 +607,6 @@ export function cleanSchema(schema) {
|
|
|
611
607
|
if (!schema || typeof schema !== 'object') return schema;
|
|
612
608
|
if (Array.isArray(schema)) return schema.map(cleanSchema);
|
|
613
609
|
|
|
614
|
-
// Phase 1: Convert $refs to hints
|
|
615
610
|
let result = convertRefsToHints(schema);
|
|
616
611
|
|
|
617
612
|
// Phase 1b: Add enum hints (preserves enum info in description)
|
|
@@ -632,7 +627,6 @@ export function cleanSchema(schema) {
|
|
|
632
627
|
// Phase 2c: Flatten type arrays and update required for nullable
|
|
633
628
|
result = flattenTypeArrays(result);
|
|
634
629
|
|
|
635
|
-
// Phase 3: Remove unsupported keywords
|
|
636
630
|
const unsupported = [
|
|
637
631
|
'additionalProperties', 'default', '$schema', '$defs',
|
|
638
632
|
'definitions', '$ref', '$id', '$comment', 'title',
|
|
@@ -652,7 +646,6 @@ export function cleanSchema(schema) {
|
|
|
652
646
|
}
|
|
653
647
|
}
|
|
654
648
|
|
|
655
|
-
// Phase 4: Final cleanup - recursively clean nested schemas and validate required
|
|
656
649
|
if (result.properties && typeof result.properties === 'object') {
|
|
657
650
|
const newProps = {};
|
|
658
651
|
for (const [key, value] of Object.entries(result.properties)) {
|
|
@@ -678,21 +671,11 @@ export function cleanSchema(schema) {
|
|
|
678
671
|
}
|
|
679
672
|
}
|
|
680
673
|
|
|
681
|
-
// Phase 5: Convert type to Google's uppercase format (STRING, OBJECT, ARRAY, etc.)
|
|
682
|
-
// Only convert at current level - nested types already converted by recursive cleanSchema calls
|
|
683
674
|
if (result.type && typeof result.type === 'string') {
|
|
684
675
|
result.type = toGoogleType(result.type);
|
|
685
676
|
}
|
|
686
677
|
|
|
687
|
-
//
|
|
688
|
-
// declare a single object `items` schema. Plain JSON Schema allows this
|
|
689
|
-
// to be omitted (`{ type: 'array' }` = "array of anything"), boolean
|
|
690
|
-
// (`items: true` = "any"), or tuple-form (`items: [...]`) - none of
|
|
691
|
-
// which the Cloud Code API accepts; it rejects the first two with
|
|
692
|
-
// "items: missing field". This runs after the items/properties
|
|
693
|
-
// recursion above (and after Phase 5's type conversion), so it applies
|
|
694
|
-
// at every nesting depth - fixing issue #368 (nested array schemas like
|
|
695
|
-
// `where.items.items` losing their innermost `items`).
|
|
678
|
+
// Cloud Code rejects ARRAY schemas without a single object `items` ("items: missing field"), issue #368.
|
|
696
679
|
if (result.type === 'ARRAY') {
|
|
697
680
|
if (!result.items || typeof result.items !== 'object') {
|
|
698
681
|
// Missing entirely, or a boolean schema like `items: true`.
|
|
@@ -713,9 +696,7 @@ export function cleanSchema(schema) {
|
|
|
713
696
|
}
|
|
714
697
|
}
|
|
715
698
|
|
|
716
|
-
//
|
|
717
|
-
// These can survive on the current node after anyOf/oneOf flattening
|
|
718
|
-
// picked a non-object option while the parent still carried them.
|
|
699
|
+
// Google rejects properties/required on non-OBJECT nodes, which anyOf/oneOf flattening can leave behind.
|
|
719
700
|
if (result.type !== 'OBJECT') {
|
|
720
701
|
delete result.properties;
|
|
721
702
|
delete result.required;
|
|
@@ -7,10 +7,6 @@ import { MIN_SIGNATURE_LENGTH } from '../constants.js';
|
|
|
7
7
|
import { getCachedSignature, getCachedSignatureFamily } from './signature-cache.js';
|
|
8
8
|
import { logger } from '../utils/logger.js';
|
|
9
9
|
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// Gemini Thinking Budget Limits (Issue #289)
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
10
|
// Max thinking budget per Gemini model version
|
|
15
11
|
// Gemini 2.5 Flash: max 24,576 (API error: "supported values are integers from 1 to 24576")
|
|
16
12
|
const GEMINI_THINKING_BUDGET_LIMITS = {
|
|
@@ -57,10 +53,6 @@ export function clampGeminiThinkingBudget(modelName, budget) {
|
|
|
57
53
|
return requestedBudget;
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
// ============================================================================
|
|
61
|
-
// Cache Control Cleaning (Issue #189)
|
|
62
|
-
// ============================================================================
|
|
63
|
-
|
|
64
56
|
/**
|
|
65
57
|
* Remove cache_control fields from all content blocks in messages.
|
|
66
58
|
* This is a critical fix for Issue #189 where Claude Code CLI sends cache_control
|
|
@@ -94,7 +86,7 @@ export function cleanCacheControl(messages) {
|
|
|
94
86
|
if (block.cache_control === undefined) return block;
|
|
95
87
|
|
|
96
88
|
// Create a shallow copy without cache_control
|
|
97
|
-
const { cache_control, ...cleanBlock } = block;
|
|
89
|
+
const { cache_control: _cacheControl, ...cleanBlock } = block;
|
|
98
90
|
removedCount++;
|
|
99
91
|
|
|
100
92
|
return cleanBlock;
|
|
@@ -431,10 +423,6 @@ export function reorderAssistantContent(content) {
|
|
|
431
423
|
return reordered;
|
|
432
424
|
}
|
|
433
425
|
|
|
434
|
-
// ============================================================================
|
|
435
|
-
// Thinking Recovery Functions
|
|
436
|
-
// ============================================================================
|
|
437
|
-
|
|
438
426
|
/**
|
|
439
427
|
* Check if a message has any VALID (signed) thinking blocks.
|
|
440
428
|
* Only counts thinking blocks that have valid signatures, not unsigned ones
|
package/src/index.js
CHANGED
|
@@ -10,14 +10,13 @@ import app, { accountManager } from './server.js';
|
|
|
10
10
|
import { DEFAULT_PORT } from './constants.js';
|
|
11
11
|
import { logger } from './utils/logger.js';
|
|
12
12
|
import { config } from './config.js';
|
|
13
|
-
import {
|
|
13
|
+
import { STRATEGY_NAMES } from './account-manager/strategies/index.js';
|
|
14
14
|
import { getPackageVersion } from './utils/helpers.js';
|
|
15
15
|
import path from 'path';
|
|
16
16
|
import os from 'os';
|
|
17
17
|
|
|
18
18
|
const packageVersion = getPackageVersion();
|
|
19
19
|
|
|
20
|
-
// Parse command line arguments
|
|
21
20
|
const args = process.argv.slice(2);
|
|
22
21
|
const isDebug = args.includes('--debug') || args.includes('--dev-mode') || process.env.DEBUG === 'true' || process.env.DEV_MODE === 'true';
|
|
23
22
|
const isFallbackEnabled = args.includes('--fallback') || process.env.FALLBACK === 'true';
|
|
@@ -31,13 +30,11 @@ for (let i = 0; i < args.length; i++) {
|
|
|
31
30
|
strategyOverride = args[i + 1];
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
|
-
// Validate strategy
|
|
35
33
|
if (strategyOverride && !STRATEGY_NAMES.includes(strategyOverride.toLowerCase())) {
|
|
36
34
|
logger.warn(`[Startup] Invalid strategy "${strategyOverride}". Valid options: ${STRATEGY_NAMES.join(', ')}. Using default.`);
|
|
37
35
|
strategyOverride = null;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
// Initialize logger and devMode
|
|
41
38
|
logger.setDebug(isDebug);
|
|
42
39
|
|
|
43
40
|
if (isDebug) {
|
|
@@ -65,7 +62,6 @@ const HOME_DIR = os.homedir();
|
|
|
65
62
|
const CONFIG_DIR = path.join(HOME_DIR, '.antigravity-claude-proxy');
|
|
66
63
|
|
|
67
64
|
const server = app.listen(PORT, HOST, () => {
|
|
68
|
-
// Get actual bound address
|
|
69
65
|
const address = server.address();
|
|
70
66
|
const boundHost = typeof address === 'string' ? address : address.address;
|
|
71
67
|
const boundPort = typeof address === 'string' ? null : address.port;
|
|
@@ -78,7 +74,6 @@ const server = app.listen(PORT, HOST, () => {
|
|
|
78
74
|
const align = (text) => text + ' '.repeat(Math.max(0, 60 - text.length));
|
|
79
75
|
const align4 = (text) => text + ' '.repeat(Math.max(0, 58 - text.length));
|
|
80
76
|
|
|
81
|
-
// Build Control section dynamically
|
|
82
77
|
const strategyOptions = `(${STRATEGY_NAMES.join('/')})`;
|
|
83
78
|
const strategyLine2 = ' ' + strategyOptions;
|
|
84
79
|
let controlSection = '║ Control: ║\n';
|
package/src/server.js
CHANGED
|
@@ -46,7 +46,6 @@ export const accountManager = new AccountManager();
|
|
|
46
46
|
|
|
47
47
|
// Track initialization status
|
|
48
48
|
let isInitialized = false;
|
|
49
|
-
let initError = null;
|
|
50
49
|
let initPromise = null;
|
|
51
50
|
|
|
52
51
|
/**
|
|
@@ -65,7 +64,6 @@ async function ensureInitialized() {
|
|
|
65
64
|
const status = accountManager.getStatus();
|
|
66
65
|
logger.success(`[Server] Account pool initialized: ${status.summary}`);
|
|
67
66
|
} catch (error) {
|
|
68
|
-
initError = error;
|
|
69
67
|
initPromise = null; // Allow retry on failure
|
|
70
68
|
logger.error('[Server] Failed to initialize account manager:', error.message);
|
|
71
69
|
throw error;
|
|
@@ -78,6 +76,12 @@ async function ensureInitialized() {
|
|
|
78
76
|
// Middleware
|
|
79
77
|
app.use(cors());
|
|
80
78
|
app.use(express.json({ limit: REQUEST_BODY_LIMIT }));
|
|
79
|
+
// Express 5 leaves req.body undefined when no body was parsed (Express 4 set {}).
|
|
80
|
+
// Keep the old behavior so handlers can destructure req.body safely.
|
|
81
|
+
app.use((req, res, next) => {
|
|
82
|
+
if (req.body === undefined) req.body = {};
|
|
83
|
+
next();
|
|
84
|
+
});
|
|
81
85
|
|
|
82
86
|
// API Key authentication middleware for /v1/* endpoints
|
|
83
87
|
app.use('/v1', (req, res, next) => {
|
|
@@ -110,7 +114,6 @@ app.use('/v1', (req, res, next) => {
|
|
|
110
114
|
next();
|
|
111
115
|
});
|
|
112
116
|
|
|
113
|
-
// Setup usage statistics middleware
|
|
114
117
|
usageStats.setupMiddleware(app);
|
|
115
118
|
|
|
116
119
|
/**
|
|
@@ -119,11 +122,9 @@ usageStats.setupMiddleware(app);
|
|
|
119
122
|
* Using app.use instead of app.post for earlier middleware interception
|
|
120
123
|
*/
|
|
121
124
|
app.use((req, res, next) => {
|
|
122
|
-
// Handle Claude Code event logging requests silently
|
|
123
125
|
if (req.method === 'POST' && req.path === '/api/event_logging/batch') {
|
|
124
126
|
return res.status(200).json({ status: 'ok' });
|
|
125
127
|
}
|
|
126
|
-
// Handle Claude Code root POST requests silently
|
|
127
128
|
if (req.method === 'POST' && req.path === '/') {
|
|
128
129
|
return res.status(200).json({ status: 'ok' });
|
|
129
130
|
}
|
|
@@ -172,7 +173,6 @@ function parseError(error) {
|
|
|
172
173
|
} else if (error.message.includes('PERMISSION_DENIED')) {
|
|
173
174
|
errorType = 'permission_error';
|
|
174
175
|
statusCode = 403;
|
|
175
|
-
errorMessage = errorMessage;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
return { errorType, statusCode, errorMessage };
|
|
@@ -182,7 +182,6 @@ function parseError(error) {
|
|
|
182
182
|
app.use((req, res, next) => {
|
|
183
183
|
const start = Date.now();
|
|
184
184
|
|
|
185
|
-
// Log response on finish
|
|
186
185
|
res.on('finish', () => {
|
|
187
186
|
const duration = Date.now() - start;
|
|
188
187
|
const status = res.statusCode;
|
|
@@ -235,14 +234,12 @@ app.get('/health', async (req, res) => {
|
|
|
235
234
|
await ensureInitialized();
|
|
236
235
|
const start = Date.now();
|
|
237
236
|
|
|
238
|
-
// Get high-level status first
|
|
239
237
|
const status = accountManager.getStatus();
|
|
240
238
|
const allAccounts = accountManager.getAllAccounts();
|
|
241
239
|
|
|
242
240
|
// Fetch quotas for each account in parallel to get detailed model info
|
|
243
241
|
const accountDetails = await Promise.allSettled(
|
|
244
242
|
allAccounts.map(async (account) => {
|
|
245
|
-
// Check model-specific rate limits
|
|
246
243
|
const activeModelLimits = Object.entries(account.modelRateLimits || {})
|
|
247
244
|
.filter(([_, limit]) => limit.isRateLimited && limit.resetTime > Date.now());
|
|
248
245
|
const isRateLimited = activeModelLimits.length > 0;
|
|
@@ -300,7 +297,6 @@ app.get('/health', async (req, res) => {
|
|
|
300
297
|
})
|
|
301
298
|
);
|
|
302
299
|
|
|
303
|
-
// Process results
|
|
304
300
|
const detailedAccounts = accountDetails.map((result, index) => {
|
|
305
301
|
if (result.status === 'fulfilled') {
|
|
306
302
|
return result.value;
|
|
@@ -351,7 +347,6 @@ app.get('/account-limits', async (req, res) => {
|
|
|
351
347
|
const format = req.query.format || 'json';
|
|
352
348
|
const includeHistory = req.query.includeHistory === 'true';
|
|
353
349
|
|
|
354
|
-
// Fetch quotas for each account in parallel
|
|
355
350
|
const results = await Promise.allSettled(
|
|
356
351
|
allAccounts.map(async (account) => {
|
|
357
352
|
// Skip invalid accounts
|
|
@@ -367,13 +362,11 @@ app.get('/account-limits', async (req, res) => {
|
|
|
367
362
|
try {
|
|
368
363
|
const token = await accountManager.getTokenForAccount(account);
|
|
369
364
|
|
|
370
|
-
// Fetch subscription tier first to get project ID
|
|
371
365
|
const subscription = await getSubscriptionTier(token);
|
|
372
366
|
|
|
373
367
|
// Then fetch quotas with project ID for accurate quota info
|
|
374
368
|
const quotas = await getModelQuotas(token, subscription.projectId);
|
|
375
369
|
|
|
376
|
-
// Update account object with fresh data
|
|
377
370
|
account.subscription = {
|
|
378
371
|
tier: subscription.tier,
|
|
379
372
|
projectId: subscription.projectId,
|
|
@@ -418,7 +411,6 @@ app.get('/account-limits', async (req, res) => {
|
|
|
418
411
|
})
|
|
419
412
|
);
|
|
420
413
|
|
|
421
|
-
// Process results
|
|
422
414
|
const accountLimits = results.map((result, index) => {
|
|
423
415
|
if (result.status === 'fulfilled') {
|
|
424
416
|
return result.value;
|
|
@@ -442,16 +434,13 @@ app.get('/account-limits', async (req, res) => {
|
|
|
442
434
|
|
|
443
435
|
const sortedModels = Array.from(allModelIds).sort();
|
|
444
436
|
|
|
445
|
-
// Return ASCII table format
|
|
446
437
|
if (format === 'table') {
|
|
447
438
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
448
439
|
|
|
449
|
-
// Build table
|
|
450
440
|
const lines = [];
|
|
451
441
|
const timestamp = new Date().toLocaleString();
|
|
452
442
|
lines.push(`Account Limits (${timestamp})`);
|
|
453
443
|
|
|
454
|
-
// Get account status info
|
|
455
444
|
const status = accountManager.getStatus();
|
|
456
445
|
lines.push(`Accounts: ${status.total} total, ${status.available} available, ${status.rateLimited} rate-limited, ${status.invalid} invalid`);
|
|
457
446
|
lines.push('');
|
|
@@ -470,7 +459,6 @@ app.get('/account-limits', async (req, res) => {
|
|
|
470
459
|
const shortEmail = acc.email.split('@')[0].slice(0, 22);
|
|
471
460
|
const lastUsed = acc.lastUsed ? new Date(acc.lastUsed).toLocaleString() : 'never';
|
|
472
461
|
|
|
473
|
-
// Get status and error from accountLimits
|
|
474
462
|
const accLimit = accountLimits.find(a => a.email === acc.email);
|
|
475
463
|
let accStatus;
|
|
476
464
|
if (acc.isInvalid) {
|
|
@@ -492,7 +480,6 @@ app.get('/account-limits', async (req, res) => {
|
|
|
492
480
|
}
|
|
493
481
|
}
|
|
494
482
|
|
|
495
|
-
// Get reset time from quota API
|
|
496
483
|
const claudeModel = sortedModels.find(m => m.includes('claude'));
|
|
497
484
|
const quota = claudeModel && accLimit?.models?.[claudeModel];
|
|
498
485
|
const resetTime = quota?.resetTime
|
|
@@ -558,13 +545,11 @@ app.get('/account-limits', async (req, res) => {
|
|
|
558
545
|
return res.send(lines.join('\n'));
|
|
559
546
|
}
|
|
560
547
|
|
|
561
|
-
// Get account metadata from AccountManager
|
|
562
548
|
const accountStatus = accountManager.getStatus();
|
|
563
549
|
const accountMetadataMap = new Map(
|
|
564
550
|
accountStatus.accounts.map(a => [a.email, a])
|
|
565
551
|
);
|
|
566
552
|
|
|
567
|
-
// Build response data
|
|
568
553
|
const responseData = {
|
|
569
554
|
timestamp: new Date().toLocaleString(),
|
|
570
555
|
totalAccounts: allAccounts.length,
|
|
@@ -699,7 +684,6 @@ app.post('/v1/messages/count_tokens', (req, res) => {
|
|
|
699
684
|
* Main messages endpoint - Anthropic Messages API compatible
|
|
700
685
|
*/
|
|
701
686
|
|
|
702
|
-
|
|
703
687
|
/**
|
|
704
688
|
* Anthropic-compatible Messages API
|
|
705
689
|
* POST /v1/messages
|
|
@@ -738,7 +722,6 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
738
722
|
|
|
739
723
|
const modelId = requestedModel;
|
|
740
724
|
|
|
741
|
-
// Validate model ID before processing
|
|
742
725
|
const { account: validationAccount } = accountManager.selectAccount();
|
|
743
726
|
if (validationAccount) {
|
|
744
727
|
const token = await accountManager.getTokenForAccount(validationAccount);
|
|
@@ -757,7 +740,6 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
757
740
|
accountManager.resetAllRateLimits();
|
|
758
741
|
}
|
|
759
742
|
|
|
760
|
-
// Validate required fields
|
|
761
743
|
if (!messages || !Array.isArray(messages)) {
|
|
762
744
|
return res.status(400).json({
|
|
763
745
|
type: 'error',
|
|
@@ -773,7 +755,6 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
773
755
|
return res.json({});
|
|
774
756
|
}
|
|
775
757
|
|
|
776
|
-
// Build the request object
|
|
777
758
|
const request = {
|
|
778
759
|
model: modelId,
|
|
779
760
|
messages,
|
|
@@ -807,7 +788,6 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
807
788
|
// to ensure we don't send a 200 OK if the upstream fails immediately (e.g. 429/503).
|
|
808
789
|
|
|
809
790
|
try {
|
|
810
|
-
// Initialize the generator
|
|
811
791
|
const generator = sendMessageStream(request, accountManager, FALLBACK_ENABLED);
|
|
812
792
|
|
|
813
793
|
// BUFFERING STRATEGY:
|
|
@@ -866,7 +846,6 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
866
846
|
}
|
|
867
847
|
|
|
868
848
|
} else {
|
|
869
|
-
// Handle non-streaming response
|
|
870
849
|
const response = await sendMessage(request, accountManager, FALLBACK_ENABLED);
|
|
871
850
|
usageStats.track(response?.model || modelId);
|
|
872
851
|
res.json(response);
|
|
@@ -886,6 +865,7 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
886
865
|
await forceRefresh();
|
|
887
866
|
errorMessage = 'Token was expired and has been refreshed. Please retry your request.';
|
|
888
867
|
} catch (refreshError) {
|
|
868
|
+
logger.warn(`[API] Token refresh failed: ${refreshError.message}`);
|
|
889
869
|
errorMessage = 'Could not refresh token. Make sure Antigravity is running.';
|
|
890
870
|
}
|
|
891
871
|
}
|
|
@@ -917,8 +897,7 @@ app.post('/v1/messages', async (req, res) => {
|
|
|
917
897
|
*/
|
|
918
898
|
usageStats.setupRoutes(app);
|
|
919
899
|
|
|
920
|
-
app.use(
|
|
921
|
-
// Log 404s (use originalUrl since wildcard strips req.path)
|
|
900
|
+
app.use((req, res) => {
|
|
922
901
|
if (logger.isDebugEnabled) {
|
|
923
902
|
logger.debug(`[API] 404 Not Found: ${req.method} ${req.originalUrl}`);
|
|
924
903
|
}
|
|
@@ -83,7 +83,7 @@ export async function updateClaudeConfig(updates) {
|
|
|
83
83
|
try {
|
|
84
84
|
await fs.mkdir(configDir, { recursive: true });
|
|
85
85
|
} catch (error) {
|
|
86
|
-
|
|
86
|
+
logger.debug(`[ClaudeConfig] Could not create ${configDir}: ${error.message}`);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// 4. Write back to file
|
|
@@ -112,7 +112,7 @@ export async function replaceClaudeConfig(config) {
|
|
|
112
112
|
try {
|
|
113
113
|
await fs.mkdir(configDir, { recursive: true });
|
|
114
114
|
} catch (error) {
|
|
115
|
-
|
|
115
|
+
logger.debug(`[ClaudeConfig] Could not create ${configDir}: ${error.message}`);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
// 2. Write config directly (no merge)
|
|
@@ -153,10 +153,6 @@ function isObject(item) {
|
|
|
153
153
|
return (item && typeof item === 'object' && !Array.isArray(item));
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
// ==========================================
|
|
157
|
-
// Claude CLI Presets
|
|
158
|
-
// ==========================================
|
|
159
|
-
|
|
160
156
|
/**
|
|
161
157
|
* Get the path to the presets file
|
|
162
158
|
* @returns {string} Absolute path to claude-presets.json
|
|
@@ -64,14 +64,12 @@ export function rebuildModule(packagePath) {
|
|
|
64
64
|
try {
|
|
65
65
|
logger.info(`[NativeModule] Rebuilding native module at: ${packagePath}`);
|
|
66
66
|
|
|
67
|
-
// Run npm rebuild in the package directory
|
|
68
67
|
const output = execSync('npm rebuild', {
|
|
69
68
|
cwd: packagePath,
|
|
70
69
|
stdio: 'pipe', // Capture output instead of printing
|
|
71
70
|
timeout: 120000 // 2 minute timeout
|
|
72
71
|
});
|
|
73
72
|
|
|
74
|
-
// Log rebuild output for debugging
|
|
75
73
|
const outputStr = output?.toString().trim();
|
|
76
74
|
if (outputStr) {
|
|
77
75
|
logger.debug(`[NativeModule] Rebuild output:\n${outputStr}`);
|
|
@@ -140,7 +138,6 @@ export function clearRequireCache(modulePath, cache, visited = new Set()) {
|
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
// Remove from parent's children array
|
|
144
141
|
if (mod.parent && mod.parent.children) {
|
|
145
142
|
const idx = mod.parent.children.indexOf(mod);
|
|
146
143
|
if (idx !== -1) {
|
|
@@ -148,7 +145,6 @@ export function clearRequireCache(modulePath, cache, visited = new Set()) {
|
|
|
148
145
|
}
|
|
149
146
|
}
|
|
150
147
|
|
|
151
|
-
// Delete from cache
|
|
152
148
|
delete cache[modulePath];
|
|
153
149
|
}
|
|
154
150
|
|
package/src/utils/proxy.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* HTTP Proxy Support
|
|
3
|
-
*
|
|
4
|
-
* Configures global fetch to use HTTP proxy from environment variables.
|
|
5
|
-
* Supports: http_proxy, HTTP_PROXY, https_proxy, HTTPS_PROXY
|
|
6
|
-
*
|
|
7
|
-
* This module should be imported at the very beginning of the application
|
|
8
|
-
* entry point (src/index.js) before any fetch calls are made.
|
|
9
|
-
*/
|
|
10
|
-
|
|
1
|
+
// Routes global fetch through http(s)_proxy env vars. Import before any fetch call (see src/index.js).
|
|
11
2
|
import { ProxyAgent, setGlobalDispatcher } from 'undici';
|
|
12
3
|
import { logger } from './logger.js';
|
|
13
4
|
|
|
@@ -27,7 +27,7 @@ import { config } from '../config.js';
|
|
|
27
27
|
|
|
28
28
|
// Fallback for User-Agent version (ideVersion in product.json), used only when
|
|
29
29
|
// no installed Antigravity version is detected
|
|
30
|
-
const FALLBACK_USER_AGENT_VERSION = process.env.FALLBACK_ANTIGRAVITY_VERSION || '2.
|
|
30
|
+
const FALLBACK_USER_AGENT_VERSION = process.env.FALLBACK_ANTIGRAVITY_VERSION || '2.32.0';
|
|
31
31
|
|
|
32
32
|
// Detected versions must be above this to be used (older installs are ignored)
|
|
33
33
|
const MIN_DETECTED_USER_AGENT_VERSION = '2.0.3';
|
|
@@ -104,7 +104,7 @@ function getProductJson() {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
} catch (e) {
|
|
107
|
-
|
|
107
|
+
logDetectionFailure(`Reading ${p}`, e);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -112,6 +112,15 @@ function getProductJson() {
|
|
|
112
112
|
return null;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Debug-log a failed detection step (lazy import to avoid circular deps).
|
|
117
|
+
*/
|
|
118
|
+
function logDetectionFailure(step, err) {
|
|
119
|
+
import('./logger.js').then(({ logger }) => {
|
|
120
|
+
logger.debug(`[VersionDetector] ${step} failed: ${err?.message ?? err}`);
|
|
121
|
+
}).catch(() => {});
|
|
122
|
+
}
|
|
123
|
+
|
|
115
124
|
/**
|
|
116
125
|
* Log version detection results once at startup (lazy import to avoid circular deps).
|
|
117
126
|
*/
|
|
@@ -188,7 +197,7 @@ function getUserAgentVersionConfig() {
|
|
|
188
197
|
localVersion = getVersionWindows();
|
|
189
198
|
}
|
|
190
199
|
} catch (error) {
|
|
191
|
-
|
|
200
|
+
logDetectionFailure('OS version detection', error);
|
|
192
201
|
}
|
|
193
202
|
|
|
194
203
|
return resolveUserAgentVersion({ localVersion });
|
|
@@ -246,7 +255,7 @@ function getVersionMacos() {
|
|
|
246
255
|
return version;
|
|
247
256
|
}
|
|
248
257
|
} catch (e) {
|
|
249
|
-
|
|
258
|
+
logDetectionFailure('plutil', e);
|
|
250
259
|
}
|
|
251
260
|
return null;
|
|
252
261
|
}
|
|
@@ -273,7 +282,7 @@ function getVersionWindows() {
|
|
|
273
282
|
}
|
|
274
283
|
}
|
|
275
284
|
} catch (e) {
|
|
276
|
-
|
|
285
|
+
logDetectionFailure('PowerShell version lookup', e);
|
|
277
286
|
}
|
|
278
287
|
return null;
|
|
279
288
|
}
|