feat(cursor): Add cursor Provider

This commit is contained in:
decolua
2026-02-05 11:06:20 +07:00
parent abaeb22863
commit 0a026c7af6
16 changed files with 1113 additions and 937 deletions

View File

@@ -0,0 +1,84 @@
import { NextResponse } from "next/server";
import { homedir } from "os";
import { join } from "path";
import Database from "better-sqlite3";
/**
* GET /api/oauth/cursor/auto-import
* Auto-detect and extract Cursor tokens from local SQLite database
*/
export async function GET() {
try {
const platform = process.platform;
let dbPath;
// Determine database path based on platform
if (platform === "darwin") {
dbPath = join(homedir(), "Library/Application Support/Cursor/User/globalStorage/state.vscdb");
} else if (platform === "linux") {
dbPath = join(homedir(), ".config/Cursor/User/globalStorage/state.vscdb");
} else if (platform === "win32") {
dbPath = join(process.env.APPDATA || "", "Cursor/User/globalStorage/state.vscdb");
} else {
return NextResponse.json(
{ error: "Unsupported platform", found: false },
{ status: 400 }
);
}
// Try to open database
let db;
try {
db = new Database(dbPath, { readonly: true, fileMustExist: true });
} catch (error) {
return NextResponse.json({
found: false,
error: "Cursor database not found. Make sure Cursor IDE is installed and you are logged in.",
});
}
try {
// Extract tokens from database
const rows = db.prepare(
"SELECT key, value FROM itemTable WHERE key IN (?, ?)"
).all("cursorAuth/accessToken", "storage.serviceMachineId");
const tokens = {};
for (const row of rows) {
if (row.key === "cursorAuth/accessToken") {
tokens.accessToken = row.value;
} else if (row.key === "storage.serviceMachineId") {
tokens.machineId = row.value;
}
}
db.close();
// Validate tokens exist
if (!tokens.accessToken || !tokens.machineId) {
return NextResponse.json({
found: false,
error: "Tokens not found in database. Please login to Cursor IDE first.",
});
}
return NextResponse.json({
found: true,
accessToken: tokens.accessToken,
machineId: tokens.machineId,
});
} catch (error) {
db?.close();
return NextResponse.json({
found: false,
error: `Failed to read database: ${error.message}`,
});
}
} catch (error) {
console.log("Cursor auto-import error:", error);
return NextResponse.json(
{ found: false, error: error.message },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,85 @@
import { NextResponse } from "next/server";
import { readFile, readdir } from "fs/promises";
import { homedir } from "os";
import { join } from "path";
/**
* GET /api/oauth/kiro/auto-import
* Auto-detect and extract Kiro refresh token from AWS SSO cache
*/
export async function GET() {
try {
const cachePath = join(homedir(), ".aws/sso/cache");
// Try to read cache directory
let files;
try {
files = await readdir(cachePath);
} catch (error) {
return NextResponse.json({
found: false,
error: "AWS SSO cache not found. Please login to Kiro IDE first.",
});
}
// Look for kiro-auth-token.json or any .json file with refreshToken
let refreshToken = null;
let foundFile = null;
// First try kiro-auth-token.json
const kiroTokenFile = "kiro-auth-token.json";
if (files.includes(kiroTokenFile)) {
try {
const content = await readFile(join(cachePath, kiroTokenFile), "utf-8");
const data = JSON.parse(content);
if (data.refreshToken && data.refreshToken.startsWith("aorAAAAAG")) {
refreshToken = data.refreshToken;
foundFile = kiroTokenFile;
}
} catch (error) {
// Continue to search other files
}
}
// If not found, search all .json files
if (!refreshToken) {
for (const file of files) {
if (!file.endsWith(".json")) continue;
try {
const content = await readFile(join(cachePath, file), "utf-8");
const data = JSON.parse(content);
// Look for Kiro refresh token (starts with aorAAAAAG)
if (data.refreshToken && data.refreshToken.startsWith("aorAAAAAG")) {
refreshToken = data.refreshToken;
foundFile = file;
break;
}
} catch (error) {
// Skip invalid JSON files
continue;
}
}
}
if (!refreshToken) {
return NextResponse.json({
found: false,
error: "Kiro token not found in AWS SSO cache. Please login to Kiro IDE first.",
});
}
return NextResponse.json({
found: true,
refreshToken,
source: foundFile,
});
} catch (error) {
console.log("Kiro auto-import error:", error);
return NextResponse.json(
{ found: false, error: error.message },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,13 @@
import { NextResponse } from "next/server";
import { getRecentLogs } from "@/lib/usageDb";
export async function GET() {
try {
const logs = await getRecentLogs(200);
return NextResponse.json(logs);
} catch (error) {
console.error("[API ERROR] /api/usage/logs failed:", error);
console.error("[API ERROR] Stack:", error?.stack);
return NextResponse.json({ error: "Failed to fetch logs" }, { status: 500 });
}
}