Fix: Add Windows support for SQLite database access (Issue #23)
Replace CLI-based sqlite3 extraction with better-sqlite3 library: - Create shared src/db/database.js module for cross-platform SQLite access - Remove duplicate extractTokenFromDB functions from token-extractor.js and account-manager.js - Add better-sqlite3 dependency with prebuilt Windows/Mac/Linux binaries - Improve error handling with specific messages for common issues Fixes #23 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
import { readFile, writeFile, mkdir, access } from 'fs/promises';
|
||||
import { constants as fsConstants } from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import {
|
||||
ACCOUNT_CONFIG_PATH,
|
||||
ANTIGRAVITY_DB_PATH,
|
||||
@@ -20,6 +19,7 @@ import {
|
||||
} from './constants.js';
|
||||
import { refreshAccessToken } from './oauth.js';
|
||||
import { formatDuration } from './utils/helpers.js';
|
||||
import { getAuthStatus } from './db/database.js';
|
||||
|
||||
export class AccountManager {
|
||||
#accounts = [];
|
||||
@@ -92,7 +92,7 @@ export class AccountManager {
|
||||
*/
|
||||
async #loadDefaultAccount() {
|
||||
try {
|
||||
const authData = this.#extractTokenFromDB();
|
||||
const authData = getAuthStatus();
|
||||
if (authData?.apiKey) {
|
||||
this.#accounts = [{
|
||||
email: authData.email || 'default@antigravity',
|
||||
@@ -115,22 +115,6 @@ export class AccountManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract token from Antigravity's SQLite database
|
||||
*/
|
||||
#extractTokenFromDB(dbPath = ANTIGRAVITY_DB_PATH) {
|
||||
const result = execSync(
|
||||
`sqlite3 "${dbPath}" "SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus';"`,
|
||||
{ encoding: 'utf-8', timeout: 5000 }
|
||||
);
|
||||
|
||||
if (!result || !result.trim()) {
|
||||
throw new Error('No auth status found in database');
|
||||
}
|
||||
|
||||
return JSON.parse(result.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of accounts
|
||||
* @returns {number} Number of configured accounts
|
||||
@@ -453,7 +437,7 @@ export class AccountManager {
|
||||
} else {
|
||||
// Extract from database
|
||||
const dbPath = account.dbPath || ANTIGRAVITY_DB_PATH;
|
||||
const authData = this.#extractTokenFromDB(dbPath);
|
||||
const authData = getAuthStatus(dbPath);
|
||||
token = authData.apiKey;
|
||||
}
|
||||
|
||||
|
||||
93
src/db/database.js
Normal file
93
src/db/database.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* SQLite Database Access Module
|
||||
* Provides cross-platform database operations for Antigravity state.
|
||||
*
|
||||
* Uses better-sqlite3 for:
|
||||
* - Windows compatibility (no CLI dependency)
|
||||
* - Native performance
|
||||
* - Synchronous API (simple error handling)
|
||||
*/
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
import { ANTIGRAVITY_DB_PATH } from '../constants.js';
|
||||
|
||||
/**
|
||||
* Query Antigravity database for authentication status
|
||||
* @param {string} [dbPath] - Optional custom database path
|
||||
* @returns {Object} Parsed auth data with apiKey, email, name, etc.
|
||||
* @throws {Error} If database doesn't exist, query fails, or no auth status found
|
||||
*/
|
||||
export function getAuthStatus(dbPath = ANTIGRAVITY_DB_PATH) {
|
||||
let db;
|
||||
try {
|
||||
// Open database in read-only mode
|
||||
db = new Database(dbPath, {
|
||||
readonly: true,
|
||||
fileMustExist: true
|
||||
});
|
||||
|
||||
// Prepare and execute query
|
||||
const stmt = db.prepare(
|
||||
"SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus'"
|
||||
);
|
||||
const row = stmt.get();
|
||||
|
||||
if (!row || !row.value) {
|
||||
throw new Error('No auth status found in database');
|
||||
}
|
||||
|
||||
// Parse JSON value
|
||||
const authData = JSON.parse(row.value);
|
||||
|
||||
if (!authData.apiKey) {
|
||||
throw new Error('Auth data missing apiKey field');
|
||||
}
|
||||
|
||||
return authData;
|
||||
} catch (error) {
|
||||
// Enhance error messages for common issues
|
||||
if (error.code === 'SQLITE_CANTOPEN') {
|
||||
throw new Error(
|
||||
`Database not found at ${dbPath}. ` +
|
||||
'Make sure Antigravity is installed and you are logged in.'
|
||||
);
|
||||
}
|
||||
// Re-throw with context if not already our error
|
||||
if (error.message.includes('No auth status') || error.message.includes('missing apiKey')) {
|
||||
throw error;
|
||||
}
|
||||
throw new Error(`Failed to read Antigravity database: ${error.message}`);
|
||||
} finally {
|
||||
// Always close database connection
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if database exists and is accessible
|
||||
* @param {string} [dbPath] - Optional custom database path
|
||||
* @returns {boolean} True if database exists and can be opened
|
||||
*/
|
||||
export function isDatabaseAccessible(dbPath = ANTIGRAVITY_DB_PATH) {
|
||||
let db;
|
||||
try {
|
||||
db = new Database(dbPath, {
|
||||
readonly: true,
|
||||
fileMustExist: true
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getAuthStatus,
|
||||
isDatabaseAccessible
|
||||
};
|
||||
@@ -6,46 +6,16 @@
|
||||
* so this approach doesn't require any manual intervention.
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import {
|
||||
TOKEN_REFRESH_INTERVAL_MS,
|
||||
ANTIGRAVITY_AUTH_PORT,
|
||||
ANTIGRAVITY_DB_PATH
|
||||
ANTIGRAVITY_AUTH_PORT
|
||||
} from './constants.js';
|
||||
import { getAuthStatus } from './db/database.js';
|
||||
|
||||
// Cache for the extracted token
|
||||
let cachedToken = null;
|
||||
let tokenExtractedAt = null;
|
||||
|
||||
/**
|
||||
* Extract token from Antigravity's SQLite database
|
||||
* This is the preferred method as the DB is auto-updated
|
||||
*/
|
||||
function extractTokenFromDB() {
|
||||
try {
|
||||
const result = execSync(
|
||||
`sqlite3 "${ANTIGRAVITY_DB_PATH}" "SELECT value FROM ItemTable WHERE key = 'antigravityAuthStatus';"`,
|
||||
{ encoding: 'utf-8', timeout: 5000 }
|
||||
);
|
||||
|
||||
if (!result || !result.trim()) {
|
||||
throw new Error('No auth status found in database');
|
||||
}
|
||||
|
||||
const authData = JSON.parse(result.trim());
|
||||
return {
|
||||
apiKey: authData.apiKey,
|
||||
name: authData.name,
|
||||
email: authData.email,
|
||||
// Include other fields we might need
|
||||
...authData
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[Token] Database extraction failed:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the chat params from Antigravity's HTML page (fallback method)
|
||||
*/
|
||||
@@ -83,7 +53,7 @@ async function extractChatParams() {
|
||||
async function getTokenData() {
|
||||
// Try database first (preferred - always has fresh token)
|
||||
try {
|
||||
const dbData = extractTokenFromDB();
|
||||
const dbData = getAuthStatus();
|
||||
if (dbData?.apiKey) {
|
||||
console.log('[Token] Got fresh token from SQLite database');
|
||||
return dbData;
|
||||
|
||||
Reference in New Issue
Block a user