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 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",
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
  }
@@ -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 = this.#extractTokenFromDB();
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 = this.#extractTokenFromDB(dbPath);
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
  */
@@ -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 = extractTokenFromDB();
56
+ const dbData = getAuthStatus();
87
57
  if (dbData?.apiKey) {
88
58
  console.log('[Token] Got fresh token from SQLite database');
89
59
  return dbData;