From a6c764d772407ae4c62a45e11976fb910961c4ad Mon Sep 17 00:00:00 2001
From: Anurag Saxena
Date: Thu, 26 Mar 2026 23:45:11 -0400
Subject: [PATCH] fix: use better-sqlite3 for Cursor auto-import, drop sqlite3
CLI requirement (closes #395) (#411)
---
src/app/api/oauth/cursor/auto-import/route.js | 65 +++++++++++++++++--
src/shared/components/CursorAuthModal.js | 5 +-
2 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/src/app/api/oauth/cursor/auto-import/route.js b/src/app/api/oauth/cursor/auto-import/route.js
index f866e218..5f84fa92 100644
--- a/src/app/api/oauth/cursor/auto-import/route.js
+++ b/src/app/api/oauth/cursor/auto-import/route.js
@@ -72,9 +72,50 @@ const normalize = (value) => {
}
};
+/**
+ * Extract tokens via better-sqlite3 (bundled dependency).
+ * This is the preferred strategy — no external CLI required.
+ */
+function extractTokensViaBetterSqlite(dbPath) {
+ // Dynamic require so the route stays importable even if native bindings fail
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const Database = require("better-sqlite3");
+ const db = new Database(dbPath, { readonly: true, fileMustExist: true });
+
+ const query = (key) => {
+ const row = db.prepare("SELECT value FROM itemTable WHERE key=? LIMIT 1").get(key);
+ return row?.value || null;
+ };
+
+ const normalize = (value) => {
+ if (typeof value !== "string") return value;
+ try {
+ const parsed = JSON.parse(value);
+ return typeof parsed === "string" ? parsed : value;
+ } catch {
+ return value;
+ }
+ };
+
+ let accessToken = null;
+ for (const key of ACCESS_TOKEN_KEYS) {
+ const raw = query(key);
+ if (raw) { accessToken = normalize(raw); break; }
+ }
+
+ let machineId = null;
+ for (const key of MACHINE_ID_KEYS) {
+ const raw = query(key);
+ if (raw) { machineId = normalize(raw); break; }
+ }
+
+ db.close();
+ return { accessToken, machineId };
+}
+
/**
* Extract tokens via sqlite3 CLI.
- * Keeps the route build-safe by avoiding optional sql.js bundling.
+ * Fallback when better-sqlite3 native bindings are unavailable.
*/
async function extractTokensViaCLI(dbPath) {
const normalize = (raw) => {
@@ -131,7 +172,7 @@ async function extractTokensViaCLI(dbPath) {
/**
* GET /api/oauth/cursor/auto-import
* Auto-detect and extract Cursor tokens from local SQLite database.
- * Strategy: sqlite3 CLI → manual fallback
+ * Strategy: better-sqlite3 → sqlite3 CLI → manual fallback
*/
export async function GET() {
try {
@@ -177,7 +218,21 @@ export async function GET() {
}
}
- // Strategy 1: sqlite3 CLI
+ // Strategy 1: better-sqlite3 (bundled — no external tools required)
+ try {
+ const tokens = extractTokensViaBetterSqlite(dbPath);
+ if (tokens.accessToken && tokens.machineId) {
+ return NextResponse.json({
+ found: true,
+ accessToken: tokens.accessToken,
+ machineId: tokens.machineId,
+ });
+ }
+ } catch {
+ // Native bindings unavailable — try CLI fallback
+ }
+
+ // Strategy 2: sqlite3 CLI
try {
const tokens = await extractTokensViaCLI(dbPath);
if (tokens.accessToken && tokens.machineId) {
@@ -188,10 +243,10 @@ export async function GET() {
});
}
} catch {
- // sqlite3 CLI not available
+ // sqlite3 CLI not available either
}
- // Strategy 2: ask user to paste manually
+ // Strategy 3: ask user to paste manually
return NextResponse.json({ found: false, windowsManual: true, dbPath });
} catch (error) {
console.log("Cursor auto-import error:", error);
diff --git a/src/shared/components/CursorAuthModal.js b/src/shared/components/CursorAuthModal.js
index c8966784..704db9bf 100644
--- a/src/shared/components/CursorAuthModal.js
+++ b/src/shared/components/CursorAuthModal.js
@@ -131,11 +131,8 @@ export default function CursorAuthModal({ isOpen, onSuccess, onClose }) {
- Run this command in your terminal, then click Retry:
+ Make sure Cursor IDE has been opened at least once, then click Retry. If the problem persists, paste your tokens manually below.