antigravity-claude-proxy 1.2.7 → 1.2.8

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
@@ -400,4 +400,4 @@ MIT
400
400
 
401
401
  ## Star History
402
402
 
403
- [![Star History Chart](https://api.star-history.com/svg?repos=badri-s2001/antigravity-claude-proxy&type=date&legend=top-left&cache-control=no-cache)](https://www.star-history.com/#badri-s2001/antigravity-claude-proxy&type=date&legend=top-left)
403
+ [![Star History Chart](https://api.star-history.com/svg?repos=badrisnarayanan/antigravity-claude-proxy&type=date&legend=top-left&cache-control=no-cache)](https://www.star-history.com/#badrisnarayanan/antigravity-claude-proxy&type=date&legend=top-left)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antigravity-claude-proxy",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
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",
@@ -6,10 +6,80 @@
6
6
  * - Windows compatibility (no CLI dependency)
7
7
  * - Native performance
8
8
  * - Synchronous API (simple error handling)
9
+ *
10
+ * Includes auto-rebuild capability for handling Node.js version updates
11
+ * that cause native module incompatibility.
9
12
  */
10
13
 
11
- import Database from 'better-sqlite3';
14
+ import { createRequire } from 'module';
12
15
  import { ANTIGRAVITY_DB_PATH } from '../constants.js';
16
+ import { isModuleVersionError, attemptAutoRebuild, clearRequireCache } from '../utils/native-module-helper.js';
17
+ import { logger } from '../utils/logger.js';
18
+ import { NativeModuleError } from '../errors.js';
19
+
20
+ const require = createRequire(import.meta.url);
21
+
22
+ // Lazy-loaded Database constructor
23
+ let Database = null;
24
+ let moduleLoadError = null;
25
+
26
+ /**
27
+ * Load the better-sqlite3 module with auto-rebuild on version mismatch
28
+ * Uses synchronous require to maintain API compatibility
29
+ * @returns {Function} The Database constructor
30
+ * @throws {Error} If module cannot be loaded even after rebuild
31
+ */
32
+ function loadDatabaseModule() {
33
+ // Return cached module if already loaded
34
+ if (Database) return Database;
35
+
36
+ // Re-throw cached error if previous load failed permanently
37
+ if (moduleLoadError) throw moduleLoadError;
38
+
39
+ try {
40
+ Database = require('better-sqlite3');
41
+ return Database;
42
+ } catch (error) {
43
+ if (isModuleVersionError(error)) {
44
+ logger.warn('[Database] Native module version mismatch detected');
45
+
46
+ if (attemptAutoRebuild(error)) {
47
+ // Clear require cache and retry
48
+ try {
49
+ const resolvedPath = require.resolve('better-sqlite3');
50
+ // Clear the module and all its dependencies from cache
51
+ clearRequireCache(resolvedPath, require.cache);
52
+
53
+ Database = require('better-sqlite3');
54
+ logger.success('[Database] Module reloaded successfully after rebuild');
55
+ return Database;
56
+ } catch (retryError) {
57
+ // Rebuild succeeded but reload failed - user needs to restart
58
+ moduleLoadError = new NativeModuleError(
59
+ 'Native module rebuild completed. Please restart the server to apply the fix.',
60
+ true, // rebuildSucceeded
61
+ true // restartRequired
62
+ );
63
+ logger.info('[Database] Rebuild succeeded - server restart required');
64
+ throw moduleLoadError;
65
+ }
66
+ } else {
67
+ moduleLoadError = new NativeModuleError(
68
+ 'Failed to auto-rebuild native module. Please run manually:\n' +
69
+ ' npm rebuild better-sqlite3\n' +
70
+ 'Or if using npx, find the package location in the error and run:\n' +
71
+ ' cd /path/to/better-sqlite3 && npm rebuild',
72
+ false, // rebuildSucceeded
73
+ false // restartRequired
74
+ );
75
+ throw moduleLoadError;
76
+ }
77
+ }
78
+
79
+ // Non-version-mismatch error, just throw it
80
+ throw error;
81
+ }
82
+ }
13
83
 
14
84
  /**
15
85
  * Query Antigravity database for authentication status
@@ -18,10 +88,11 @@ import { ANTIGRAVITY_DB_PATH } from '../constants.js';
18
88
  * @throws {Error} If database doesn't exist, query fails, or no auth status found
19
89
  */
20
90
  export function getAuthStatus(dbPath = ANTIGRAVITY_DB_PATH) {
91
+ const Db = loadDatabaseModule();
21
92
  let db;
22
93
  try {
23
94
  // Open database in read-only mode
24
- db = new Database(dbPath, {
95
+ db = new Db(dbPath, {
25
96
  readonly: true,
26
97
  fileMustExist: true
27
98
  });
@@ -56,6 +127,10 @@ export function getAuthStatus(dbPath = ANTIGRAVITY_DB_PATH) {
56
127
  if (error.message.includes('No auth status') || error.message.includes('missing apiKey')) {
57
128
  throw error;
58
129
  }
130
+ // Re-throw native module errors from loadDatabaseModule without wrapping
131
+ if (error instanceof NativeModuleError) {
132
+ throw error;
133
+ }
59
134
  throw new Error(`Failed to read Antigravity database: ${error.message}`);
60
135
  } finally {
61
136
  // Always close database connection
@@ -73,7 +148,8 @@ export function getAuthStatus(dbPath = ANTIGRAVITY_DB_PATH) {
73
148
  export function isDatabaseAccessible(dbPath = ANTIGRAVITY_DB_PATH) {
74
149
  let db;
75
150
  try {
76
- db = new Database(dbPath, {
151
+ const Db = loadDatabaseModule();
152
+ db = new Db(dbPath, {
77
153
  readonly: true,
78
154
  fileMustExist: true
79
155
  });
package/src/auth/oauth.js CHANGED
@@ -131,10 +131,10 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
131
131
  const error = url.searchParams.get('error');
132
132
 
133
133
  if (error) {
134
- res.writeHead(400, { 'Content-Type': 'text/html' });
134
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
135
135
  res.end(`
136
136
  <html>
137
- <head><title>Authentication Failed</title></head>
137
+ <head><meta charset="UTF-8"><title>Authentication Failed</title></head>
138
138
  <body style="font-family: system-ui; padding: 40px; text-align: center;">
139
139
  <h1 style="color: #dc3545;">❌ Authentication Failed</h1>
140
140
  <p>Error: ${error}</p>
@@ -148,10 +148,10 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
148
148
  }
149
149
 
150
150
  if (state !== expectedState) {
151
- res.writeHead(400, { 'Content-Type': 'text/html' });
151
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
152
152
  res.end(`
153
153
  <html>
154
- <head><title>Authentication Failed</title></head>
154
+ <head><meta charset="UTF-8"><title>Authentication Failed</title></head>
155
155
  <body style="font-family: system-ui; padding: 40px; text-align: center;">
156
156
  <h1 style="color: #dc3545;">❌ Authentication Failed</h1>
157
157
  <p>State mismatch - possible CSRF attack.</p>
@@ -165,10 +165,10 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
165
165
  }
166
166
 
167
167
  if (!code) {
168
- res.writeHead(400, { 'Content-Type': 'text/html' });
168
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
169
169
  res.end(`
170
170
  <html>
171
- <head><title>Authentication Failed</title></head>
171
+ <head><meta charset="UTF-8"><title>Authentication Failed</title></head>
172
172
  <body style="font-family: system-ui; padding: 40px; text-align: center;">
173
173
  <h1 style="color: #dc3545;">❌ Authentication Failed</h1>
174
174
  <p>No authorization code received.</p>
@@ -182,10 +182,10 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
182
182
  }
183
183
 
184
184
  // Success!
185
- res.writeHead(200, { 'Content-Type': 'text/html' });
185
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
186
186
  res.end(`
187
187
  <html>
188
- <head><title>Authentication Successful</title></head>
188
+ <head><meta charset="UTF-8"><title>Authentication Successful</title></head>
189
189
  <body style="font-family: system-ui; padding: 40px; text-align: center;">
190
190
  <h1 style="color: #28a745;">✅ Authentication Successful!</h1>
191
191
  <p>You can close this window and return to the terminal.</p>
package/src/constants.js CHANGED
@@ -74,7 +74,7 @@ export const ACCOUNT_CONFIG_PATH = join(
74
74
  // Uses platform-specific path detection
75
75
  export const ANTIGRAVITY_DB_PATH = getAntigravityDbPath();
76
76
 
77
- export const DEFAULT_COOLDOWN_MS = 60 * 1000; // 1 minute default cooldown
77
+ export const DEFAULT_COOLDOWN_MS = 10 * 1000; // 10 second default cooldown
78
78
  export const MAX_RETRIES = 5; // Max retry attempts across accounts
79
79
  export const MAX_ACCOUNTS = 10; // Maximum number of accounts allowed
80
80
 
package/src/errors.js CHANGED
@@ -118,6 +118,23 @@ export class ApiError extends AntigravityError {
118
118
  }
119
119
  }
120
120
 
121
+ /**
122
+ * Native module error (version mismatch, rebuild required)
123
+ */
124
+ export class NativeModuleError extends AntigravityError {
125
+ /**
126
+ * @param {string} message - Error message
127
+ * @param {boolean} rebuildSucceeded - Whether auto-rebuild succeeded
128
+ * @param {boolean} restartRequired - Whether server restart is needed
129
+ */
130
+ constructor(message, rebuildSucceeded = false, restartRequired = false) {
131
+ super(message, 'NATIVE_MODULE_ERROR', false, { rebuildSucceeded, restartRequired });
132
+ this.name = 'NativeModuleError';
133
+ this.rebuildSucceeded = rebuildSucceeded;
134
+ this.restartRequired = restartRequired;
135
+ }
136
+ }
137
+
121
138
  /**
122
139
  * Check if an error is a rate limit error
123
140
  * Works with both custom error classes and legacy string-based errors
@@ -154,6 +171,7 @@ export default {
154
171
  NoAccountsError,
155
172
  MaxRetriesError,
156
173
  ApiError,
174
+ NativeModuleError,
157
175
  isRateLimitError,
158
176
  isAuthError
159
177
  };
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Native Module Helper
3
+ * Detects and auto-rebuilds native Node.js modules when they become
4
+ * incompatible after a Node.js version update.
5
+ */
6
+
7
+ import { execSync } from 'child_process';
8
+ import { dirname, join } from 'path';
9
+ import { existsSync } from 'fs';
10
+ import { logger } from './logger.js';
11
+
12
+ /**
13
+ * Check if an error is a NODE_MODULE_VERSION mismatch error
14
+ * @param {Error} error - The error to check
15
+ * @returns {boolean} True if it's a version mismatch error
16
+ */
17
+ export function isModuleVersionError(error) {
18
+ const message = error?.message || '';
19
+ return message.includes('NODE_MODULE_VERSION') &&
20
+ message.includes('was compiled against a different Node.js version');
21
+ }
22
+
23
+ /**
24
+ * Extract the module path from a NODE_MODULE_VERSION error message
25
+ * @param {Error} error - The error containing the module path
26
+ * @returns {string|null} The path to the .node file, or null if not found
27
+ */
28
+ export function extractModulePath(error) {
29
+ const message = error?.message || '';
30
+ // Match pattern like: "The module '/path/to/module.node'"
31
+ const match = message.match(/The module '([^']+\.node)'/);
32
+ return match ? match[1] : null;
33
+ }
34
+
35
+ /**
36
+ * Find the package root directory from a .node file path
37
+ * @param {string} nodeFilePath - Path to the .node file
38
+ * @returns {string|null} Path to the package root, or null if not found
39
+ */
40
+ export function findPackageRoot(nodeFilePath) {
41
+ // Walk up from the .node file to find package.json
42
+ let dir = dirname(nodeFilePath);
43
+ while (dir) {
44
+ const packageJsonPath = join(dir, 'package.json');
45
+ if (existsSync(packageJsonPath)) {
46
+ return dir;
47
+ }
48
+ const parentDir = dirname(dir);
49
+ // Stop when we've reached the filesystem root (dirname returns same path)
50
+ if (parentDir === dir) {
51
+ break;
52
+ }
53
+ dir = parentDir;
54
+ }
55
+ return null;
56
+ }
57
+
58
+ /**
59
+ * Attempt to rebuild a native module
60
+ * @param {string} packagePath - Path to the package root directory
61
+ * @returns {boolean} True if rebuild succeeded, false otherwise
62
+ */
63
+ export function rebuildModule(packagePath) {
64
+ try {
65
+ logger.info(`[NativeModule] Rebuilding native module at: ${packagePath}`);
66
+
67
+ // Run npm rebuild in the package directory
68
+ const output = execSync('npm rebuild', {
69
+ cwd: packagePath,
70
+ stdio: 'pipe', // Capture output instead of printing
71
+ timeout: 120000 // 2 minute timeout
72
+ });
73
+
74
+ // Log rebuild output for debugging
75
+ const outputStr = output?.toString().trim();
76
+ if (outputStr) {
77
+ logger.debug(`[NativeModule] Rebuild output:\n${outputStr}`);
78
+ }
79
+
80
+ logger.success('[NativeModule] Rebuild completed successfully');
81
+ return true;
82
+ } catch (error) {
83
+ // Include stdout/stderr from the failed command for troubleshooting
84
+ const stdout = error.stdout?.toString().trim();
85
+ const stderr = error.stderr?.toString().trim();
86
+ let errorDetails = `[NativeModule] Rebuild failed: ${error.message}`;
87
+ if (stdout) {
88
+ errorDetails += `\n[NativeModule] stdout: ${stdout}`;
89
+ }
90
+ if (stderr) {
91
+ errorDetails += `\n[NativeModule] stderr: ${stderr}`;
92
+ }
93
+ logger.error(errorDetails);
94
+ return false;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Attempt to auto-rebuild a native module from an error
100
+ * @param {Error} error - The NODE_MODULE_VERSION error
101
+ * @returns {boolean} True if rebuild succeeded, false otherwise
102
+ */
103
+ export function attemptAutoRebuild(error) {
104
+ const nodePath = extractModulePath(error);
105
+ if (!nodePath) {
106
+ logger.error('[NativeModule] Could not extract module path from error');
107
+ return false;
108
+ }
109
+
110
+ const packagePath = findPackageRoot(nodePath);
111
+ if (!packagePath) {
112
+ logger.error('[NativeModule] Could not find package root');
113
+ return false;
114
+ }
115
+
116
+ logger.warn('[NativeModule] Native module version mismatch detected');
117
+ logger.info('[NativeModule] Attempting automatic rebuild...');
118
+
119
+ return rebuildModule(packagePath);
120
+ }
121
+
122
+ /**
123
+ * Recursively clear a module and its dependencies from the require cache
124
+ * This is needed after rebuilding a native module to force re-import
125
+ * @param {string} modulePath - Resolved path to the module
126
+ * @param {object} cache - The require.cache object
127
+ * @param {Set} [visited] - Set of already-visited paths to prevent cycles
128
+ */
129
+ export function clearRequireCache(modulePath, cache, visited = new Set()) {
130
+ if (visited.has(modulePath)) return;
131
+ visited.add(modulePath);
132
+
133
+ const mod = cache[modulePath];
134
+ if (!mod) return;
135
+
136
+ // Recursively clear children first
137
+ if (mod.children) {
138
+ for (const child of mod.children) {
139
+ clearRequireCache(child.id, cache, visited);
140
+ }
141
+ }
142
+
143
+ // Remove from parent's children array
144
+ if (mod.parent && mod.parent.children) {
145
+ const idx = mod.parent.children.indexOf(mod);
146
+ if (idx !== -1) {
147
+ mod.parent.children.splice(idx, 1);
148
+ }
149
+ }
150
+
151
+ // Delete from cache
152
+ delete cache[modulePath];
153
+ }
154
+
155
+ export default {
156
+ isModuleVersionError,
157
+ extractModulePath,
158
+ findPackageRoot,
159
+ rebuildModule,
160
+ attemptAutoRebuild,
161
+ clearRequireCache
162
+ };