antigravity-claude-proxy 1.1.3 → 1.1.5
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 +33 -0
- package/package.json +2 -1
- package/src/account-manager.js +3 -19
- package/src/db/database.js +93 -0
- package/src/format/content-converter.js +19 -1
- package/src/server.js +13 -0
- package/src/token-extractor.js +3 -33
package/README.md
CHANGED
|
@@ -166,6 +166,37 @@ Or to use Gemini models:
|
|
|
166
166
|
}
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
+
### Load Environment Variables
|
|
170
|
+
|
|
171
|
+
Add the proxy settings to your shell profile:
|
|
172
|
+
|
|
173
|
+
**macOS / Linux:**
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
echo 'export ANTHROPIC_BASE_URL="http://localhost:8080"' >> ~/.zshrc
|
|
177
|
+
echo 'export ANTHROPIC_API_KEY="test"' >> ~/.zshrc
|
|
178
|
+
source ~/.zshrc
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
> For Bash users, replace `~/.zshrc` with `~/.bashrc`
|
|
182
|
+
|
|
183
|
+
**Windows (PowerShell):**
|
|
184
|
+
|
|
185
|
+
```powershell
|
|
186
|
+
Add-Content $PROFILE "`n`$env:ANTHROPIC_BASE_URL = 'http://localhost:8080'"
|
|
187
|
+
Add-Content $PROFILE "`$env:ANTHROPIC_API_KEY = 'test'"
|
|
188
|
+
. $PROFILE
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Windows (Command Prompt):**
|
|
192
|
+
|
|
193
|
+
```cmd
|
|
194
|
+
setx ANTHROPIC_BASE_URL "http://localhost:8080"
|
|
195
|
+
setx ANTHROPIC_API_KEY "test"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Restart your terminal for changes to take effect.
|
|
199
|
+
|
|
169
200
|
### Run Claude Code
|
|
170
201
|
|
|
171
202
|
```bash
|
|
@@ -176,6 +207,8 @@ antigravity-claude-proxy start
|
|
|
176
207
|
claude
|
|
177
208
|
```
|
|
178
209
|
|
|
210
|
+
> **Note:** If Claude Code asks you to select a login method, add `"hasCompletedOnboarding": true` to `~/.claude.json` (macOS/Linux) or `%USERPROFILE%\.claude.json` (Windows), then restart your terminal and try again.
|
|
211
|
+
|
|
179
212
|
---
|
|
180
213
|
|
|
181
214
|
## Available Models
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antigravity-claude-proxy",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Proxy server to use Antigravity's Claude models with Claude Code CLI",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"node": ">=18.0.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"better-sqlite3": "^12.5.0",
|
|
51
52
|
"cors": "^2.8.5",
|
|
52
53
|
"express": "^4.18.2"
|
|
53
54
|
}
|
package/src/account-manager.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { readFile, writeFile, mkdir, access } from 'fs/promises';
|
|
8
8
|
import { constants as fsConstants } from 'fs';
|
|
9
9
|
import { dirname } from 'path';
|
|
10
|
-
import { execSync } from 'child_process';
|
|
11
10
|
import {
|
|
12
11
|
ACCOUNT_CONFIG_PATH,
|
|
13
12
|
ANTIGRAVITY_DB_PATH,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
} from './constants.js';
|
|
21
20
|
import { refreshAccessToken } from './oauth.js';
|
|
22
21
|
import { formatDuration } from './utils/helpers.js';
|
|
22
|
+
import { getAuthStatus } from './db/database.js';
|
|
23
23
|
|
|
24
24
|
export class AccountManager {
|
|
25
25
|
#accounts = [];
|
|
@@ -92,7 +92,7 @@ export class AccountManager {
|
|
|
92
92
|
*/
|
|
93
93
|
async #loadDefaultAccount() {
|
|
94
94
|
try {
|
|
95
|
-
const authData =
|
|
95
|
+
const authData = getAuthStatus();
|
|
96
96
|
if (authData?.apiKey) {
|
|
97
97
|
this.#accounts = [{
|
|
98
98
|
email: authData.email || 'default@antigravity',
|
|
@@ -115,22 +115,6 @@ export class AccountManager {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
/**
|
|
119
|
-
* Extract token from Antigravity's SQLite database
|
|
120
|
-
*/
|
|
121
|
-
#extractTokenFromDB(dbPath = ANTIGRAVITY_DB_PATH) {
|
|
122
|
-
const result = execSync(
|
|
123
|
-
`sqlite3 "${dbPath}" "SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus';"`,
|
|
124
|
-
{ encoding: 'utf-8', timeout: 5000 }
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
if (!result || !result.trim()) {
|
|
128
|
-
throw new Error('No auth status found in database');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return JSON.parse(result.trim());
|
|
132
|
-
}
|
|
133
|
-
|
|
134
118
|
/**
|
|
135
119
|
* Get the number of accounts
|
|
136
120
|
* @returns {number} Number of configured accounts
|
|
@@ -453,7 +437,7 @@ export class AccountManager {
|
|
|
453
437
|
} else {
|
|
454
438
|
// Extract from database
|
|
455
439
|
const dbPath = account.dbPath || ANTIGRAVITY_DB_PATH;
|
|
456
|
-
const authData =
|
|
440
|
+
const authData = getAuthStatus(dbPath);
|
|
457
441
|
token = authData.apiKey;
|
|
458
442
|
}
|
|
459
443
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database Access Module
|
|
3
|
+
* Provides cross-platform database operations for Antigravity state.
|
|
4
|
+
*
|
|
5
|
+
* Uses better-sqlite3 for:
|
|
6
|
+
* - Windows compatibility (no CLI dependency)
|
|
7
|
+
* - Native performance
|
|
8
|
+
* - Synchronous API (simple error handling)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import Database from 'better-sqlite3';
|
|
12
|
+
import { ANTIGRAVITY_DB_PATH } from '../constants.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Query Antigravity database for authentication status
|
|
16
|
+
* @param {string} [dbPath] - Optional custom database path
|
|
17
|
+
* @returns {Object} Parsed auth data with apiKey, email, name, etc.
|
|
18
|
+
* @throws {Error} If database doesn't exist, query fails, or no auth status found
|
|
19
|
+
*/
|
|
20
|
+
export function getAuthStatus(dbPath = ANTIGRAVITY_DB_PATH) {
|
|
21
|
+
let db;
|
|
22
|
+
try {
|
|
23
|
+
// Open database in read-only mode
|
|
24
|
+
db = new Database(dbPath, {
|
|
25
|
+
readonly: true,
|
|
26
|
+
fileMustExist: true
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Prepare and execute query
|
|
30
|
+
const stmt = db.prepare(
|
|
31
|
+
"SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus'"
|
|
32
|
+
);
|
|
33
|
+
const row = stmt.get();
|
|
34
|
+
|
|
35
|
+
if (!row || !row.value) {
|
|
36
|
+
throw new Error('No auth status found in database');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Parse JSON value
|
|
40
|
+
const authData = JSON.parse(row.value);
|
|
41
|
+
|
|
42
|
+
if (!authData.apiKey) {
|
|
43
|
+
throw new Error('Auth data missing apiKey field');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return authData;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// Enhance error messages for common issues
|
|
49
|
+
if (error.code === 'SQLITE_CANTOPEN') {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Database not found at ${dbPath}. ` +
|
|
52
|
+
'Make sure Antigravity is installed and you are logged in.'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
// Re-throw with context if not already our error
|
|
56
|
+
if (error.message.includes('No auth status') || error.message.includes('missing apiKey')) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Failed to read Antigravity database: ${error.message}`);
|
|
60
|
+
} finally {
|
|
61
|
+
// Always close database connection
|
|
62
|
+
if (db) {
|
|
63
|
+
db.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if database exists and is accessible
|
|
70
|
+
* @param {string} [dbPath] - Optional custom database path
|
|
71
|
+
* @returns {boolean} True if database exists and can be opened
|
|
72
|
+
*/
|
|
73
|
+
export function isDatabaseAccessible(dbPath = ANTIGRAVITY_DB_PATH) {
|
|
74
|
+
let db;
|
|
75
|
+
try {
|
|
76
|
+
db = new Database(dbPath, {
|
|
77
|
+
readonly: true,
|
|
78
|
+
fileMustExist: true
|
|
79
|
+
});
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
} finally {
|
|
84
|
+
if (db) {
|
|
85
|
+
db.close();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default {
|
|
91
|
+
getAuthStatus,
|
|
92
|
+
isDatabaseAccessible
|
|
93
|
+
};
|
|
@@ -112,14 +112,29 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
|
|
|
112
112
|
} else if (block.type === 'tool_result') {
|
|
113
113
|
// Convert tool_result to functionResponse (Google format)
|
|
114
114
|
let responseContent = block.content;
|
|
115
|
+
let imageParts = [];
|
|
116
|
+
|
|
115
117
|
if (typeof responseContent === 'string') {
|
|
116
118
|
responseContent = { result: responseContent };
|
|
117
119
|
} else if (Array.isArray(responseContent)) {
|
|
120
|
+
// Extract images from tool results first (e.g., from Read tool reading image files)
|
|
121
|
+
for (const item of responseContent) {
|
|
122
|
+
if (item.type === 'image' && item.source?.type === 'base64') {
|
|
123
|
+
imageParts.push({
|
|
124
|
+
inlineData: {
|
|
125
|
+
mimeType: item.source.media_type,
|
|
126
|
+
data: item.source.data
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Extract text content
|
|
118
133
|
const texts = responseContent
|
|
119
134
|
.filter(c => c.type === 'text')
|
|
120
135
|
.map(c => c.text)
|
|
121
136
|
.join('\n');
|
|
122
|
-
responseContent = { result: texts };
|
|
137
|
+
responseContent = { result: texts || (imageParts.length > 0 ? 'Image attached' : '') };
|
|
123
138
|
}
|
|
124
139
|
|
|
125
140
|
const functionResponse = {
|
|
@@ -133,6 +148,9 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
|
|
|
133
148
|
}
|
|
134
149
|
|
|
135
150
|
parts.push({ functionResponse });
|
|
151
|
+
|
|
152
|
+
// Add any images from the tool result as separate parts
|
|
153
|
+
parts.push(...imageParts);
|
|
136
154
|
} else if (block.type === 'thinking') {
|
|
137
155
|
// Handle thinking blocks - only those with valid signatures
|
|
138
156
|
if (block.signature && block.signature.length >= MIN_SIGNATURE_LENGTH) {
|
package/src/server.js
CHANGED
|
@@ -380,6 +380,19 @@ app.get('/v1/models', async (req, res) => {
|
|
|
380
380
|
}
|
|
381
381
|
});
|
|
382
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Count tokens endpoint (not supported)
|
|
385
|
+
*/
|
|
386
|
+
app.post('/v1/messages/count_tokens', (req, res) => {
|
|
387
|
+
res.status(501).json({
|
|
388
|
+
type: 'error',
|
|
389
|
+
error: {
|
|
390
|
+
type: 'not_implemented',
|
|
391
|
+
message: 'Token counting is not implemented. Use /v1/messages with max_tokens or configure your client to skip token counting.'
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
|
|
383
396
|
/**
|
|
384
397
|
* Main messages endpoint - Anthropic Messages API compatible
|
|
385
398
|
*/
|
package/src/token-extractor.js
CHANGED
|
@@ -6,46 +6,16 @@
|
|
|
6
6
|
* so this approach doesn't require any manual intervention.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
9
|
import {
|
|
11
10
|
TOKEN_REFRESH_INTERVAL_MS,
|
|
12
|
-
ANTIGRAVITY_AUTH_PORT
|
|
13
|
-
ANTIGRAVITY_DB_PATH
|
|
11
|
+
ANTIGRAVITY_AUTH_PORT
|
|
14
12
|
} from './constants.js';
|
|
13
|
+
import { getAuthStatus } from './db/database.js';
|
|
15
14
|
|
|
16
15
|
// Cache for the extracted token
|
|
17
16
|
let cachedToken = null;
|
|
18
17
|
let tokenExtractedAt = null;
|
|
19
18
|
|
|
20
|
-
/**
|
|
21
|
-
* Extract token from Antigravity's SQLite database
|
|
22
|
-
* This is the preferred method as the DB is auto-updated
|
|
23
|
-
*/
|
|
24
|
-
function extractTokenFromDB() {
|
|
25
|
-
try {
|
|
26
|
-
const result = execSync(
|
|
27
|
-
`sqlite3 "${ANTIGRAVITY_DB_PATH}" "SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus';"`,
|
|
28
|
-
{ encoding: 'utf-8', timeout: 5000 }
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
if (!result || !result.trim()) {
|
|
32
|
-
throw new Error('No auth status found in database');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const authData = JSON.parse(result.trim());
|
|
36
|
-
return {
|
|
37
|
-
apiKey: authData.apiKey,
|
|
38
|
-
name: authData.name,
|
|
39
|
-
email: authData.email,
|
|
40
|
-
// Include other fields we might need
|
|
41
|
-
...authData
|
|
42
|
-
};
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.error('[Token] Database extraction failed:', error.message);
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
19
|
/**
|
|
50
20
|
* Extract the chat params from Antigravity's HTML page (fallback method)
|
|
51
21
|
*/
|
|
@@ -83,7 +53,7 @@ async function extractChatParams() {
|
|
|
83
53
|
async function getTokenData() {
|
|
84
54
|
// Try database first (preferred - always has fresh token)
|
|
85
55
|
try {
|
|
86
|
-
const dbData =
|
|
56
|
+
const dbData = getAuthStatus();
|
|
87
57
|
if (dbData?.apiKey) {
|
|
88
58
|
console.log('[Token] Got fresh token from SQLite database');
|
|
89
59
|
return dbData;
|